μ
grammar reference
Grammar
μ is parsed right-to-left. A program is a sequence of expressions separated by newlines or semicolons. The last expression in a sequence is the result.
program ::= sequence EOF
sequence ::= expr (';' expr)*
expr ::= name ':' expr -- assignment
| verb_expr
verb_expr ::= composition verb adverb? verb_expr -- dyadic
| verb adverb? verb_expr -- monadic / adverb
| composition
composition ::= strand ('.' strand)* -- function composition
strand ::= atom+ -- juxtaposition / apply
atom ::= '(' sequence ')'
| '[' sequence (';' sequence)* ']' -- vector literal
| '{' arg_list? sequence '}' adverb? -- lambda
| name
| integer
| float
| char_literal
| string_literal
| symbol
arg_list ::= '[' name (';' name)* ']'
vector ::= '[' sequence (';' sequence)* ']' -- bracket vector (expr1;expr2;...)
adverb ::= '/' | '\' -- fold / scan (after a verb)
| '/:' -- each-right (after a verb or function)
| '\:' -- each-left (after a verb or function)
| "'" -- each (postfix on a name or lambda)
Verbs
Verbs are single characters. Their meaning depends on arity (monadic = one argument, dyadic = two).
| verb | monadic | dyadic |
|---|---|---|
+ | transpose (flip) | add |
- | negate | subtract |
* | first element | multiply |
*. | — | matrix multiply |
% | reciprocal | divide |
%. | — | matrix solve (Ax=b) |
! | iota (0..n-1) | modulo |
# | count | take |
@ | type tag | index |
= | — | equal (element-wise) |
< | sort ascending | min (returns lesser value); </ list = min of list |
> | sort descending | max (returns greater value); >/ list = max of list |
, | — | join / concatenate lists or atoms; ,/ = raze (flatten list-of-lists) |
| | list comprehension separator — see List Comprehensions | |
? | generate n uniform random floats in [0,1) | — |
_ | floor | drop |
Adverbs
Adverbs modify a verb or function to produce a new function. The left operand determines the mode at evaluation time.
| syntax | name | meaning | example |
|---|---|---|---|
v/ list | fold | reduce list with verb v, right-to-left; ,/ = raze (flatten list-of-lists) | +/ 1 2 3 → 6; ,/((1 2);(3 4)) → 1 2 3 4 |
v\ list | scan | running reduction, returns list; ,\ = cumulative join | +\ 1 2 3 → 1 3 6 |
f' list | each | map single-arg function f over every element | {x*x}' 1 2 3 → 1 4 9 |
n f/ x | iterate-fold | apply f to x exactly n times, return final value | 3 {x+1}/ 0 → 3 |
n f\ x | iterate-scan | apply f to x exactly n times, return all n+1 states | 3 {x+1}\ 0 → 0 1 2 3 |
f/ x | converge | apply f repeatedly until result stabilises | {(x+2.0%x)*0.5}/ 1.0 → 1.4142… |
f/: [x; list] | each-right | apply two-arg f[x; yi] for each element yi of list | {[a;b] a+b}/: [10; 1 2 3] → 11 12 13 |
f\: (list) x | each-left | apply two-arg f[xi; x] for each element xi of list | {[a;b] a*b}\: (1 2 3) 10 → 10 20 30 |
{[a;x;i]…}/[(seed); list] | ternary fold | reduce list with a 3-arg step function receiving accumulator, element, and 0-based index; returns final accumulator | {[a;x;i] a+x}/[(0); (1 2 3)] → 6 |
{[a;x;i]…}\[(seed); list] | ternary scan | same as ternary fold but returns all n+1 states (seed + one result per element) | {[a;x;i] a+x}\[(0); (1 2 3)] → 0 1 3 6 |
Disambiguation of f/ left operand at evaluation time (mirrors K):
- integer left (
n f/ x) → iterate-n fold - no left (
f/ list) → fold with verb, or map-then-fold with lambda - function literal with operand (
{body}/ x) → converge - 3-arg step function + bracket vector (
{[a;x;i]…}/[(seed); list]) → ternary fold/scan; indexiis 0-based
Function Composition
The . operator composes two functions: f . g creates a new function
that applies g first, then f to the result: (f . g)(x) = f(g(x)).
| syntax | meaning | example |
|---|---|---|
f . g | compose: apply g, then f | inc: {x+1}; dbl: {x*2}; h: inc . dbl; h 5 → 11 |
f . g . h | chained composition | sqrt . sum . {x*x} → Euclidean norm |
(f . g)' list | composed function with each | (sqrt . {x*x})' 1 2 3 → identity map |
math . f | math keywords are first-class functions | sqrt . exp' 0 1 2 → √e⁰ √e¹ √e² |
Composition has higher precedence than verbs but lower than strands:
sqrt . sq 9 parses as sqrt . (sq 9), not (sqrt . sq) 9.
To apply the composition: (sqrt . sq) 9 or f: sqrt . sq; f 9.
Math keywords bind to one atom: sqrt sq 9 is sqrt(sq 9) = sqrt 81 = 9.
I/O keywords
File and data keywords for reading, writing, and parsing structured data.
In the CLI, read and write operate on local file paths.
In the WASM playground, csv works via the JavaScript fetch bridge
(MuWASM.loadCSV).
| keyword | description | example |
|---|---|---|
read "path" |
Read a text file into a list of char-list lines | read "data.csv" → ("a,b";"1,2";"3,4") |
csv "path" |
Read & parse CSV file → column-oriented table (headers; columns).
Headers become symbols; values auto-detect as integer, float, or string. |
csv "data.csv" → (`a `b;(1 2;3 4)) |
csv lines |
Parse an in-memory list of char-list lines as CSV (same output format) | csv (read "f.csv") |
write["path"; data] |
Write a table or list of lines to a file (CLI only). Tables are written as CSV with headers. | write["out.csv"; t] |
The table format (headers; columns) is a 2-element list: element 0
is a list of symbols (column names), element 1 is a list of column vectors.
Index with t@0 for headers and (t@1)@i for column i.
Conditional semantics (cond)
Inside a lambda body, a semicolon-separated sequence acts as a K-style conditional chain, not a plain sequence:
{e0; e1; e2; e3} -- if e0 truthy → return e1; else if e2 truthy → return e3; ...
Each even-indexed expression is a condition; if truthy its successor is
evaluated and returned, stopping evaluation. A tail with no matching
condition returns nil. A single expression {e0}
is returned directly (no cond logic).
cfib:{x=0; 1; x=1; 1; (cfib x-1)+(cfib x-2)}
cfib 10 -- → 89
-- Truthiness: non-zero values are truthy.
-- Dyadic < and > return the min/max value, not a boolean:
-- 0>x is truthy when x>0 (returns x), falsy when x=0 (returns 0)
-- Boolean greater-than idiom: 0>a-b (truthy when a>b, falsy otherwise)
Lambda forms
| form | args | example |
|---|---|---|
{expr} |
implicit x (single arg); x y if body uses y |
{x+1} 5 → 6 |
{[x;y] expr} |
explicit named args | {[a;b] a*b} 3 4 → 12 |
{expr}/ |
map body over list, then fold result with + |
{x*x}/ 1 2 3 → 14 |
{expr}\ |
map body over list, then scan result with + |
{x*x}\ 1 2 3 → 1 5 14 |
List Comprehensions
List comprehensions provide a concise way to create lists from existing lists,
with optional filtering. They desugar to nested each' expressions.
[expr | var <- list; guard]
| introduces the generator. Multiple generators nest left-to-right.
An optional guard (after ;) filters elements — only elements for which
the guard is truthy are included in the result.
| pattern | meaning | example |
|---|---|---|
[x*x | x <- 1 2 3] |
map: square each element | → 1 4 9 |
[x | x <- !10; (2!x)=0] |
filter: even numbers 0–9 (2!x = x mod 2) |
→ 0 2 4 6 8 |
[[i;j] | i <- !3; j <- !3] |
multiple generators: cartesian product | → [0;0] [0;1] ... [2;2] |
[matget[cm;i;j] | i <- !10; j <- ((i+1)+!((10-i)-1))] |
practical: upper triangle of correlation matrix | extract off-diagonal elements |
Desugaring: [x*x | x <- 1 2 3] compiles to {[x] x*x}' 1 2 3.
Guards compile to filter-each expressions. This ensures efficient execution via μ's bump allocator
with automatic scratch-reset for memory efficiency.
Plot & visualisation keywords
In the CLI, plot verbs save SVG files to the current directory (filename derived from the chart title, or a timestamp if untitled) and print the saved path. In the WASM playground, they render to an inline canvas.
| keyword | description | example |
|---|---|---|
plot y |
Line plot of y values; x = 0, 1, 2, … |
plot 1 4 9 16 |
plot[x; y] |
Line plot of y vs explicit x |
plot[1 2 3; 2 4 6] |
plot[x; y; `Title] |
As above with a symbol title | plot[xs; ys; `Returns] |
plotpng y |
Same as plot but outputs PNG (requires rsvg-convert or ImageMagick) |
plotpng 1 4 9 16 |
plothist counts |
Bar chart of a counts list; x = 0, 1, 2, … | plothist 3 7 4 2 |
plothist[bins; counts] |
Bar chart with explicit bin centres | plothist[0.0 0.5 1.0; 3 7 4] |
plothist[counts; `Title] or plothist[bins; counts; `Title] |
As above with a symbol title | plothist[h; `Dist] |
plothist[bins; series_list] |
Multi-series grouped bar chart; series_list is a list of equal-length count vectors |
plothist[xs; (s1; s2; s3)] |
plothline val |
Overlay a dashed horizontal reference line at val on the most recent plot |
plot 1 4 9; plothline 5 |
heatmap[matrix] |
Render a matrix (list-of-lists) as a colour grid | heatmap[cut 3 !9] |
heatmap[matrix; `Title] |
Heatmap with a symbol title | heatmap[c; `Corr] |
plot3d[zs] |
3-D surface of a matrix of z values; x and y = 0, 1, 2, … | plot3d[m] |
plot3d[xs; ys; zs] or plot3d[xs; ys; zs; `Title] |
Full 3-D surface with explicit grid coordinates | plot3d[xs; ys; zs; `Surface] |
The hist keyword computes bin counts without plotting:
hist[n; vals] -- divide vals into n equal-width buckets; returns count list
hist[4; 0.0 0.5 1.0 1.5] -- → 1 1 1 1
Layered chart composition
The chart verb composes multiple layer descriptors into a single chart with
shared axes, auto-ranging, and palette colors. Layer constructors return tagged lists that
chart interprets at render time.
| keyword | description | example |
|---|---|---|
chart[layers...; `Title] |
Compose layers into a single chart (up to 16) | chart[line[x;y]; hline 5; `MyChart] |
line[x; y] |
Layer: polyline with dots | line[0 1 2; 1 4 9] |
scatter[x; y] |
Layer: scatter plot (circles) | scatter[xs; ys] |
band[x; ylo; yhi] |
Layer: filled region between ylo and yhi | band[x; lo; hi] |
bar[bins; counts] |
Layer: histogram bars | bar[xs; cs] |
hline val |
Layer: horizontal dashed reference line | hline 2.5 |
vline val |
Layer: vertical dashed reference line | vline 100 |
diag[] |
Layer: y = x diagonal reference | diag[] |
step[x; y] |
Layer: step function (staircase) | step[xs; cdf] |
qqplot data or qqplot[data; `Title] |
QQ normal probability plot (scatter + diagonal) | qqplot[samples; `QQ] |
Named math keywords
Math keywords are first-class functions — they live in the environment like any other name. They work element-wise over lists and can be used with adverbs, assigned to variables, or passed as arguments.
| keyword | operation | example |
|---|---|---|
sqrt | square root | sqrt 4.0 → 2.0 |
abs | absolute value | abs (-3) → 3.0 |
exp | ex | exp 0 → 1.0 |
log | natural logarithm | log 1.0 → 0.0 |
cos | cosine (radians) | cos 0 → 1.0 |
sin | sine (radians) | sin 0 → 0.0 |
tan | tangent (radians) | tan 0 → 0.0 |
acos | arc cosine — acos(-1) gives π | acos(-1) → 3.14159… |
asin | arc sine (radians) | asin 0 → 0.0 |
atan | arc tangent (radians) | atan 1 → 0.7854… |
Math keywords bind to a single adjacent atom — the same as a monadic verb.
sqrt x * y evaluates as (sqrt x) * y.
When the argument is a compound expression, use parentheses: sqrt(a*a + b*b).
neg is a verb alias for monadic - with identical atom-binding:
neg x * y = (-x) * y.
Because math keywords are first-class, they work directly with adverbs and assignment:
sin' 1 2 3 -- → 0.8414… 0.9092… 0.1411… (each)
f:sin; f' 1 2 3 -- assign and use via each
sin(1 2 3) -- parenthesised list: same result
Reference cells
Reference cells provide mutable state — a container that holds a value which can be read and updated. They enable imperative patterns like counters, accumulators, and shared mutable state.
| keyword | description | example |
|---|---|---|
ref x |
Create a reference cell containing x |
r:(ref 42) → ref[42] |
get r |
Read the current value from reference r |
get r → 42 |
set[r; y] |
Update reference r to contain y; returns y |
set[r; 100] → 100 |
mutate[r; f] |
Apply function f to current value, update r, return new value |
mutate[r; {x+1}] → 43 |
References print as ref[value] showing their current contents. They are first-class values and can be stored in lists, passed to functions, etc.
counter:{[n]
r:(ref 0);
{mutate[r; {x+1}]}' !n; -- increment n times
get r -- return final count
}
counter 5 -- → 5
Control flow
while is the only looping construct. It evaluates a condition expression repeatedly,
running the body statements as long as the condition is truthy. It returns the value of the last
body statement evaluated, or nothing if the body never executes.
while[condition; stmt1; stmt2; ...]
| example | result | notes |
|---|---|---|
cnt:0; while[cnt<3; cnt:cnt+1] |
3 |
Simple counter — single body statement |
i:0; s:0; while[i<3; i:i+1; s:s+i; s] |
6 |
Multiple statements; last value (s) returned |
x:1; while[x<=100; x:x*2; x] |
128 |
First power of 2 greater than 100 |
eps:1.0; while[eps>0.001; eps:eps*0.5; eps] |
0.000976… |
Converge to small epsilon |
x:5; while[x<0; x:x+1] |
(nothing) | Body never runs — condition is false from the start |
while modifies its local bindings in place. Variables updated in the body
(e.g. cnt above) are visible to subsequent iterations and after the loop.
For purely functional iteration prefer n f/ seed (iterate-fold).
Global variables
A dot-prefix on a name (.name) reads from or writes to a
shared global namespace that persists across all closures and is never captured
by a snapshot. This mirrors K-style globals and is useful for mutable shared state that
must be visible inside deeply nested lambdas.
.x: 42 -- write global x
.x -- read global x → 42
.x: .x + 1 -- update global x → 43
The key distinction from local assignment is capture semantics:
- Local (
x:val) — the optimizer snapshots the value into closures at definition time; mutations after definition are not seen by already-defined lambdas. - Global (
.x:val) — always read live; every call to a closure that reads.xsees the current global value.
.base:100
snap:{.base+x} -- closes over nothing; reads .base live
.base:999
snap 1 -- → 1000 (sees updated .base)
Global assignment inside a lambda writes back to the global namespace, not a local copy:
inc_global:{.counter:.counter+1}
.counter:0
inc_global[]; inc_global[]; .counter -- → 2
Literals
| literal | syntax | example |
|---|---|---|
| integer | decimal digits, optional leading - | 42, -7 |
| float | digits with ., or integer with f suffix | 3.14, -1.0, 1f, 0f |
| char | "c" (single-char string) or 'c' / 'c (apostrophe form) | "a", 'a' |
| string | "hello" — double-quoted; produces a list of char values; supports \n \t \" \\ escapes | "hi" |
| symbol | `name (backtick prefix) | `hello |
| list strand | space-separated atoms | 1 2 3 |
Assignment & sequencing
x: 42 -- assign 42 to x
f: {x+1} -- assign lambda to f
sum:+/ -- verb alias: sum is +/ fold
a:1; b:2; a+b -- semicolon sequences expressions; result is last
REPL commands
| command | action |
|---|---|
\l path | load and execute a .mu source file |
\S n | seed the random number generator with integer n |
\d | toggle debug mode (prints AST + memory stats) |
\t | toggle type annotations on output (appends :type to each result) |
\p name | set plot colour palette (e.g. \p warm) |
\\ | quit |
:q | quit |
Example: Black-Scholes Monte Carlo
Box-Muller transform and Monte Carlo pricing of a European call option.
Demonstrates random number generation, antithetic variates, and vectorised
arithmetic. Uses explicit named-arg lambdas to avoid the implicit-x/y/z
wrapping rule; avoids naming intermediate variables x, y,
or z (single-char implicit arg names) at the top level.
\S 7
// Box-Muller: produce n standard normal variates
bm:{[n] sqrt(-2*log(1.0-?n)) * cos(2*acos(-1)*?n)}
// European call: s0=spot, k=strike, r=rate, sig=vol, t=maturity, n=paths
mcCall:{[s0;k;r;sig;t;n] half:_(n%2);zz:bm half;zz:zz,-zz;drift:(r-0.5*sig*sig)*t;st:s0*(exp(drift))*(exp(sig*sqrt(t)*zz));exp(-r*t)*mean(0|st-k)}
mcCall 100 100 0.05 0.2 1.0 100000
// → ~10.45 (closed-form Black-Scholes: 10.4506)
Notes on μ syntax:
?ngenerates n uniform floats (monadic?);\S 7seeds the RNG.- Math keywords bind to a single atom:
sqrt(-2*log …)— the-2*log…is the atom inside(). Without parens,sqrt -2.0would try to take the sqrt of the literal-2.0. acos(-1)*xnow correctly evaluates as(acos(-1))*x= π·x. Keywords bind to one atom, not the full right expression.zz:zz,-zzappends the negated antithetic variates using monadic-; amend-join (zz,:…) is not supported.mean(fromstdlib/stats.mu) computes the arithmetic mean;avgis an alias defined in the same file.0|st-kclips the payoff at zero using dyadic|(element-wise max).
Example: Asian Option Monte Carlo
Arithmetic-average Asian call option priced by path simulation.
Uses iter to accumulate payoffs one path at a time (avoids
pool exhaustion for large n), and sums
(cumulative-sum scan, from stdlib/stdlib.mu) to build the
price path from log-returns.
\S 7
// Box-Muller: produce n standard normal variates
bm:{[n] sqrt(-2*log(1.0-?n)) * cos(2*acos(-1)*?n)}
// Asian call: s0=spot, k=strike, r=rate, sig=vol, t=maturity, m=steps, n=paths
// Arithmetic average of m in-period prices forms the payoff.
mcAsian:{[s0;k;r;sig;t;m;n] exp(-r*t)*((n {[acc] acc+(0|(mean(s0*exp(sums ((r-0.5*sig*sig)*(t%m))+(sig*sqrt(t%m))*bm m)))-k)}/ 0.0)%n)}
mcAsian 100 100 0.05 0.2 1.0 12 10000
// → ~6.13 (arithmetic-average Asian call; less than European ~10.45 because averaging dampens volatility)
Notes on mcAsian:
sums(alias for+\scan) builds cumulative log-returns;expthen converts to price levels. Usesumsrather than+\varname— the backslash is intercepted by the REPL tokenizer when immediately followed by an identifier.n f/ seedappliesfexactly n times toseed, returning the final accumulated value. Each call generates one independent path; intermediate pool allocations are reused across iterations.- The anonymous inner lambda
{[acc] acc+…}closes overs0,k,sig,r,t,m, andbmfrom the outer scope. - Increasing
m(monitoring steps) improves accuracy but increases pool usage; keepn·m ≲ 120 000to stay within the 4 M value-pool limit.