Language guide
A tour with a small example at every step. The REPL, scripts, and packages load
library forms such as lambda and let automatically.
1. Values & quotes
Numbers, booleans, strings, and #inert are self-evaluating. Symbols look up bindings. Quote freezes a tree.
Literals
42
#t
"hello"
#inert
'foo
'(1 2 3)
2. define & if
define and if are primitive operatives: they control evaluation of their operands.
Bind and branch
(define answer 42)
(if (< answer 100)
'small
'huge)
3. Lists & vectors
Pairs use cons/car/cdr. Improper lists write (a & b). Vectors use […] or vector.
Structure
(cons 1 (cons 2 ()))
(car '(a b c))
(define v (vector 10 20 30))
(vector-ref v 1)
(vector-set! v 1 99)
(vector-ref v 1)
4. vau & lambda
vau builds operatives. lambda (stdlib) wraps a vau so arguments
evaluate first — the usual function calling convention.
Operative vs applicative
(define raw (vau (x) _ x))
(raw (+ 1 2)) ; ⇒ (+ 1 2)
(define add (lambda (x y) (+ x y)))
(add 3 4) ; ⇒ 7
((λ (x) (* x x)) 8) ; ⇒ 64
eval in the caller’s environment
(define force-it (vau (x) e (eval e x)))
(define n 21)
(force-it (+ n n))
; ⇒ 42
5. Control forms from the library
cond, and?, and or? are operatives — they short-circuit safely.
Short-circuit
(and? #f (/ 1 0)) ; ⇒ #f (no division)
(or? #t (/ 1 0)) ; ⇒ #t
(cond
((<= 1 0) 'nope)
((eqv? 1 1) 'yes))
6. The let family
let / let* / letrec
(let ((x 2) (y 3)) (* x y))
; ⇒ 6
(let* ((x 3) (y x)) (+ x y))
; ⇒ 6
(letrec ((sum (lambda (n)
(if (zero? n) 0 (+ n (sum (- n 1)))))))
(sum 5))
; ⇒ 15
7. First-class environments
Bundle bindings, evaluate remotely, or import names into the current environment.
bindings→environment & remote-eval
(define e (bindings->environment (x 10) (y 20)))
(remote-eval x e)
; ⇒ 10
(import! e x)
x
; ⇒ 10
8. Continuations
Full call/cc and delimited shift/reset are native.
Escape with call/cc
(call/cc (lambda (k) (* 5 (k 4))))
; ⇒ 4
Generator-style yield
(defn (yield x)
(shift (lambda (k) (cons x (k (#inert))))))
(reset (begin (yield 1) (yield 2) (yield 3) ()))
; ⇒ (1 2 3)
9. .NET interop
new, . (methods), .get / .set (fields & properties).
Static members use a type name atom; instance members use an object value.
CLR objects
(define id (. System.Guid NewGuid))
(. System.Console WriteLine
(. System.String Format "id={0}" id))
(define path
(. System.IO.Path Combine
(. System.IO.Path GetTempPath)
"hello.txt"))
(. System.IO.File WriteAllText path "IronKernel")
(. System.IO.File ReadAllText path)
10. Invent your own syntax
This is Kernel’s punchline: grow the language with vau.
trace — print source, then run it
(define trace
(vau (exp) env
(begin
(show exp)
(. System.Console WriteLine "")
(eval env exp))))
(trace (+ 2 2))
; prints: (+ 2 2)
; ⇒ 4
timed — no thunks required
(define timed
(vau (label exp) env
(let* ((start (.get System.DateTime Now))
(result (eval env exp))
(ms (.get (- (.get System.DateTime Now) start)
TotalMilliseconds)))
(printf "[{0}] {1} ms\n" label ms)
result)))
(timed "work" (letrec ((f (lambda (n)
(if (zero? n) 0 (+ n (f (- n 1)))))))
(f 200)))
See the full sample in
Examples/vau-dotnet.scm,
then keep operators.html open as a reference.