This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Cant is a message-oriented language descended from E and Scheme. Implementation runs on Chez Scheme. The core idea: almost everything is sending messages to objects.
Requires Chez Scheme installed.
./incant # REPL
./incant file.cant # Run file
./incant -i file.cant # Run file, then REPL
./run-tests # All tests
./t quick # Single test (test/test-quick.cant)
./bless-tests # Update expected outputFrom the listener:
(load "examples/automata/game-of-life.cant") ;; load and run file
25.sqrt ;; NOT (sqrt 25)
(-5).magnitude ;; NOT (abs -5) -- note: .magnitude not .abs
3.14.floor ;; NOT (floor 3.14)
(2 .** 10) ;; exponentiation, => 1024
foo.count ;; OK - sugar for (foo .count)
(foo bar).count ;; FAILS - expression, not identifier
((foo bar) .count) ;; OK - explicit parens needed
(sort xs).count ;; FAILS
((sort xs) .count) ;; OK
This is a frequent source of errors. When in doubt, use explicit (expr .method) form.
Can't shadow: sum, count, each, keep, fold, max, min, sort-by, flexarray<-, etc. Error: "An interactive setting may not shadow its parent"
(let t {point 3 4})
t.tag ;; => point
t.parts ;; => (3 4)
(t .part 0) ;; => 3
~w = write, ~d = display. Use ~~ for literal tilde. This matters when documenting ~.method syntax.
;; Complex numbers - no complex<- constructor
{complex 3 4} ;; just use the term directly
(c+ {complex 3 4} {complex 1 1}) ;; functions operate on terms
;; Ratios similarly
{ratio 1 2}
;; Lists (immutable)
'(1 2 3) xs.count xs.first xs.rest (xs 2)
(link 0 xs) ;; cons
(chain xs ys) ;; append
(xs .from 2) ;; drop 2
(xs .from 1 4) ;; slice [1,4)
;; Arrays (mutable, fixed size)
(array<- 'a 'b 'c) (arr 0) (arr .set! 1 'x)
;; Maps
(map<- {~ 'a 1} {~ 'b 2}) ;; immutable
(!map<-) ;; mutable empty
(m .set! key val) (m .get key) (m .get key default)
m.keys m.values m.items (m .maps? key)
;; Sets (mutable despite no !)
(set<- 1 2 3) (s .add! x) (s .maps? x)
(each f xs) ;; map
(each! f xs) ;; map for side effects
(keep pred xs) ;; filter
(fold f xs init) ;; right fold
(amass f init xs) ;; left fold
(some pred xs) ;; any match?
(every pred xs) ;; all match?
;; for sugar - rearranges arguments
(for each [(x xs)] (* x x)) ;; = (each (on (x) (* x x)) xs)
(for keep [(x xs)] x.even?)
(for amass [(acc 0) (x xs)] (+ acc x))
(0 .till 5) ;; 0,1,2,3,4 (lazy)
(1 .thru 5) ;; 1,2,3,4,5
(0 .till 10 2) ;; 0,2,4,6,8 (stride)
(5 .down-thru 1) ;; 5,4,3,2,1
n.sqrt n.magnitude n.floor n.ceiling n.round
n.even? n.odd? n.positive? n.negative?
(n .** k) ;; exponent
(n .quotient d) (n .modulo d) (n .remainder d)
n.+1 n.-1 ;; increment/decrement
s.count s.first s.rest (s n) (s .from i j)
(chain s1 s2)
("~d + ~d = ~w" .format a b (+ a b))
s.uppercase s.lowercase s.trim s.capitalize
(s .split ",") ;; split on separator
(sep .join list) ;; join with separator
(may value
(be {point x y} (use x and y...))
(be {empty} ...)
(be (? number?) ...) ;; predicate guard
(be `(,a ,b) ...) ;; quasiquote pattern
(else ...))
(make name
(to ~.method-no-args body)
(to (~ .method arg) body)
(to (~ regular-call arg) body)
(to message (delegate ...))) ;; catch-all
;; With trait
(make name {extend some-trait}
(to ~.method body))
;; Mutable state via boxes
(let x (box<- 0))
x.^ ;; read
(x .^= 5) ;; write
x.+1! ;; increment, returns new value
(let m (use 'library-name)) ;; from library/
(let m (use "relative/path")) ;; .cant extension added
(import m foo bar baz) ;; bind names locally
(export foo bar baz) ;; at end of module
| Type | Definition file |
|---|---|
| Numbers | abcs/00-primordia/types/number.cant |
| Texts/Strings | abcs/00-primordia/types/text.cant |
| Lists | abcs/00-primordia/types/link.cant + list-trait.cant |
| Arrays | abcs/00-primordia/types/array.cant + array-trait.cant |
| Maps | abcs/00-primordia/types/map.cant + map-trait.cant |
| Terms | abcs/00-primordia/types/term.cant |
| Boxes | abcs/00-primordia/types/box.cant |
| Symbols | abcs/00-primordia/types/symbol.cant |
| I/O | abcs/00-primordia/types/sink.cant, source.cant |
Global functions: abcs/30-functions.cant
player/ Scheme interpreter core
player.scm Main interpreter
parse.scm Syntactic sugar / desugaring
read.scm Reader
abcs/ Bootstrap (loaded in numbered order)
00-primordia/ Type behaviors and traits
types/ Primitive type scripts
20-cant.cant Metaobject (parse, eval)
30-functions.cant Standard library functions
50-top.cant REPL, error handling
library/ Optional modules (use 'name)
examples/ Example programs
test/ Tests (test/test-*.cant)
When you call (obj .method arg), this sends message {.method arg} (a term) to obj. Dispatch depends on the receiver type:
- Primitives (numbers, strings, lists, etc.): Dispatch is in Scheme (
player/player.scm), delegating to Cant scripts inabcs/00-primordia/types/for most behavior - Objects (created via
make): Match message against the object's clauses - Callable values (functions, terms): Apply with the arguments
ejector-protectcrashes when an ejector fires through it (marked "TODO untested" inplayer/player.scm:218)
scratch/REFERENCE.md- Comprehensive method reference for all typesscratch/NOTES.md- Design observations, pitfalls, ideasscratch/*.cant- Working examples demonstrating various featuresguide.md- Original language guide (long, detailed)
plug-ins/cant-mode.el for syntax highlighting.