1 Mu explores ways to turn arbitrary manual tests into reproducible automated
2 tests. Hoped-for benefits:
4 1. Projects release with confidence without requiring manual QA or causing
5 regressions for their users.
7 1. Open source projects become easier for outsiders to comprehend, since they
8 can more confidently try out changes with the knowledge that they'll get
9 rapid feedback if they break something. Projects also become more
10 *rewrite-friendly* for insiders: it's easier to leave your project's
11 historical accidents and other baggage behind if you can be confident of
12 not causing regressions.
14 1. It becomes easier to teach programming by emphasizing tests far earlier
17 The hypothesis is that designing the entire system to be testable from day 1
18 and from the ground up would radically impact the culture of an eco-system in
19 a way that no bolted-on tool or service at higher levels can replicate. It
20 would make it easier to write programs that can be [easily understood by newcomers](http://akkartik.name/about).
21 It would reassure authors that an app is free from regression if all automated
22 tests pass. It would make the stack easy to rewrite and simplify by dropping
23 features, without fear that a subset of targeted apps might break. As a result
24 people might fork projects more easily, and also exchange code between
25 disparate forks more easily (copy the tests over, then try copying code over
26 and making tests pass, rewriting and polishing where necessary). The community
27 would have in effect a diversified portfolio of forks, a “wavefront” of
28 possible combinations of features and alternative implementations of features
29 instead of the single trunk with monotonically growing complexity that we get
30 today. Application writers who wrote thorough tests for their apps (something
31 they just can’t do today) would be able to bounce around between forks more
32 easily without getting locked in to a single one as currently happens.
34 In this quest, Mu is currently experimenting with the following mechanisms:
36 1. New, testable interfaces for the operating system. Currently manual tests
37 are hard to automate because a file you rely on might be deleted, the
38 network might go down, etc. To make manual tests reproducible it suffices
39 to improve the 15 or so OS syscalls through which a computer talks to the
40 outside world. We have to allow programs to transparently write to a fake
41 screen, read from a fake disk/network, etc. In Mu, printing to screen
42 explicitly takes a screen object, so it can be called on the real screen,
43 or on a fake screen inside tests, so that we can then check the expected
44 state of the screen at the end of a test. Here's a test for a little
45 text-mode chessboard program in Mu (delimiting the edge of the 'screen'
48 <img alt='a screen test' src='https://github.com/akkartik/mu/html/archive/2.vm/chessboard-test.png'>
50 We've built up similarly *dependency-injected* interfaces to the keyboard,
51 mouse, disk and network.
53 1. Support for testing side-effects like performance, deadlock-freedom,
54 race-freeness, memory usage, etc. Mu's *white-box tests* can check not just
55 the results of a function call, but also the presence or absence of
56 specific events in the log of its progress. For example, here's a test that
57 our string-comparison function doesn't scan individual characters unless it
60 <img alt='white-box test' src='https://github.com/akkartik/mu/html/archive/2.vm/tracing-test.png'>
62 Another example: if a sort function logs each swap, a performance test can
63 check that the number of swaps doesn't quadruple when the size of the input
66 Besides expanding the scope of tests, this ability also allows more
67 radical refactoring without needing to modify tests. All Mu's tests call a
68 top-level function rather than individual sub-systems directly. As a result
69 the way the subsystems are invoked can be radically changed (interface
70 changes, making synchronous functions asynchronous, etc.). As long as the
71 new versions emit the same implementation-independent events in the logs,
72 the tests will continue to pass. ([More information.](http://akkartik.name/post/tracing-tests))
74 1. Organizing code and tests in layers of functionality, so that outsiders can
75 build simple and successively more complex versions of a project, gradually
76 enabling more peripheral features. Think of it as a cleaned-up `git log`
77 for the project. ([More information.](http://akkartik.name/post/wart-layers))
79 These mechanisms exist in the context of a low-level statement-oriented
80 language (like Basic, or Assembly). The language is as powerful as C for
81 low-level pointer operations and manual memory management, but much safer,
82 paying some run-time overhead to validate pointers. It also provides a number
83 of features usually associated with higher-level languages: strong
84 type-safety, function overloading, lexical scope, generic functions,
85 higher-order functions, and [delimited continuations](http://akkartik.name/coroutines-in-mu).
87 *Taking Mu for a spin*
89 Mu is currently implemented in C++ and requires a Unix-like environment. It's
90 been tested on Ubuntu, Mac OS X and OpenBSD; on x86, x86\_64 and ARMv7; and on
91 recent versions of GCC and Clang. Since it uses no bleeding-edge language
92 features and has no exotic dependencies, it should work with most reasonable
93 versions, compilers or processors.
95 Running Mu will always (re)compile it if necessary:
102 As a simple example, here's a program with some arithmetic:
104 <img alt='code example' src='https://github.com/akkartik/mu/html/archive/2.vm/example1.png'>
106 Mu functions are lists of instructions, one to a line. Each instruction
107 operates on some *ingredients* and returns some *products*.
110 [products] <- instruction [ingredients]
113 Product and ingredient *reagents* cannot contain instructions or infix
114 expressions. On the other hand, you can have any number of them. In
115 particular, you can have any number of products. For example, you can perform
116 integer division as follows:
119 quotient:number, remainder:number <- divide-with-remainder 11, 3
122 Each reagent consists of a name and its type, separated by a colon. You only
123 have to specify the type the first time you mention a name, but you can be
124 more explicit if you choose. Types can be multiple words and even arbitrary
128 x:array:number:3 # x is an array of 3 numbers
129 y:list:number # y is a list of numbers
130 # ':' is just syntactic sugar
131 {z: (map (address array character) (list number))} # map from string to list of numbers
134 Try out the program now:
141 Not much to see yet, since it doesn't print anything. To print the result, try
142 adding the instruction `$print a` to the function.
146 Here's a second example, of a function that can take ingredients:
148 <img alt='fahrenheit to celsius' src='https://github.com/akkartik/mu/html/archive/2.vm/f2c-1.png'>
150 Functions can specify headers showing their expected ingredients and products,
151 separated by `->` (unlike the `<-` in calls).
153 Once defined, functions can be called just like primitives. No need to mess
154 with a `CALL` instruction or push/pop arguments to the stack.
156 Since Mu is a low-level VM language, it provides extra control at the cost of
157 verbosity. Using `local-scope`, you have explicit control over stack frames to
158 isolate your functions in a type-safe manner. You can also create more
159 sophisticated setups like closures. One consequence of this extra control: you
160 have to explicitly `load-ingredients` after you set up the stack.
162 An alternative syntax is what the above example is converted to internally:
164 <img alt='fahrenheit to celsius desugared' src='https://github.com/akkartik/mu/html/archive/2.vm/f2c-2.png'>
166 The header gets dropped after checking types at call-sites, and after
167 replacing `load-ingredients` with explicit instructions to load each
168 ingredient separately, and to explicitly return products to the caller. After
169 this translation functions are once again just lists of instructions.
171 This alternative syntax isn't just an implementation detail. It turns out to
172 be easier to teach functions to non-programmers by starting with this syntax,
173 so that they can visualize a pipe from caller to callee, and see the names of
174 variables get translated one by one through the pipe.
178 A third example, this time illustrating conditionals:
180 <img alt='factorial example' src='https://github.com/akkartik/mu/html/archive/2.vm/factorial.png'>
182 In spite of how it looks, this is still just a list of instructions and
183 labels. Internally, the instructions `break` and `loop` get converted to
184 `jump` instructions to after the enclosing `}` or `{` labels, respectively.
186 Try out the factorial program now:
190 result: 120 # factorial of 5
193 You can also run its unit tests:
196 $ ./mu test factorial.mu
199 Here's what one of the tests inside `factorial.mu` looks like:
201 <img alt='test example' src='https://github.com/akkartik/mu/html/archive/2.vm/factorial-test.png'>
203 Every test conceptually spins up a really lightweight virtual machine, so you
204 can do things like check the value of specific locations in memory. You can
205 also print to screen and check that the screen contains what you expect at the
206 end of a test. For example, you've seen earlier how `chessboard.mu` checks the
207 initial position of a game of chess (delimiting the edges of the screen with
210 <img alt='screen test' src='https://github.com/akkartik/mu/html/archive/2.vm/chessboard-test.png'>
212 Similarly you can fake the keyboard to pretend someone typed something:
214 <img alt='fake keyboard' src='https://github.com/akkartik/mu/html/archive/2.vm/fake-keyboard.png'>
216 ..or clicked somewhere:
218 <img alt='fake console (keyboard, mouse, ..)' src='https://github.com/akkartik/mu/html/archive/2.vm/fake-console.png'>
220 Within tests you can map arbitrary paths (local files or URLs) to contents:
222 <img alt='fake file-system and network' src='https://github.com/akkartik/mu/html/archive/2.vm/resources.png'>
224 As we add graphics, audio, and so on, we'll augment scenarios with
225 corresponding abilities.
229 Mu assumes that all ingredients passed in to functions are immutable by
230 default -- *unless* they are also products. So this program will throw an
233 <img alt='immutable ingredient triggering an error' src='https://github.com/akkartik/mu/html/archive/2.vm/immutable-error.png'>
235 To modify `foo`'s ingredient, you have to add it to the list of products
238 <img alt='mutable ingredient' src='https://github.com/akkartik/mu/html/archive/2.vm/mutable.png'>
240 The names of the variables are important here: a function that takes an
241 (immutable) address and returns a different one is different from a function
242 that takes a mutable address (and also returns it).
244 These immutability checks can be annoying, but the benefit they provide is
245 that you can always tell what a function modifies just by looking at its
246 header. In combination with dependency-injected hardware, they provide all the
247 benefits of [referential transparency](https://en.wikipedia.org/wiki/Referential_transparency)
248 that we typically associate with purely functional languages -- along with the
249 option of imperatively modifying variables willy-nilly.
253 You can append arbitrary properties to reagents besides types. Just separate
257 x:array:number:3/uninitialized
259 z:number/assign-once:true/assigned:false
262 Most properties are meaningless to Mu, and it'll silently skip them when
263 running, but they are fodder for *meta-programs* to check or modify your
264 programs, a task other languages typically hide from their programmers. For
265 example, where other programmers are restricted to the checks their type
266 system permits and forces them to use, you'll learn to create new checks that
267 make sense for your specific program. If it makes sense to perform different
268 checks in different parts of your program, you'll be able to do that.
270 You can imagine each reagent as a table, rows separated by slashes, columns
271 within a row separated by colons. So the last example above would become
282 An alternative way to define factorial is by inserting labels and later
283 inserting code at them.
285 <img alt='literate programming' src='https://github.com/akkartik/mu/html/archive/2.vm/tangle.png'>
287 (You'll find this version in `tangle.mu`.)
289 By convention we use the prefix '+' to indicate function-local label names you
290 can jump to, and surround in '<>' global label names for inserting code at.
294 Another example, this time with concurrency:
296 <img alt='forking concurrent routines' src='https://github.com/akkartik/mu/html/archive/2.vm/fork.png'>
302 Notice that it repeatedly prints either '34' or '35' at random. Hit ctrl-c to
305 [Yet another example](https://github.com/akkartik/mu/blob/master/archive/2.vm/channel.mu) forks
306 two 'routines' that communicate over a channel:
321 # The exact order above might shift over time, but you'll never see a number
322 # consumed before it's produced.
325 Channels are the unit of synchronization in Mu. Blocking on a channel is the
326 only way for the OS to put a task to sleep. The plan is to do all I/O over
329 Routines are expected to communicate purely by message passing, though nothing
330 stops them from sharing memory since all routines share a common address
331 space. However, idiomatic Mu will make it hard to accidentally read or
332 clobber random memory locations. Bounds checking is baked deeply into
333 the semantics, and using pointers after freeing them immediately fails.
337 Mu has a programming environment:
345 <img alt='programming environment' src='https://github.com/akkartik/mu/html/archive/2.vm/edit.png'>
347 You write functions on the left and try them out in *sandboxes* on the right.
348 Hit F4 to rerun all sandboxes with the latest version of the code. More
349 details: http://akkartik.name/post/mu. Beware, it won't save your edits by
350 default. But if you create a sub-directory called `lesson/` under `mu/` it
351 will. If you turn that directory into a git repo with `git init`, it will also
352 back up your changes each time you hit F4. Use the provided `new_lesson`
353 script to take care of these details.
355 Once you have a sandbox you can click on its result to mark it as expected:
357 <img alt='expected result' src='https://github.com/akkartik/mu/html/archive/2.vm/expected-result.png'>
359 Later if the result changes it'll be flagged in red to draw your attention to
360 it. Thus, manually tested sandboxes become reproducible automated tests.
362 <img alt='unexpected result' src='https://github.com/akkartik/mu/html/archive/2.vm/unexpected-result.png'>
364 Another feature: Clicking on the code in a sandbox expands its trace for you
365 to browse. To add to the trace, use `stash`. For example:
368 stash [first ingredient is], x
371 Invaluable at times for understanding program behavior, but it won't clutter
372 up your screen by default.
376 If you're still reading, here are some more things to check out:
378 a) Look at the [chessboard program](https://github.com/akkartik/mu/blob/master/archive/2.vm/chessboard.mu)
379 for a more complex example with tests of blocking reads from the keyboard and
380 what gets printed to the screen -- things we don't typically associate with
383 b) Try running the tests:
389 c) Check out [the programming environment](https://github.com/akkartik/mu/tree/master/edit#readme),
390 the largest app built so far in Mu.
392 d) Check out the tracing infrastructure which gives you a maps-like zoomable
393 UI for browsing Mu's traces:
396 $ ./mu --trace nqueens.mu # just an example
397 saving trace to 'last_run'
398 $ ./browse_trace/browse_trace last_run
402 For more details see the [Readme](browse_trace/Readme.md).
404 e) Look at the `build` scripts. Mu's compilation process is itself designed to
405 support staged learning. Each of the scripts (`build0`, `build1`, `build2`,
406 etc.) is self-contained and can compile the project by itself. Successive
407 versions add new features and configurability -- and complexity -- to the
410 f) Try skimming the source code. You should be able to get a pretty good sense
411 for how things work just by skimming the files in order, skimming the top of
412 each file and ignoring details lower down.
413 [Some details on my unconventional approach to organizing projects.](http://akkartik.name/post/four-repos)
417 Mu builds on many ideas that have come before, especially:
419 - [Peter Naur](http://alistair.cockburn.us/ASD+book+extract%3A+%22Naur,+Ehn,+Musashi%22)
420 for articulating the paramount problem of programming: communicating a
422 - [Christopher Alexander](http://www.amazon.com/Notes-Synthesis-Form-Harvard-Paperbacks/dp/0674627512)
423 and [Richard Gabriel](http://dreamsongs.net/Files/PatternsOfSoftware.pdf) for
424 the intellectual tools for reasoning about the higher order design of a
426 - Unix and C for showing us how to co-evolve language and OS, and for teaching
427 the (much maligned, misunderstood and underestimated) value of concise
428 *implementation* in addition to a clean interface;
429 - Donald Knuth's [literate programming](http://www.literateprogramming.com/knuthweb.pdf)
430 for liberating "code for humans to read" from the tyranny of compiler order;
431 - [David Parnas](http://www.cs.umd.edu/class/spring2003/cmsc838p/Design/criteria.pdf)
432 and others for highlighting the value of separating concerns and stepwise
434 - [Lisp](http://www.paulgraham.com/rootsoflisp.html) for showing the power of
435 dynamic languages, late binding and providing the right primitives *a la
436 carte*, especially lisp macros;
437 - The folklore of debugging by print and the trace facility in many lisp
439 - Automated tests for showing the value of developing programs inside an
441 - [Python doctest](http://docs.python.org/2/library/doctest.html) for
442 exemplifying interactive documentation that doubles as tests;
443 - [ReStructuredText](https://en.wikipedia.org/wiki/ReStructuredText)
444 and [its antecedents](https://en.wikipedia.org/wiki/Setext) for showing that
446 - BDD for challenging us all to write tests at a higher level;
447 - JavaScript and CSS for demonstrating the power of a DOM for complex
448 structured documents.
449 - Rust for demonstrating that a system-programming language can be safe.