Skip to content

Advanced Features

Concurrency

Kaappi offers two concurrency models: green threads (fibers) for cooperative multitasking and OS threads (SRFI-18) for true parallelism.

Green threads (fibers)

Fibers run cooperatively within a single OS thread. They are lightweight and communicate via channels:

(import (kaappi fibers))

(define ch (make-channel))

(spawn (lambda ()
  (channel-send ch "hello from fiber")))

(display (channel-receive ch))  ;=> hello from fiber

Use fibers for concurrent I/O, event loops, and cooperative task scheduling. See the Fibers reference for the full API.

OS threads (SRFI-18)

Real OS threads via pthread_create. Each thread gets its own VM and GC with an independent heap. Requires the --experimental-threads flag:

kaappi --experimental-threads program.scm
(import (srfi 18))

(define t (thread-start!
  (make-thread
    (lambda ()
      (* 6 7)))))

(thread-join! t)  ;=> 42

Values are deep-copied when crossing thread boundaries:

  • At thread-start!: the thunk closure is deep-copied from parent to child
  • At thread-join!: the result is deep-copied from child to parent

Threads cannot share mutable heap state directly -- use return values or channels to communicate. See the SRFI-18 reference for mutexes, condition variables, and the full threading API.

FFI (Foreign Function Interface)

Call C library functions directly from Scheme:

(import (kaappi ffi))

;; Open a shared library
(define libm (ffi-open "libm.dylib"))  ;; macOS
;; (define libm (ffi-open "libm.so.6"))  ;; Linux

;; Bind a C function: (ffi-fn lib "name" (param-types ...) return-type)
(define c-sqrt (ffi-fn libm "sqrt" '(double) 'double))
(define c-pow  (ffi-fn libm "pow"  '(double double) 'double))

(c-sqrt 2.0)     ;=> 1.4142135623730951
(c-pow 2.0 10.0) ;=> 1024.0

;; Clean up
(ffi-close libm)

Supported C types: int, long, double, float, string, pointer, void, bool, uint8, int8, int16, int32, int64, uint16, uint32, uint64, size_t, char.

FFI callbacks — pass Scheme procedures to C functions that expect function pointers:

(define cb (ffi-callback (lambda (a b) (- a b)) '(pointer pointer) 'int))
;; Pass cb to a C function like qsort
(ffi-callback-release cb)  ;; free when done

REPL Commands

The REPL supports meta-commands prefixed with , (comma). Type ,help to see the full list:

Command Description
,time <expr> Measure execution time of an expression
,profile <expr> Profile timing, call counts, and allocations
,expand <expr> Show the result of macro expansion
,env [prefix] List global bindings (optional prefix filter)
,gc Show garbage collector statistics
,break <name> Set a breakpoint on a function (see Debugger below)
,help Show all available commands
kaappi> ,time (fib 30)
fib(30): 0.173s
kaappi> ,env string-
string-append string-copy string-length ...

Bytecode Caching

Kaappi automatically caches compiled bytecode to .sbc files next to the source. On subsequent runs, if the source hasn't changed, the cached bytecode is loaded directly -- skipping the reader, expander, and compiler stages.

# Explicitly compile to bytecode
kaappi --compile program.scm
# Output: Compiled program.scm -> program.sbc

# Subsequent runs use the cache automatically
kaappi program.scm

Debugger

The REPL includes a built-in stepping debugger.

Setting breakpoints:

kaappi> ,break factorial
Breakpoint set on factorial

Running with breakpoints:

When a breakpoint is hit, the debugger pauses and shows a debug> prompt:

kaappi> (factorial 5)
Break at factorial (<repl>:1)
debug>

Debugger commands:

Command Short Action
step s Step into the next expression
next n Step over (stay in current frame)
continue c Continue to next breakpoint
locals l Show local variable bindings
backtrace bt Print the call stack
quit q Exit the debugger

Other REPL debug commands:

,break name        -- Set a breakpoint on a function
,breakpoints       -- List all breakpoints
,delete all        -- Remove all breakpoints
,step (expr)       -- Step through an expression from the start

Next: CLI Reference