Tips¶
REPL Workflow¶
- The REPL has built-in tools. Type
,helpto see commands for timing (,time), profiling (,profile), macro expansion (,expand), binding exploration (,env), and GC stats (,gc). See Advanced Features for the full reference.
Performance¶
- Prefer vectors over lists for random access.
vector-refis O(1);list-refis O(n) because it walks the chain of pairs. Use lists for sequential processing, vectors when you need indexing:
;; Fast: O(1) lookup
(define v (vector 10 20 30 40 50))
(vector-ref v 3) ;=> 40
;; Slow: O(n) traversal
(define ls (list 10 20 30 40 50))
(list-ref ls 3) ;=> 40
- JIT compiles hot functions after 100 calls. Kaappi's JIT compiler
targets ARM64 and inlines fixnum arithmetic, comparisons,
car/cdr, andcons. Functions called fewer than 100 times stay interpreted. Warm up critical paths before benchmarking:
;; Warm up the JIT before measuring
(fib 10) ;; triggers JIT compilation of fib
(let ((start (current-jiffy)))
(fib 35)
(/ (- (current-jiffy) start)
(jiffies-per-second))) ;=> ~1.9
- Use
for-eachinstead ofmapwhen you don't need results.mapallocates a new list for its return value;for-eachjust applies the procedure for side effects:
;; Allocates a list you throw away -- wasteful
(map display '(1 2 3))
;; No allocation
(for-each display '(1 2 3))
- Hash tables are O(1) for lookup. Kaappi uses open-addressing with
linear probing. For key-value associations that grow beyond a handful of
entries,
hash-table-refis much faster thanassocon an alist:
(import (srfi 69))
(define ht (alist->hash-table '((a . 1) (b . 2) (c . 3))))
(hash-table-ref ht 'b) ;=> 2
Language Essentials¶
- Tail calls are optimized. Write loops as recursive calls without worrying about stack overflow:
- Bignum arithmetic is automatic. When a fixnum operation would overflow 63 bits, the result is promoted to a bignum:
- Unicode works everywhere. String indexing, character predicates, and case conversion all operate on Unicode codepoints:
- Exact division produces rationals, not floats.
(/ 1 3)gives1/3, not0.333.... Useinexactto convert when you need a float:
Common Pitfalls¶
eq?doesn't compare numbers reliably. Use=for numeric equality,eqv?for numbers and characters,equal?for deep structural comparison.eq?tests pointer identity -- two equal numbers may not beeq?:
(eq? 42 42) ;=> #t (fixnums are immediate)
(eq? 3.14 3.14) ;=> #f (flonums are heap-allocated)
(= 3.14 3.14) ;=> #t (use = for numbers)
set!changes the binding, not the value. If you pass a variable to a function andset!it inside, the caller's variable is unaffected:
(define x 10)
(define (change! v) (set! v 99))
(change! x)
x ;=> 10 (unchanged -- set! modified the local binding v)
string-set!requires mutable strings. String literals are immutable. Usestring-copyto get a mutable copy first:
mapandfor-eachstop at the shortest list. When given multiple lists of different lengths, extra elements are silently ignored:
beginin a definition context defines, not sequences. At the top level or in a library body,beginsplices definitions rather than sequencing expressions:
Error Handling and Safety¶
- Use
guardfor structured error handling. It combines exception catching with pattern matching:
- Use
call/ecinstead ofcall/ccfor non-local exits.call/eccaptures a one-shot escape continuation with no stack snapshot -- much cheaper thancall/cc, which copies all registers and frames:
- Use
write-sharedfor circular structures.writewill hang on circular lists or vectors.write-sharedprints them with datum labels:
- Sandbox mode blocks dangerous operations. The
--sandboxflag disables FFI, file I/O,eval,load, and environment variable access -- useful for running untrusted code safely.
Next: Troubleshooting