Language Quick Reference¶
A quick tour of Kaappi's Scheme dialect with runnable examples. For detailed procedure documentation, see the Procedure Reference. There is also a printable two-page A4 cheatsheet covering the language plus CLI, REPL, and thottam essentials.
Most examples below use procedures from (scheme base). In a file you
need to import it explicitly; the REPL imports it automatically. Some
examples also use (srfi 1) for filter and fold.
Numbers¶
Kaappi supports fixnums (63-bit integers), bignums (arbitrary precision),
exact rationals, flonums (IEEE 754 f64), and complex numbers. When a
fixnum operation would overflow 63 bits, the result is promoted to a
bignum automatically. Exact division produces rationals, not floats —
use inexact when you need a float.
(+ 1 2 3) ;=> 6
(* 2.5 4) ;=> 10.0
(expt 2 100) ;=> 1267650600228229401496703205376
(/ 1 3) ;=> 1/3
(inexact (/ 1 3)) ;=> 0.3333333333333333
(+ 1/3 1/6) ;=> 1/2
(sqrt -1) ;=> +i
(make-rectangular 3 4) ;=> 3+4i
Strings¶
Strings are UTF-8 encoded and indexed by codepoint position.
(string-length "hello") ;=> 5
(string-ref "hello" 1) ;=> #\e
(substring "hello" 1 4) ;=> "ell"
(string-append "foo" "bar") ;=> "foobar"
(string-upcase "hello") ;=> "HELLO"
(string-length "héllo") ;=> 5
(string-ref "lambda: λ" 8) ;=> #\λ
Raw string literals (#"X"..."X", SRFI 267) interpret no escape
sequences — backslashes and newlines are taken verbatim. The X is a
per-literal delimiter, possibly empty, chosen so the content can contain
double quotes:
#""C:\Users\me"" ;=> "C:\\Users\\me" — backslashes stay literal
(string-length #""x\ny"") ;=> 4 — backslash + n, not a newline
#"end"he said "hi""end" ;=> "he said \"hi\""
The syntax is built into the reader, so no import is needed;
(import (srfi 267)) adds port procedures such as read-raw-string,
write-raw-string, and generate-delimiter. See
SRFI Support.
Lists¶
See Pairs and Lists, SRFI-1.
(cons 1 '(2 3)) ;=> (1 2 3)
(car '(a b c)) ;=> a
(cdr '(a b c)) ;=> (b c)
(list 1 2 3) ;=> (1 2 3)
(map (lambda (x) (* x x)) '(1 2 3)) ;=> (1 4 9)
(filter odd? '(1 2 3 4 5)) ;=> (1 3 5)
(fold + 0 '(1 2 3 4 5)) ;=> 15
Vectors¶
;; literal vectors like #(10 20 30) are immutable — build with
;; vector (or vector-copy a literal) when you need vector-set!
(define v (vector 10 20 30))
(vector-ref v 1) ;=> 20
(vector-set! v 0 99)
(vector-map + #(1 2 3) #(10 20 30)) ;=> #(11 22 33)
Booleans, Characters, Symbols¶
(and #t #f) ;=> #f
(or #f 42) ;=> 42
(char-alphabetic? #\A) ;=> #t
(char-upcase #\a) ;=> #\A
(symbol? 'hello) ;=> #t
(eq? 'abc 'abc) ;=> #t
Bytevectors¶
(define bv #u8(10 20 30))
(bytevector-u8-ref bv 0) ;=> 10
(bytevector-length bv) ;=> 3
(utf8->string #u8(104 101 108 108 111)) ;=> "hello"
Definitions and Functions¶
(define x 42)
(define (add a b) (+ a b))
(add x 8) ;=> 50
(define greet
(lambda (name)
(string-append "Hello, " name "!")))
(greet "World") ;=> "Hello, World!"
Conditionals¶
(if (> 3 2) "yes" "no") ;=> "yes"
(cond
((< x 0) "negative")
((= x 0) "zero")
(else "positive")) ;=> "positive"
(case (+ 1 1)
((1) "one")
((2) "two")
(else "other")) ;=> "two"
Binding Forms¶
(let ((x 1) (y 2)) (+ x y)) ;=> 3
(let* ((x 1) (y (+ x 1))) (+ x y)) ;=> 3
(letrec ((even? (lambda (n)
(if (= n 0) #t (odd? (- n 1)))))
(odd? (lambda (n)
(if (= n 0) #f (even? (- n 1))))))
(even? 10)) ;=> #t
;; Named let (loop)
(let loop ((n 5) (acc 1))
(if (= n 0) acc
(loop (- n 1) (* n acc)))) ;=> 120
;; do
(do ((i 0 (+ i 1))
(sum 0 (+ sum i)))
((= i 5) sum)) ;=> 10
Tail Calls¶
Tail calls are optimized — a call in tail position reuses the current stack frame. Write loops as recursive calls without worrying about stack overflow:
(define (countdown n)
(if (zero? n)
'done
(countdown (- n 1)))) ;; tail call: constant stack space
(countdown 10000000) ;=> done
Macros¶
(define-syntax my-when
(syntax-rules ()
((my-when test body ...)
(if test (begin body ...)))))
(my-when (> 3 2)
(display "yes")
(newline))
;; prints: yes
Exceptions¶
(guard (exn
((string? (error-object-message exn))
(display "Caught: ")
(display (error-object-message exn))
(newline)))
(error "something went wrong" 42))
;; prints: Caught: something went wrong
(with-exception-handler
(lambda (e) (display "Error!\n") 'recovered)
(lambda () (raise-continuable "boom")))
;=> recovered — after printing "Error!"
With plain raise the exception is non-continuable: if the handler
returns, a secondary "handler returned" error is signaled. Use
raise-continuable when the handler is meant to supply a replacement
value, and guard (above) to catch-and-handle plain raise.
Continuations¶
See Control Flow.
;; Escape continuation (non-local exit)
(call/cc (lambda (exit)
(for-each (lambda (x)
(when (negative? x) (exit x)))
'(1 2 -3 4))
'all-positive))
;=> -3
For simple non-local exits like the one above, prefer call/ec — it
captures a one-shot escape continuation with no stack snapshot, much
cheaper than call/cc, which copies all registers and frames.
Parameters¶
(define my-param (make-parameter 10))
(my-param) ;=> 10
(parameterize ((my-param 42))
(my-param)) ;=> 42
(my-param) ;=> 10
Lazy Evaluation¶
(define p (delay (begin (display "computed!\n") 42)))
(force p) ;; prints "computed!" then returns 42
(force p) ;; returns 42 (cached, no recomputation)
Records¶
(define-record-type <point>
(make-point x y)
point?
(x point-x)
(y point-y set-point-y!))
(define p (make-point 3 4))
(point-x p) ;=> 3
(set-point-y! p 10)
(point-y p) ;=> 10
Multiple Values¶
(call-with-values
(lambda () (values 1 2 3))
(lambda (a b c) (+ a b c))) ;=> 6
(let-values (((a b) (values 1 2)))
(+ a b)) ;=> 3
Next: Libraries