Mu explores ways to turn arbitrary manual tests into reproducible automated tests. Hoped-for benefits: 1. Projects release with confidence without requiring manual QA or causing regressions for their users. 1. Open source projects become easier for outsiders to comprehend, since they can more confidently try out changes with the knowledge that they'll get rapid feedback if they break something. As a result projects become more *rewrite-friendly*: it's easier to leave your project's historical accidents and other baggage behind if you can be confident of not causing regressions. 1. It becomes easier to teach programming by emphasizing tests far earlier than we do today. In this quest, Mu is currently experimenting with the following mechanisms: 1. New, testable interfaces for the operating system. Currently manual tests are hard to automate because a file you assumed might vanish, the network might go down, etc. To make manual tests reproducible it suffices to improve the 15 or so OS syscalls through which a computer talks to the outside world. We have to allow programs to transparently write to a fake screen, read from a fake disk/network, etc. In Mu, printing to screen explicitly takes a screen object, so it can be called on the real screen, or on a fake screen inside tests, so that we can then check the expected state of the screen at the end of a test. Here's a test for a little text-mode chessboard program in Mu (delimiting the edge of the 'screen' with dots):
      a screen test
I'm building up similarly *dependency-injected* interfaces to the keyboard, mouse, touch screen, disk, network, etc. 1. Support for testing side-effects like performance, deadlock-freedom, race-freeness, memory usage, etc. Mu's *white-box tests* can check not just the results of a function call, but also the presence or absence of specific events in the log of its progress. For example, here's a test that our string-comparison function doesn't scan individual characters unless it has to:
      white-box test
Another example: if a sort function logs each swap, a performance test can ensure that the number of swaps doesn't quadruple when the size of the input doubles.

Besides expanding the scope of tests, this ability also allows more radical refactoring without needing to modify tests. All Mu's tests call a top-level function rather than individual sub-systems directly. As a result the way the subsystems are invoked can be radically changed (interface changes, making synchronous functions asynchronous, etc.). As long as the new versions emit the same implementation-independent events in the logs, the tests will continue to pass. ([More information.](http://akkartik.name/post/tracing-tests)) 1. Organizing code and tests in layers of functionality, so that outsiders can build simple and successively more complex versions of a project, gradually enabling more peripheral features. Think of it as a cleaned-up `git log` for the project. ([More information.](http://akkartik.name/post/wart-layers)) Since I don't understand how Linux and other modern platforms work, Mu is built on an idealized VM while I learn. Eventually the plan is to transplant what I learn back to Linux. To minimize my workload, Mu doesn't have a high-level language yet. Instead, I've been programming directly in the VM's idealized assembly language. I expected this to be painful, but it's had some surprising benefits. First, programs as lists of instructions seem to be easier for non-programmers to comprehend than programs as trees of expressions. Second, I've found that Literate Programming using layers makes assembly much more ergonomic. Third, labels for gotos turn out to be great waypoints to insert code at from future layers; when I tried to divide C programs into layers, I sometimes had to split statements in two so I could insert code between them. High level languages today seem to provide three kinds of benefits: expressiveness (e.g. nested expressions, classes), safety (e.g. type checking) and automation (e.g. garbage collection). Mu gives up some expressiveness by not providing recursive expressions, but still supports lexical scope, generic types, and higher-order functions. It provides strong memory safety in spite of having manual memory management and supporting full-scale pointer operations (albeit at the cost of some runtime checks). *Taking Mu for a spin* Mu is currently implemented in C++ and requires a unix-like environment. It's been tested on Ubuntu and Mac OS X, on x86, x86\_64 and ARMv7 with recent versions of gcc and clang. Since it uses no recent language features and has no exotic dependencies, it should work with most reasonable versions, compilers or processors. [![Build Status](https://api.travis-ci.org/akkartik/mu.svg)](https://travis-ci.org/akkartik/mu) Running Mu will always recompile it if necessary: ```shell $ cd mu $ ./mu ``` As a simple example, here's a program with some arithmetic: code example As I said before, Mu functions are lists of instructions, one to a line. Each instruction operates on some *ingredients* and returns some *products*. ``` [products] <- instruction [ingredients] ``` Result and ingredient *reagents* cannot contain instructions or infix expressions. On the other hand, you can have any number of them. In particular, you can have any number of products. For example, you can perform integer division as follows: ``` quotient:number, remainder:number <- divide-with-remainder 11, 3 ``` Each reagent consists of a name and its type, separated by a colon. You only have to specify the type the first time you mention a name, but you can be more explicit if you choose. Types can be multiple words and even arbitrary trees, like: ```nim x:array:number:3 # x is an array of 3 numbers y:list:number # y is a list of numbers # ':' is just syntactic sugar {z: (map (address array character) (list number))} # map from string to list of numbers ``` Try out the program now: ```shell $ ./mu example1.mu $ ``` Not much to see yet, since it doesn't print anything. To print the result, try adding the instruction `$print a` to the function. --- Here's a second example, of a function that can take ingredients: fahrenheit to celsius Functions can specify headers showing their expected ingredients and products, separated by `->` (unlike the `<-` in calls). Since Mu is a low-level VM language, it provides extra control at the cost of verbosity. Using `local-scope`, you have explicit control over stack frames to isolate your functions in a type-safe manner. You can also create more sophisticated setups like closures. One consequence of this extra control: you have to explicitly `load-ingredients` after you set up the stack. An alternative syntax is what the above example is converted to internally: fahrenheit to celsius desugared The header gets dropped after checking types at call-sites, and after replacing `load-ingredients` with explicit instructions to load each ingredient separately, and to explicitly return products to the caller. After this translation functions are once again just lists of instructions. This alternative syntax isn't just an implementation detail. I've actually found it easier to teach functions to non-programmers by starting with this syntax, so that they can visualize a pipe from caller to callee, and see the names of variables get translated one by one through the pipe. --- A third example, this time illustrating conditionals: factorial example In spite of how it looks, this is still just a list of instructions and labels. Internally, the instructions `break` and `loop` get converted to `jump` instructions to after the enclosing `}` or `{` labels, respectively. Try out the factorial program now: ```shell $ ./mu factorial.mu result: 120 # factorial of 5 ``` You can also run its unit tests: ```shell $ ./mu test fac

//: You guessed right: the '000' prefix means you should start reading here.
//:
//: This project is set up to load all files with a numeric prefix. Just
//: create a new file and start hacking.
//:
//: The first few files (00*) are independent of what this program does, an
//: experimental skeleton that will hopefully make it both easier for others to
//: understand and more malleable, easier to rewrite and remould into radically
//: different shapes without breaking in subtle corner cases. The premise is
//: that understandability and rewrite-friendliness are related in a virtuous
//: cycle. Doing one well makes it easier to do the other.
//:
//: Lower down, this file contains a legal, bare-bones C++ program. It doesn't
//: do anything yet; subsequent files will contain :(...) directives to insert
//: lines into it. For example:
//:   :(after "more events")
//: This directive means: insert the following lines after a line in the
//: program containing the words "more events".
//:
//: A simple tool is included to 'tangle' all the files together in sequence
//: according to their directives into a single source file containing all the
//: code for the project, and then feed the source file to the compiler.
//: (It'll drop these comments starting with a '//:' prefix that only make
//: sense before tangling.)
//: