2014-11-29 15:52:08 +00:00
|
|
|
**Mu: making programs easier to understand in the large**
|
2014-11-25 07:59:55 +00:00
|
|
|
|
|
|
|
Imagine a world where you can:
|
|
|
|
|
2014-11-26 15:04:04 +00:00
|
|
|
1. think of a tiny improvement to a program you use, clone its sources,
|
|
|
|
orient yourself on its organization and make your tiny improvement, all in a
|
|
|
|
single afternoon.
|
2014-11-25 07:59:55 +00:00
|
|
|
|
2014-11-26 15:04:04 +00:00
|
|
|
2. Record your program as it runs, and easily convert arbitrary logs of runs
|
|
|
|
into reproducible automatic tests.
|
2014-11-25 07:59:55 +00:00
|
|
|
|
2014-11-26 15:04:04 +00:00
|
|
|
3. Answer arbitrary what-if questions about a codebase by trying out changes
|
|
|
|
and seeing what tests fail, confident that *every* scenario previous authors
|
|
|
|
have considered has been encoded as a test.
|
2014-11-25 07:59:55 +00:00
|
|
|
|
2014-11-30 00:22:11 +00:00
|
|
|
4. Run first simple and successively more complex versions to stage your
|
|
|
|
learning.
|
2014-11-25 07:59:55 +00:00
|
|
|
|
|
|
|
I think all these abilities might be strongly correlated; not only are they
|
|
|
|
achievable with a few common concepts, but you can't easily attack one of them
|
2014-11-25 09:28:17 +00:00
|
|
|
without also chasing after the others. The core mechanism enabling them all is
|
|
|
|
recording manual tests right after the first time you perform them:
|
2014-11-23 18:03:45 +00:00
|
|
|
|
2014-11-26 15:04:04 +00:00
|
|
|
* keyboard input
|
|
|
|
* printing to screen
|
2014-12-13 02:02:30 +00:00
|
|
|
* website layout
|
2014-11-26 15:04:04 +00:00
|
|
|
* disk filling up
|
|
|
|
* performance metrics
|
|
|
|
* race conditions
|
|
|
|
* fault tolerance
|
|
|
|
* ...
|
2014-11-23 18:03:45 +00:00
|
|
|
|
2014-11-25 07:59:55 +00:00
|
|
|
I hope to attain this world by creating a comprehensive library of fakes and
|
|
|
|
hooks for the entire software stack, at all layers of abstraction (programming
|
|
|
|
language, OS, standard libraries, application libraries).
|
|
|
|
|
|
|
|
To reduce my workload and get to a proof-of-concept quickly, this is a very
|
|
|
|
*alien* software stack. I've stolen ideas from lots of previous systems, but
|
|
|
|
it's not like anything you're used to. The 'OS' will lack virtual memory, user
|
|
|
|
accounts, any unprivileged mode, address space isolation, and many other
|
|
|
|
features.
|
|
|
|
|
|
|
|
To avoid building a compiler I'm going to do all my programming in (virtual
|
|
|
|
machine) assembly. To keep assembly from getting too painful I'm going to
|
|
|
|
pervasively use one trick: load-time directives to let me order code however I
|
|
|
|
want, and to write boilerplate once and insert it in multiple places. If
|
|
|
|
you're familiar with literate programming or aspect-oriented programming,
|
|
|
|
these directives may seem vaguely familiar. If you're not, think of them as a
|
|
|
|
richer interface for function inlining.
|
|
|
|
|
|
|
|
Trading off notational convenience for tests may seem regressive, but I
|
|
|
|
suspect high-level languages aren't particularly helpful in understanding
|
|
|
|
large codebases. No matter how good a notation is, it can only let you see a
|
|
|
|
tiny fraction of a large program at a time. Logs, on the other hand, can let
|
|
|
|
you zoom out and take in an entire *run* at a glance, making them a superior
|
|
|
|
unit of comprehension. If I'm right, it makes sense to prioritize the right
|
|
|
|
*tactile* interface for working with and getting feedback on large programs
|
|
|
|
before we invest in the *visual* tools for making them concise.
|
2014-11-23 18:03:45 +00:00
|
|
|
|
2015-03-09 23:00:05 +00:00
|
|
|
([More details.](http://akkartik.name/about))
|
|
|
|
|
2015-03-10 19:16:28 +00:00
|
|
|
**Taking Mu for a spin**
|
2014-11-01 23:15:15 +00:00
|
|
|
|
2014-11-26 15:04:04 +00:00
|
|
|
```shell
|
2014-11-01 23:15:15 +00:00
|
|
|
$ cd mu
|
2015-05-06 04:17:24 +00:00
|
|
|
$ make test
|
2014-11-25 17:17:18 +00:00
|
|
|
```
|
2014-11-01 23:15:15 +00:00
|
|
|
|
2015-03-10 19:16:28 +00:00
|
|
|
As a sneak peek, here's how you compute factorial in Mu:
|
2014-11-25 07:59:55 +00:00
|
|
|
|
2015-06-08 21:24:05 +00:00
|
|
|
![code example](http://i.imgur.com/GYtLzbM.png)
|
2014-11-25 07:59:55 +00:00
|
|
|
|
2015-06-08 21:24:05 +00:00
|
|
|
Mu functions or 'recipes' are lists of instructions, one to a line. Each
|
|
|
|
instruction operates on some *ingredients* and returns some *results*.
|
2014-11-25 07:59:55 +00:00
|
|
|
|
2014-11-25 17:17:18 +00:00
|
|
|
```
|
2015-05-06 04:17:24 +00:00
|
|
|
[results] <- instruction [ingredients]
|
2014-11-25 17:17:18 +00:00
|
|
|
```
|
2014-11-25 07:59:55 +00:00
|
|
|
|
2015-05-11 19:00:50 +00:00
|
|
|
Result and ingredient *reagents* have to be variables. But you can have any
|
|
|
|
number of them. In particular you can have any number of results. For example,
|
|
|
|
you can perform integer division as follows:
|
2014-11-26 16:30:26 +00:00
|
|
|
|
2015-06-08 21:24:05 +00:00
|
|
|
```
|
2015-05-13 17:03:26 +00:00
|
|
|
quotient:number, remainder:number <- divide-with-remainder 11:literal, 3:literal
|
2014-11-26 16:30:26 +00:00
|
|
|
```
|
|
|
|
|
2015-05-06 04:17:24 +00:00
|
|
|
Each reagent provides its name as well as its type separated by a colon. Types
|
2014-12-14 21:21:32 +00:00
|
|
|
can be multiple words, like:
|
2014-11-25 07:59:55 +00:00
|
|
|
|
2015-05-06 04:17:24 +00:00
|
|
|
```python
|
2015-06-08 21:24:05 +00:00
|
|
|
x:array:number:3 # x is an array of 3 numbers
|
|
|
|
y:list:number # y is a list of numbers
|
2014-12-14 21:21:32 +00:00
|
|
|
```
|
|
|
|
|
2015-06-08 21:24:05 +00:00
|
|
|
Recipes load their ingredients from their caller using the *next-ingredient*
|
|
|
|
instruction, and return results using *reply*.
|
|
|
|
|
2015-01-03 02:57:49 +00:00
|
|
|
Try out the factorial program now:
|
|
|
|
|
|
|
|
```shell
|
|
|
|
$ ./mu factorial.mu
|
|
|
|
result: 120 # factorial of 5
|
|
|
|
```
|
|
|
|
|
2015-06-08 21:24:05 +00:00
|
|
|
You can also run its unit tests:
|
|
|
|
|
|
|
|
```shell
|
|
|
|
$ ./mu test factorial.mu
|
|
|
|
```
|
|
|
|
|
|
|
|
Here's what one of the tests inside `factorial.mu` looks like:
|
|
|
|
|
|
|
|
![test example](http://i.imgur.com/SiQv9gn.png)
|
|
|
|
|
|
|
|
Every test conceptually spins up a really lightweight virtual machine, so you
|
|
|
|
can do things like check the value of specific locations in memory. You can
|
|
|
|
also print to screen and check that the screen contains what you expect at the
|
|
|
|
end of a test. For example, `chessboard.mu` checks the initial position of a
|
|
|
|
game of chess (delimiting the edges of the screen with periods):
|
|
|
|
|
|
|
|
![screen test](http://i.imgur.com/ufopuF8.png)
|
|
|
|
|
|
|
|
Similarly you can fake the keyboard to pretend someone typed something:
|
|
|
|
|
|
|
|
```
|
|
|
|
assume-keyboard [a2-a4]
|
|
|
|
```
|
|
|
|
|
|
|
|
As we add a file system, graphics, audio, network support and so on, we'll
|
|
|
|
augment scenarios with corresponding abilities to use them inside tests.
|
|
|
|
|
2015-01-03 02:57:49 +00:00
|
|
|
---
|
|
|
|
|
2015-05-06 04:17:24 +00:00
|
|
|
The name of a reagent is for humans, but what the computer needs to access it is
|
2015-01-03 02:57:49 +00:00
|
|
|
its address. Mu maps names to addresses for you like in other languages, but
|
2015-01-03 03:04:15 +00:00
|
|
|
in a more transparent, lightweight, hackable manner. This instruction:
|
2015-01-03 02:57:49 +00:00
|
|
|
|
2015-05-06 04:17:24 +00:00
|
|
|
```python
|
2015-05-13 17:03:26 +00:00
|
|
|
z:number <- add x:number, y:number
|
2015-01-03 02:57:49 +00:00
|
|
|
```
|
|
|
|
|
|
|
|
might turn into this:
|
|
|
|
|
2015-05-06 04:17:24 +00:00
|
|
|
```python
|
2015-05-13 17:03:26 +00:00
|
|
|
3:number <- add 1:number, 2:number
|
2015-01-03 02:57:49 +00:00
|
|
|
```
|
|
|
|
|
2015-03-10 19:16:28 +00:00
|
|
|
You shouldn't rely on the specific address Mu chooses for a variable, but it
|
2015-01-03 02:57:49 +00:00
|
|
|
will be unique (other variables won't clobber it) and consistent (all mentions
|
|
|
|
of the name will map to the same address inside a function).
|
|
|
|
|
|
|
|
Things get more complicated when your functions call other functions. Mu
|
2015-03-20 03:50:00 +00:00
|
|
|
doesn't preserve uniqueness of addresses across functions, so you need to
|
|
|
|
organize your names into spaces. At the start of each function (like
|
|
|
|
`factorial` above), set its *default space*:
|
2015-01-03 02:57:49 +00:00
|
|
|
|
2015-05-06 04:17:24 +00:00
|
|
|
```python
|
|
|
|
default-space:address:array:location <- new location:type, 30:literal
|
2015-01-03 02:57:49 +00:00
|
|
|
```
|
|
|
|
|
|
|
|
Without this line, all variables in the function will be *global*, something
|
|
|
|
you rarely want. (Luckily, this is also the sort of mistake that will be
|
|
|
|
easily caught by tests. Later we'll automatically generate this boilerplate.)
|
|
|
|
*With* this line, all addresses in your function will by default refer to one
|
2015-05-06 04:17:24 +00:00
|
|
|
of the 30 slots inside this local space. (If you need more, mu will complain.)
|
2015-01-03 02:57:49 +00:00
|
|
|
|
|
|
|
Spaces can do more than just implement local variables. You can string them
|
|
|
|
together, pass them around, return them from functions, share them between
|
|
|
|
parallel routines, and much else. However, any function receiving a space has
|
|
|
|
to know the names and types of variables in it, so any instruction should
|
|
|
|
always receive spaces created by the same function, no matter how many times
|
|
|
|
it's run. (If you're familiar with lexical scope, this constraint is
|
|
|
|
identical to it.)
|
|
|
|
|
|
|
|
To string two spaces together, write one into slot 0 of the other. This
|
|
|
|
instruction chains a space received from its caller:
|
|
|
|
|
2015-05-06 04:17:24 +00:00
|
|
|
```python
|
|
|
|
0:address:array:location <- next-ingredient
|
2015-01-03 02:57:49 +00:00
|
|
|
```
|
|
|
|
|
|
|
|
Once you've chained spaces together, you can access variables in them by
|
2015-05-06 04:17:24 +00:00
|
|
|
adding a 'space' property:
|
2015-01-03 02:57:49 +00:00
|
|
|
|
2015-05-06 04:17:24 +00:00
|
|
|
```python
|
2015-05-13 17:03:26 +00:00
|
|
|
3:number/space:1
|
2015-01-03 02:57:49 +00:00
|
|
|
```
|
|
|
|
|
2015-05-06 04:17:24 +00:00
|
|
|
This reagent is the integer in slot 3 of the space chained in slot 0 of the
|
2015-01-03 02:57:49 +00:00
|
|
|
default space. We usually call it slot 3 in the 'next space'. `/space:2` would
|
|
|
|
be the next space of the next space, and so on.
|
|
|
|
|
|
|
|
See `counters.mu` for an example of managing multiple accumulators at once
|
|
|
|
without allowing them to clobber each other. This is a classic example of the
|
|
|
|
sorts of things closures and objects are useful for in other languages. Spaces
|
2015-03-10 19:16:28 +00:00
|
|
|
in Mu provide the same functionality.
|
2015-01-03 02:57:49 +00:00
|
|
|
|
|
|
|
---
|
|
|
|
|
2015-05-06 04:17:24 +00:00
|
|
|
You can append arbitrary properties to reagents besides types and spaces. Just
|
2015-01-03 02:57:49 +00:00
|
|
|
separate them with slashes.
|
2014-12-14 21:21:32 +00:00
|
|
|
|
2015-05-06 04:17:24 +00:00
|
|
|
```python
|
2015-05-13 17:03:26 +00:00
|
|
|
x:array:number:3/uninitialized
|
2014-12-14 21:21:32 +00:00
|
|
|
y:string/tainted:yes
|
2015-05-13 17:03:26 +00:00
|
|
|
z:list:number/assign-once:true/assigned:false
|
2014-12-14 21:21:32 +00:00
|
|
|
```
|
|
|
|
|
2015-03-10 19:16:28 +00:00
|
|
|
Most properties are meaningless to Mu, and it'll silently skip them when
|
2015-01-03 02:57:49 +00:00
|
|
|
running, but they are fodder for *meta-programs* to check or modify your
|
|
|
|
programs, a task other languages typically hide from their programmers. For
|
|
|
|
example, where other programmers are restricted to the checks their type
|
2014-12-14 21:21:32 +00:00
|
|
|
system permits and forces them to use, you'll learn to create new checks that
|
|
|
|
make sense for your specific program. If it makes sense to perform different
|
|
|
|
checks in different parts of your program, you'll be able to do that.
|
|
|
|
|
2015-05-06 04:17:24 +00:00
|
|
|
To summarize: Mu instructions have multiple ingredient and result reagents.
|
|
|
|
Values can have multiple rows separated by slashes, and rows can have multiple
|
|
|
|
columns separated by colons. The address of a reagent is always in the very
|
2015-01-08 03:34:23 +00:00
|
|
|
first column of the first row of its 'table'. You can visualize the last
|
|
|
|
example above as:
|
|
|
|
|
|
|
|
```
|
|
|
|
z : list : integer /
|
|
|
|
assign-once : true /
|
|
|
|
assigned : false
|
|
|
|
```
|
2014-11-25 07:59:55 +00:00
|
|
|
|
|
|
|
---
|
2014-11-01 23:15:15 +00:00
|
|
|
|
2015-01-03 02:57:49 +00:00
|
|
|
An alternative way to define factorial is by inserting *labels* and later
|
2014-11-26 16:30:26 +00:00
|
|
|
inserting code at them.
|
|
|
|
|
2015-05-06 04:17:24 +00:00
|
|
|
```python
|
|
|
|
recipe factorial [
|
|
|
|
default-space:address:array:location <- new location:type, 30:literal
|
2015-05-13 17:03:26 +00:00
|
|
|
n:number <- next-ingredient
|
2014-11-26 16:30:26 +00:00
|
|
|
{
|
2015-05-06 08:02:13 +00:00
|
|
|
+base-case:
|
2014-11-26 16:30:26 +00:00
|
|
|
}
|
2015-05-06 08:02:13 +00:00
|
|
|
+recursive-case:
|
2014-11-26 16:30:26 +00:00
|
|
|
]
|
|
|
|
|
2015-05-06 08:02:13 +00:00
|
|
|
after +base-case [
|
2015-05-06 04:17:24 +00:00
|
|
|
# if n=0 return 1
|
2015-05-13 17:03:26 +00:00
|
|
|
zero?:boolean <- equal n:number, 0:literal
|
2014-12-14 21:21:32 +00:00
|
|
|
break-unless zero?:boolean
|
|
|
|
reply 1:literal
|
2014-11-26 16:30:26 +00:00
|
|
|
]
|
|
|
|
|
2015-05-06 08:02:13 +00:00
|
|
|
after +recursive-case [
|
2015-05-06 04:17:24 +00:00
|
|
|
# return n * factorial(n-1)
|
2015-05-13 17:03:26 +00:00
|
|
|
x:number <- subtract n:number, 1:literal
|
|
|
|
subresult:number <- factorial x:number
|
|
|
|
result:number <- multiply subresult:number, n:number
|
|
|
|
reply result:number
|
2014-11-26 16:30:26 +00:00
|
|
|
]
|
|
|
|
```
|
|
|
|
|
|
|
|
(You'll find this version in `tangle.mu`.)
|
|
|
|
|
2015-05-11 19:00:50 +00:00
|
|
|
Any instruction without ingredients or products that starts with a
|
|
|
|
non-alphanumeric character is a label. By convention we use '+' to indicate
|
|
|
|
label names.
|
|
|
|
|
2015-03-10 19:16:28 +00:00
|
|
|
This is a good time to point out that `{` and `}` are also just labels in Mu
|
2015-02-02 20:19:47 +00:00
|
|
|
syntax, and that `break` and `loop` get rewritten as jumps to just after the
|
|
|
|
enclosing `}` and `{` respectively. This gives us a simple sort of structured
|
2015-03-10 19:16:28 +00:00
|
|
|
programming without adding complexity to the parser -- Mu functions remain
|
2015-02-02 20:19:47 +00:00
|
|
|
just flat lists of instructions.
|
|
|
|
|
2014-11-26 16:30:26 +00:00
|
|
|
---
|
|
|
|
|
2014-11-01 23:34:33 +00:00
|
|
|
Another example, this time with concurrency.
|
|
|
|
|
2015-05-11 16:59:29 +00:00
|
|
|
```
|
|
|
|
recipe main [
|
|
|
|
start-running thread2:recipe
|
|
|
|
{
|
|
|
|
$print 34:literal
|
|
|
|
loop
|
|
|
|
}
|
|
|
|
]
|
|
|
|
|
|
|
|
recipe thread2 [
|
|
|
|
{
|
|
|
|
$print 35:literal
|
|
|
|
loop
|
|
|
|
}
|
|
|
|
]
|
|
|
|
```
|
|
|
|
|
2014-11-26 15:04:04 +00:00
|
|
|
```shell
|
2014-12-13 08:33:20 +00:00
|
|
|
$ ./mu fork.mu
|
2014-11-25 17:17:18 +00:00
|
|
|
```
|
2014-11-01 23:34:33 +00:00
|
|
|
|
|
|
|
Notice that it repeatedly prints either '34' or '35' at random. Hit ctrl-c to
|
|
|
|
stop.
|
|
|
|
|
2014-11-26 16:48:06 +00:00
|
|
|
Yet another example forks two 'routines' that communicate over a channel:
|
2014-11-25 09:25:20 +00:00
|
|
|
|
2014-11-26 15:04:04 +00:00
|
|
|
```shell
|
2014-12-13 08:33:20 +00:00
|
|
|
$ ./mu channel.mu
|
2014-11-25 09:25:20 +00:00
|
|
|
produce: 0
|
|
|
|
produce: 1
|
|
|
|
produce: 2
|
|
|
|
produce: 3
|
|
|
|
consume: 0
|
|
|
|
consume: 1
|
|
|
|
consume: 2
|
|
|
|
produce: 4
|
|
|
|
consume: 3
|
|
|
|
consume: 4
|
|
|
|
|
|
|
|
# The exact order above might shift over time, but you'll never see a number
|
|
|
|
# consumed before it's produced.
|
2014-11-25 17:17:18 +00:00
|
|
|
```
|
2014-11-25 09:25:20 +00:00
|
|
|
|
2015-03-10 19:16:28 +00:00
|
|
|
Channels are the unit of synchronization in Mu. Blocking on channels are the
|
2014-11-25 09:25:20 +00:00
|
|
|
only way tasks can sleep waiting for results. The plan is to do all I/O over
|
|
|
|
channels that wait for data to return.
|
|
|
|
|
|
|
|
Routines are expected to communicate purely by message passing, though nothing
|
|
|
|
stops them from sharing memory since all routines share a common address
|
2015-03-10 19:16:28 +00:00
|
|
|
space. However, idiomatic Mu will make it hard to accidentally read or clobber
|
2014-11-25 09:25:20 +00:00
|
|
|
random memory locations. Bounds checking is baked deeply into the semantics,
|
|
|
|
and pointer arithmetic will be mostly forbidden (except inside the memory
|
|
|
|
allocator and a few other places).
|
|
|
|
|
|
|
|
---
|
|
|
|
|
2015-05-16 06:01:37 +00:00
|
|
|
If you're still reading, here are some more things to check out:
|
|
|
|
|
|
|
|
a) Look at the [chessboard program](http://akkartik.github.io/mu/html/chessboard.mu.html)
|
|
|
|
for a more complex example where I write tests showing blocking reads from the
|
|
|
|
keyboard and what gets printed to the screen -- things we don't typically
|
|
|
|
associate with automated tests.
|
|
|
|
|
|
|
|
b) Try skimming the [colorized source code](http://akkartik.github.io/mu). I'd
|
|
|
|
like it to eventually be possible to get a pretty good sense for how things
|
|
|
|
work just by skimming the files in order, skimming the top of each file and
|
|
|
|
ignoring details lower down. I'd love to hear feedback about how successful my
|
|
|
|
efforts are.
|
|
|
|
|
|
|
|
c) Try running the tests:
|
2014-11-01 23:15:15 +00:00
|
|
|
|
2014-11-26 15:04:04 +00:00
|
|
|
```shell
|
2015-05-06 04:17:24 +00:00
|
|
|
$ ./mu test
|
2014-11-25 17:17:18 +00:00
|
|
|
```
|
2014-11-25 07:59:55 +00:00
|
|
|
|
2014-11-26 16:30:26 +00:00
|
|
|
You might also want to peek in the `.traces` directory, which automatically
|
2015-03-10 19:16:28 +00:00
|
|
|
includes logs for each test showing you just how it ran on my machine. If Mu
|
2014-11-25 07:59:55 +00:00
|
|
|
eventually gets complex enough that you have trouble running examples, these
|
|
|
|
logs might help figure out if my system is somehow different from yours or if
|
|
|
|
I've just been insufficiently diligent and my documentation is out of date.
|
|
|
|
|
2015-03-10 19:16:28 +00:00
|
|
|
The immediate goal of Mu is to build up towards an environment for parsing and
|
2014-11-25 07:59:55 +00:00
|
|
|
visualizing these traces in a hierarchical manner, and to easily turn traces
|
|
|
|
into reproducible tests by flagging inputs entering the log and outputs
|
|
|
|
leaving it. The former will have to be faked in, and the latter will want to
|
|
|
|
be asserted on, to turn a trace into a test.
|
|
|
|
|
2014-11-29 15:52:08 +00:00
|
|
|
**Credits**
|
2014-11-25 07:59:55 +00:00
|
|
|
|
|
|
|
Mu builds on many ideas that have come before, especially:
|
|
|
|
|
2014-11-26 15:04:04 +00:00
|
|
|
- [Peter Naur](http://alistair.cockburn.us/ASD+book+extract%3A+%22Naur,+Ehn,+Musashi%22)
|
2014-11-25 07:59:55 +00:00
|
|
|
for articulating the paramount problem of programming: communicating a
|
|
|
|
codebase to others;
|
2014-11-26 15:04:04 +00:00
|
|
|
- [Christopher Alexander](http://www.amazon.com/Notes-Synthesis-Form-Harvard-Paperbacks/dp/0674627512)
|
|
|
|
and [Richard Gabriel](http://dreamsongs.net/Files/PatternsOfSoftware.pdf) for
|
2014-11-25 07:59:55 +00:00
|
|
|
the intellectual tools for reasoning about the higher order design of a
|
|
|
|
codebase;
|
|
|
|
- Unix and C for showing us how to co-evolve language and OS, and for teaching
|
|
|
|
the (much maligned, misunderstood and underestimated) value of concise
|
|
|
|
*implementation* in addition to a clean interface;
|
2014-11-26 15:04:04 +00:00
|
|
|
- Donald Knuth's [literate programming](http://www.literateprogramming.com/knuthweb.pdf)
|
2014-11-25 07:59:55 +00:00
|
|
|
for liberating "code for humans to read" from the tyranny of compiler order;
|
2014-11-26 15:04:04 +00:00
|
|
|
- [David Parnas](http://www.cs.umd.edu/class/spring2003/cmsc838p/Design/criteria.pdf)
|
2014-11-25 07:59:55 +00:00
|
|
|
and others for highlighting the value of separating concerns and stepwise
|
|
|
|
refinement;
|
2014-11-26 15:04:04 +00:00
|
|
|
- [Lisp](http://www.paulgraham.com/rootsoflisp.html) for showing the power of
|
2014-11-25 07:59:55 +00:00
|
|
|
dynamic languages, late binding and providing the right primitives a la
|
|
|
|
carte, especially lisp macros;
|
|
|
|
- The folklore of debugging by print and the trace facility in many lisp
|
|
|
|
systems;
|
|
|
|
- Automated tests for showing the value of developing programs inside an
|
|
|
|
elaborate harness;
|
2014-11-26 15:04:04 +00:00
|
|
|
- [Python doctest](http://docs.python.org/2/library/doctest.html) for
|
2014-11-25 07:59:55 +00:00
|
|
|
exemplifying interactive documentation that doubles as tests;
|
2014-11-26 15:04:04 +00:00
|
|
|
- [ReStructuredText](https://en.wikipedia.org/wiki/ReStructuredText)
|
|
|
|
and [its antecedents](https://en.wikipedia.org/wiki/Setext) for showing that
|
2014-11-25 07:59:55 +00:00
|
|
|
markup can be clean;
|
|
|
|
- BDD for challenging us all to write tests at a higher level;
|
|
|
|
- JavaScript and CSS for demonstrating the power of a DOM for complex
|
|
|
|
structured documents.
|