(sigil core)
(sigil core) - Core Language Primitives
The foundation of Sigil. This module is automatically imported into every module and provides essential procedures, macros, and syntax forms.
List Operations
(append '(1 2) '(3 4)) ; => (1 2 3 4)
(reverse '(1 2 3)) ; => (3 2 1)
(filter even? '(1 2 3 4 5)) ; => (2 4)
(map (lambda (x) (* x 2)) '(1 2 3)) ; => (2 4 6)
(fold + 0 '(1 2 3 4)) ; => 10Control Flow
(cond
((< x 0) "negative")
((= x 0) "zero")
(else "positive"))
(case color
((red green blue) "primary")
((cyan magenta yellow) "secondary")
(else "other"))
(when (> x 0) (do-something))
(unless (null? lst) (process lst))Records
(define-record <point> (x) (y))
(define p (point 10 20))
(point-x p) ; => 10
(point? p) ; => #tException Handling
(error "Something went wrong: ~a" details)
(raise (make-exception 'not-found "Resource missing" '()))Keyword Arguments
(define (greet name (keys: (greeting "Hello")))
(format "~a, ~a!" greeting name))
(greet "World") ; => "Hello, World!"
(greet "World" greeting: "Hi") ; => "Hi, World!"This module uses bootstrap primitives (%make-module, %module-export!) instead of define-library for internal bootstrapping reasons.
Exports
zero?procedureCheck if a number is zero.
(zero? 0) ; => #t
(zero? 1) ; => #f
(zero? -1) ; => #f
(zero? 0.0) ; => #tpositive?procedureCheck if a number is positive (greater than zero).
(positive? 5) ; => #t
(positive? 0) ; => #f
(positive? -3) ; => #f
(positive? 0.1) ; => #tnegative?procedureCheck if a number is negative (less than zero).
(negative? -5) ; => #t
(negative? 0) ; => #f
(negative? 3) ; => #f
(negative? -0.1) ; => #tappendprocedureConcatenate any number of lists into one.
Returns a new list with elements from all lists in order. The last list is shared (not copied).
(append '(1 2) '(3 4)) ; => (1 2 3 4)
(append '(a) '(b) '(c d)) ; => (a b c d)
(append '(1 2) '()) ; => (1 2)reverseprocedureReverse the order of elements in a list.
Returns a new list with elements in reverse order.
(reverse '(1 2 3)) ; => (3 2 1)
(reverse '(a b c d)) ; => (d c b a)
(reverse '()) ; => ()list-length-internalprocedurelist-length-internal - Tail-recursive length
%syntax-has-local-binding?procedureCheck if a syntax object has local-binding metadata in its substs. Used for free-identifier=? comparison in literal matching.
%register-macro-captures!procedureRegister captured locals for a macro instance. Returns the capture-id to use for retrieval.
%macro-captureprocedureRetrieve a captured value at runtime. capture-id is the unique ID for the macro instance. sym is the symbol name of the captured local.
make-syntax-rules-transformerproceduresyntax-rules transformer generator
In syntax-rules, the first element of each pattern is the macro keyword. It should be treated as a literal (matched exactly, not bound as a variable).
captured-locals is an alist of (symbol . value) for free identifiers that were local bindings at macro definition time. This enables proper hygiene for macros defined in local scopes.
letrecsyntaxletrec - recursive let bindings (bootstrap version) (letrec ((var init) ...) body ...) => ((lambda (var ...) (set! var init) ... body ...) #f ...)
letsyntaxlet - local binding (bootstrap version) (let ((var val) ...) body ...) => ((lambda (var ...) body ...) val ...) (let name ((var val) ...) body ...) => (letrec ((name (lambda (var ...) body ...))) (name val ...))
let*syntaxlet - sequential binding (bootstrap version) (let () body ...) => (let () body ...) ; Use let for proper scoping (let ((v e) rest ...) body ...) => (let ((v e)) (let (rest ...) body ...))
condsyntaxcond - multi-way conditional (bootstrap version) Only supports basic (test body ...) and (else body ...) clauses Arrow syntax (=>) not supported in bootstrap version
andsyntaxand - short-circuit logical and (bootstrap version)
orsyntaxor - short-circuit logical or (bootstrap version)
caarprocedureGet the car of the car: (car (car x)).
(caar '((1 2) 3)) ; => 1cadrprocedureGet the car of the cdr (second element): (car (cdr x)).
(cadr '(1 2 3)) ; => 2cdarprocedureGet the cdr of the car: (cdr (car x)).
(cdar '((1 2 3) 4)) ; => (2 3)cddrprocedureGet the cdr of the cdr (tail after second): (cdr (cdr x)).
(cddr '(1 2 3 4)) ; => (3 4)caadrprocedureGet (car (car (cdr x))).
cadarprocedureGet (car (cdr (car x))) - useful for alist value access.
caddrprocedureGet the third element: (car (cdr (cdr x))).
(caddr '(1 2 3 4)) ; => 3cdadrprocedureGet (cdr (car (cdr x))).
cdddrprocedureGet the tail after third element: (cdr (cdr (cdr x))).
(cdddr '(1 2 3 4 5)) ; => (4 5)cadddrprocedureGet the fourth element: (car (cdr (cdr (cdr x)))).
(cadddr '(1 2 3 4 5)) ; => 4%syntax-identifier?procedureCheck if a syntax object is an identifier (syntax-wrapped symbol)
%syntax-pattern-matchprocedurePattern match on syntax objects, preserving syntax in bindings. Returns an alist of (pattern-var . syntax-value) or #f if no match. Unlike the plain %pattern-match, this keeps syntax wrappers on bindings.
%syntax-match-ellipsisprocedureMatch ellipsis patterns on syntax, building lists of bindings
%syntax-collect-pattern-varsprocedureCollect pattern variables from a pattern (for ellipsis handling) Returns plain symbols, stripping syntax wrappers from pattern variable names.
%%syntax-match-ellipsis-exprsprocedureMatch multiple expressions against a sub-pattern, accumulating bindings
%syntax-pattern-match-restprocedureContinue pattern matching after ellipsis
%syntax-template-instantiateprocedureInstantiate a template with syntax bindings. This is the core of hygienic template construction.
- Pattern variables are substituted with their bound syntax values
- Introduced bindings are renamed with gensyms for hygiene
- Free identifiers with capture-id emit (%macro-capture capture-id 'sym) forms
- Other free identifiers are wrapped with definition-site module name
%syntax-expand-ellipsisprocedureExpand an ellipsis template
%%syntax-expand-ellipsis-hygienicprocedureHygienic version of ellipsis expansion that uses renames and capture-id
%generate-pattern-var-bindingsprocedureGenerate let-bindings for pattern variables from a match result Takes: pattern, literals list, and the binding alist variable name Returns: list of (var (cdr (assq 'var bindings-var))) Note: Variable names are stripped of syntax to work as proper binding names.
syntax-casesyntaxsyntax-case macro implementation (syntax-case stx-expr (literal ...) clause ...) Each clause is (pattern body) or (pattern guard body)
syntaxsyntaxsyntax macro - construct hygienic template (syntax template) where template uses pattern variables from syntax-case For now, this is a simple wrapper; full hygiene comes from the expander marks
with-syntaxsyntaxwith-syntax - bind pattern variables for use with syntax templates (with-syntax ((pattern expr) ...) body ...) This merges new bindings with existing %match bindings
letsyntaxBind variables to values in a local scope.
Basic form binds variables in parallel (all values evaluated before binding):
(let ((x 1) (y 2)) (+ x y)) ; => 3Named let creates a local recursive procedure for iteration:
(let loop ((n 5) (acc 1))
(if (= n 0) acc (loop (- n 1) (* acc n)))) ; => 120let*syntaxBind variables sequentially, each binding visible to subsequent ones.
Unlike let, bindings are evaluated and bound one at a time:
(let* ((x 1) (y (+ x 1))) y) ; => 2
(let* ((a 1) (b (* a 2)) (c (* b 3))) c) ; => 6let-valuessyntaxlet-values - bind multiple values from producers R7RS: (let-values (((var ...) producer) ...) body ...)
let*-valuessyntaxlet-values - sequential let-values (same as let-values semantically) R7RS: (let-values (((var ...) producer) ...) body ...)
define-valuessyntaxdefine-values - define multiple variables from a values producer R7RS: (define-values (var ...) producer) Implementation: Uses a helper that captures values in a list, then extracts each value. This avoids the complexity of generating fresh temporary names in syntax-rules.
parameterizesyntaxparameterize - temporarily rebind parameters R7RS: (parameterize ((param value) ...) body ...) Saves old values, sets new values, executes body, restores old values. Note: This basic implementation doesn't use dynamic-wind so it won't restore on non-local exits. For most uses this is sufficient.
case-lambdasyntaxcase-lambda - procedure with multiple arities (R7RS)
Example: (define add (case-lambda (() 0) ((x) x) ((x y) (+ x y))))
Supports fixed arities and rest arguments.
condsyntaxMulti-way conditional with multiple test-expression clauses.
Each clause is (test expr ...). The first true test's expressions are evaluated. Use else for a default clause. The => syntax passes the test result to a procedure.
(define x 5)
(cond ((> x 0) "positive")
((< x 0) "negative")
(else "zero")) ; => "positive"
;; Using => to pass the matched value to a procedure
(cond ((assq 'b '((a 1) (b 2))) => cadr)
(else #f)) ; => 2casesyntaxDispatch on a key value, comparing against literal datums using eqv?.
(case (car '(b d))
((a e i o u) "vowel")
((b c d f g) "consonant")
(else "unknown")) ; => "consonant"andsyntaxShort-circuit logical AND. Returns last value if all are true, else #f.
(and 1 2 3) ; => 3
(and 1 #f 3) ; => #f
(and) ; => #t
(and (> 5 0) "yes") ; => "yes"orsyntaxShort-circuit logical OR. Returns first true value, else #f.
(or #f 2 3) ; => 2
(or #f #f) ; => #f
(or) ; => #f
(or (memq 'a '(b a c))) ; => (a c)whensyntaxEvaluate body expressions when test is true. Returns #f if test is false.
(when (> 5 0) (display "positive") 'done) ; prints "positive", => done
(when #f (display "never")) ; => #funlesssyntaxEvaluate body expressions when test is false. Returns #f if test is true.
(unless (< 5 0) (display "not negative") 'ok) ; prints "not negative", => ok
(unless #t (display "never")) ; => #fletrecsyntaxBind variables that can reference each other (mutually recursive).
Essential for defining local recursive procedures:
(letrec ((even? (lambda (n) (if (= n 0) #t (odd? (- n 1)))))
(odd? (lambda (n) (if (= n 0) #f (even? (- n 1))))))
(even? 10)) ; => #tletrec*syntaxletrec* - sequential letrec (each init can reference earlier bindings) This is what internal defines expand to. Transforms to nested let + set! pattern.
%qq-unquote?procedureThe quasiquote macro (invoked with backtick `) allows building lists with some parts evaluated and some quoted.
Examples: (a b c) => '(a b c) (a ,x c) => (list 'a x 'c) (a ,@xs c) => (append (list 'a) xs (list 'c)) (a (b ,c) d) => (list 'a (list 'b c) 'd)
Implementation approach:
- Walk the quasiquoted form
- For atoms: quote them
- For (unquote x): return x directly
- For lists containing unquote-splicing: use append
- For other lists: use cons or list Check if a form is (unquote x)
%qq-unquote-splicing?procedureCheck if a form is (unquote-splicing x)
%qq-has-splicing?procedureCheck if a list contains any unquote-splicing at the top level
%qq-expandprocedureExpand a quasiquoted expression into list-building code
%%qq-expand-listprocedureExpand a list without splicing using list
%%%qq-expand-list-with-splicingprocedureExpand a list that contains splicing using append
%qq-collect-segmentsprocedureCollect segments for append: quoted items get wrapped in (list ...), spliced items are included directly
%qq-collect-one-segmentprocedureCollect one segment: either a single splice, or multiple non-spliced items Returns (segment . remaining-list)
%qq-collect-non-splicedprocedureCollect consecutive non-spliced items Returns (items . remaining-list)
quasiquotesyntaxThe quasiquote macro - transforms the entire form
list->vectorprocedureConvert a list to a vector. (list->vector lst) -> vector
string-joinprocedurestring-join - Join a list of strings with an optional separator (string-join '("a" "b" "c") "-") => "a-b-c" (string-join '("a" "b" "c")) => "abc"
mapprocedureApply a procedure to each element of a list, returning a new list.
(map (lambda (x) (* x 2)) '(1 2 3)) ; => (2 4 6)
(map symbol->string '(a b c)) ; => ("a" "b" "c")For multi-list mapping, use list-map from (sigil list):
(import (sigil list))
(list-map + '(1 2 3) '(10 20 30)) ; => (11 22 33)anyprocedureTest if any element in a list satisfies a predicate.
Returns #t as soon as one matching element is found, #f otherwise.
(any even? '(1 3 4 5)) ; => #t
(any even? '(1 3 5 7)) ; => #f
(any null? '(() (1) (2))) ; => #teveryprocedureTest if all elements in a list satisfy a predicate.
Returns #t if the predicate is true for every element, #f otherwise.
(every number? '(1 2 3)) ; => #t
(every even? '(2 4 5)) ; => #f
(every symbol? '()) ; => #t (vacuously true)for-eachprocedureApply a procedure to each element of a list for side effects.
Like map, but doesn't collect results—used for side effects like printing.
(for-each println '("a" "b" "c")) ; prints a, b, c on separate lines
(for-each (lambda (x) (set! sum (+ sum x))) '(1 2 3))For multi-list iteration, use list-for-each from (sigil list):
(import (sigil list))
(list-for-each (lambda (a b) (println "~a" (+ a b))) '(1 2) '(10 20))filterprocedureReturn elements that satisfy a predicate.
Creates a new list containing only elements for which the predicate returns #t.
(filter even? '(1 2 3 4 5)) ; => (2 4)
(filter string? '(1 "a" 2 "b")) ; => ("a" "b")
(filter (lambda (x) (> x 0)) '(-1 0 1 2)) ; => (1 2)filter-mapprocedureMap a procedure over a list, keeping only non-#f results.
Applies proc to each element and collects non-false results. This is equivalent to (filter values (map proc lst)) but more efficient.
(filter-map (lambda (x) (if (even? x) (* x 2) #f)) '(1 2 3 4)) ; => (4 8)
(filter-map (lambda (x) (and (> x 0) x)) '(-1 2 -3 4)) ; => (2 4)partitionprocedurePartition a list into two lists based on a predicate.
Returns two values: a list of elements satisfying the predicate, and a list of elements that don't.
(partition even? '(1 2 3 4 5 6)) ; => (2 4 6) (1 3 5)
(partition positive? '(-1 2 -3 4)) ; => (2 4) (-1 -3)takeprocedureReturn the first n elements of a list.
If the list has fewer than n elements, returns the entire list.
(take '(1 2 3 4 5) 3) ; => (1 2 3)
(take '(a b) 5) ; => (a b)
(take '() 3) ; => ()skipprocedureSkip the first n elements of a list.
Returns the tail of the list after dropping n elements. If the list has fewer than n elements, returns an empty list.
(skip '(1 2 3 4 5) 2) ; => (3 4 5)
(skip '(a b) 5) ; => ()
(skip '(1 2 3) 0) ; => (1 2 3)removeprocedureRemove all occurrences of an item from a list.
Uses equal? for comparison. Returns a new list with all elements equal to item removed.
(remove 2 '(1 2 3 2 4)) ; => (1 3 4)
(remove 'a '(a b a c a)) ; => (b c)findprocedureFind the first element satisfying a predicate.
Returns the first element for which the predicate returns #t, or #f if no element matches.
(find even? '(1 3 4 5)) ; => 4
(find even? '(1 3 5)) ; => #f
(find (lambda (x) (> x 10)) '(5 15 25)) ; => 15assoc-refprocedureGet the value for a key in an alist, or a default if not found.
Uses equal? for key comparison.
(assoc-ref 'b '((a . 1) (b . 2) (c . 3))) ; => 2
(assoc-ref 'd '((a . 1) (b . 2)) 'not-found) ; => not-found
(assoc-ref 'd '((a . 1) (b . 2))) ; => #ffold-leftprocedureReduce a list from the left.
Applies (proc accumulator element) for each element, threading the result as the accumulator for the next call.
(fold-left + 0 '(1 2 3 4)) ; => 10
(fold-left cons '() '(1 2 3)) ; => ((((() . 1) . 2) . 3)
(fold-left max 0 '(3 1 4 1 5)) ; => 5fold-rightprocedureReduce a list from the right.
Applies (proc element accumulator) starting from the last element. Note: Not tail-recursive—may stack overflow on very long lists.
(fold-right cons '() '(1 2 3)) ; => (1 2 3)
(fold-right + 0 '(1 2 3 4)) ; => 10
(fold-right list '() '(a b c)) ; => (a (b (c ())))even?procedureeven? - Test if an integer is even (even? 4) => #t (even? 3) => #f
odd?procedureodd? - Test if an integer is odd (odd? 3) => #t (odd? 4) => #f
iotaprocedureiota - Generate a list of integers (iota 5) => (0 1 2 3 4) (iota 5 1) => (1 2 3 4 5) (iota 5 0 2) => (0 2 4 6 8)
make-listprocedureCreate a list of n copies of fill.
(make-list 3 'a) ; => (a a a)
(make-list 5) ; => (#f #f #f #f #f)
(make-list 0 'x) ; => ()If fill is omitted, #f is used.
make-stringprocedureCreate a string of n copies of char.
(make-string 3 #\a) ; => "aaa"
(make-string 5) ; => " "
(make-string 0 #\x) ; => ""If char is omitted, space is used.
vector->listprocedureConvert a vector to a list. (vector->list vec) -> list (vector->list vec start) -> list from start to end (vector->list vec start end) -> list from start to end
vector-for-eachprocedureApply procedure to each element of a vector for side effects. (vector-for-each display #(1 2 3)) displays 123
vector-mapprocedureApply procedure to each element, return vector of results. (vector-map (lambda (x) (* x 2)) #(1 2 3)) => #(2 4 6)
vector-filterprocedureReturn a new vector containing only elements that satisfy the predicate. (vector-filter even? #(1 2 3 4 5)) => #(2 4)
vector-foldprocedureFold a procedure over a vector from left to right. (vector-fold + 0 #(1 2 3 4 5)) => 15
vector-copyprocedureCopy a vector, optionally with start and end indices. (vector-copy vec) -> new vector copy (vector-copy vec start) -> copy from start to end (vector-copy vec start end) -> copy from start to end
vector-fill!procedureFill a vector with a value. (vector-fill! vec fill) -> fills entire vector (vector-fill! vec fill start) -> fills from start to end (vector-fill! vec fill start end) -> fills from start to end
string-for-eachprocedureApply procedure to each character of a string for side effects. (string-for-each display "abc") displays abc
string-mapprocedureApply procedure to each character, return string of results. (string-map char-upcase "abc") => "ABC"
string-copyprocedureCopy a string, optionally with start and end indices. (string-copy str) -> new string copy (string-copy str start) -> copy from start to end (string-copy str start end) -> copy from start to end
vector->stringprocedureConvert a vector of characters to a string. (vector->string #(#a #b #c)) => "abc"
string->vectorprocedureConvert a string to a vector of characters. (string->vector "abc") => #(#a #b #c) (string->vector "hello" 1 4) => #(#e #l #l)
bytevector-copyprocedureCopy a bytevector, optionally with start and end indices. (bytevector-copy bv) -> new bytevector copy (bytevector-copy bv start) -> copy from start to end (bytevector-copy bv start end) -> copy from start to end
bytevector-appendprocedureAppend multiple bytevectors. (bytevector-append #u8(1 2) #u8(3 4)) => #u8(1 2 3 4)
resetsyntaxreset - establish a delimiting prompt Usage: (reset expr ...) The body is evaluated and its result returned. If shift is called within, control transfers to the shift's handler.
The handler receives (k thunk) where:
- k is the delimited continuation
- thunk is the procedure passed by shift: (lambda (k) body ...) We call the thunk with k to execute the shift body.
shiftsyntaxshift - capture the continuation up to the nearest reset Usage: (shift k expr ...) Captures the continuation up to the enclosing reset and binds it to k. The body expressions are then evaluated with k available. The captured continuation k is composable - calling (k v) will resume the computation with v, and return to this point when that completes.
call-with-portprocedurecall-with-port - R7RS procedure for safe port handling Calls proc with port as argument, closes port when proc returns, and returns the result of proc.
Usage: (call-with-port (open-input-file "data.txt") (lambda (port) (read-line port)))
delaysyntaxdelay - create a promise that lazily evaluates an expression
Usage: (define p (delay (+ 1 2))) (force p) => 3 (force p) => 3 (memoized)
delay-forcesyntaxdelay-force - create a promise for tail-recursive lazy algorithms
Like delay, but if the expression returns a promise, that promise is iteratively forced. This enables proper tail recursion in lazy streams.
Usage: (define (lazy-range n) (delay-force (if (> n 0) (cons n (lazy-range (- n 1))) '())))
make-exceptionprocedureCreate an exception with the given kind, message, and irritants.
make-exception-with-traceprocedureCreate an exception with stack trace.
exception?procedureCheck if obj is an exception.
exception-kindprocedureGet the kind of an exception (e.g., 'error, 'type-error).
exception-messageprocedureGet the message from an exception.
exception-irritantsprocedureGet the irritants (additional data) from an exception.
exception-stack-traceprocedureGet the stack trace from an exception.
default-exception-handlerprocedureDefault handler - prints error and returns #f
current-exception-handlerprocedureGet the current exception handler.
add-trace-to-exceptionprocedureAdd stack trace to an exception (internal helper).
raiseprocedureRaise an exception (non-continuable). Aborts to the nearest exception handler.
raise-continuableprocedureRaise a continuable exception. The handler can return a value to continue execution.
errorprocedureConvenience function to raise an error exception. (error "message" irritant ...)
with-exception-handlerprocedureInstall an exception handler and execute thunk (R6RS style). Handler is a procedure taking the exception. Thunk is the body to execute.
run-with-error-handlerprocedureRun a thunk with a default error handler that prints and exits. Used by sigil-run to wrap the entry point so errors are visible. Applications can install their own handlers (like REPL does) to override.
guardsyntaxHandle exceptions with pattern-matching clauses (R7RS).
Each clause has the form (test result ...) or (else result ...). If no clause matches and there's no else, the exception is re-raised.
(guard (exn
((eq? (exception-kind exn) 'not-found) "missing")
(else (exception-message exn)))
(risky-operation))string-refprocedureReturn the character at position k in the string.
(string-ref "hello" 0) ; => #\h
(string-ref "hello" 4) ; => #\oThe index k must be a non-negative integer less than the string length. Raises an error if the index is out of bounds.
string-lengthprocedureReturn the number of characters in the string.
(string-length "hello") ; => 5
(string-length "") ; => 0
(string-length "λ") ; => 1 (counts characters, not bytes)string-appendprocedureConcatenate zero or more strings into a single string.
(string-append "hello" " " "world") ; => "hello world"
(string-append) ; => ""
(string-append "only") ; => "only"substringprocedureExtract a substring from start (inclusive) to end (exclusive).
(substring "hello" 1 4) ; => "ell"
(substring "hello" 0 5) ; => "hello"
(substring "hello" 2 2) ; => ""Both start and end must be valid indices where 0 <= start <= end <= length.
string=?procedureTest if two strings are equal.
(string=? "hello" "hello") ; => #t
(string=? "hello" "Hello") ; => #f (case-sensitive)
(string=? "" "") ; => #tchar=?procedureTest if two characters are equal.
(char=? #\a #\a) ; => #t
(char=? #\a #\A) ; => #fchar<?procedureTest if the first character is less than the second.
(char<? #\a #\b) ; => #t
(char<? #\b #\a) ; => #f
(char<? #\A #\a) ; => #t (uppercase before lowercase in Unicode)char<=?procedureTest if the first character is less than or equal to the second.
(char<=? #\a #\a) ; => #t
(char<=? #\a #\b) ; => #t
(char<=? #\b #\a) ; => #fchar>?procedureTest if the first character is greater than the second.
(char>? #\b #\a) ; => #t
(char>? #\a #\b) ; => #fchar>=?procedureTest if the first character is greater than or equal to the second.
(char>=? #\a #\a) ; => #t
(char>=? #\b #\a) ; => #t
(char>=? #\a #\b) ; => #fchar->integerprocedureConvert a character to its Unicode code point.
(char->integer #\a) ; => 97
(char->integer #\A) ; => 65
(char->integer #\λ) ; => 955
(char->integer #\space) ; => 32integer->charprocedureConvert a Unicode code point to its character.
(integer->char 97) ; => #\a
(integer->char 65) ; => #\A
(integer->char 955) ; => #\λstringprocedureCreate a string from zero or more character arguments.
(string #\h #\i) ; => "hi"
(string) ; => ""
(string #\λ) ; => "λ"carprocedureReturn the first element (car) of a pair.
(car '(1 2 3)) ; => 1
(car '(a . b)) ; => a
(car '((1 2) 3 4)) ; => (1 2)cdrprocedureReturn the rest (cdr) of a pair.
(cdr '(1 2 3)) ; => (2 3)
(cdr '(a . b)) ; => b
(cdr '(1)) ; => ()consprocedureConstruct a new pair with the given car and cdr.
(cons 1 2) ; => (1 . 2)
(cons 1 '(2 3)) ; => (1 2 3)
(cons 'a '()) ; => (a)set-car!procedureMutate the car of a pair.
(define p (cons 1 2))
(set-car! p 'a)
p ; => (a . 2)set-cdr!procedureMutate the cdr of a pair.
(define p (cons 1 2))
(set-cdr! p 'b)
p ; => (1 . b)vector-refprocedureReturn the element at position k in the vector.
(vector-ref #(a b c) 0) ; => a
(vector-ref #(a b c) 2) ; => cThe index must be a non-negative integer less than the vector length.
vector-set!procedureSet the element at position k in the vector.
(define v #(1 2 3))
(vector-set! v 1 'x)
v ; => #(1 x 3)vector-lengthprocedureReturn the number of elements in the vector.
(vector-length #(a b c)) ; => 3
(vector-length #()) ; => 0make-vectorprocedureCreate a vector of the given length, optionally filled with a value.
(make-vector 3) ; => #(#<undefined> #<undefined> #<undefined>)
(make-vector 3 'x) ; => #(x x x)
(make-vector 0) ; => #()displayprocedureDisplay a value to a port (default: current output port).
(display "hello") ; prints: hello
(display 42) ; prints: 42
(display #\newline) ; prints a newlineStrings are printed without quotes, characters without # notation.
newlineprocedureWrite a newline to a port (default: current output port).
(newline) ; prints a newline characterwriteprocedureWrite a value in machine-readable form to a port.
(write "hello") ; prints: "hello"
(write #\a) ; prints: #\a
(write '(1 2)) ; prints: (1 2)Unlike display, strings include quotes and characters include # notation.
read-exprprocedureRead an S-expression from a string.
(read-expr "(+ 1 2)") ; => (+ 1 2)
(read-expr "42") ; => 42
(read-expr "\"hello\"") ; => "hello"loadprocedureLoad and evaluate a Scheme source file.
(load "myfile.sgl")Reads and evaluates all expressions in the file sequentially.
evalprocedureEvaluate an expression in the current environment.
(eval '(+ 1 2)) ; => 3
(eval '(list 'a 'b 'c)) ; => (a b c)symbol->stringprocedureConvert a symbol to its string name.
(symbol->string 'hello) ; => "hello"
(symbol->string '+) ; => "+"string->symbolprocedureConvert a string to a symbol (interned).
(string->symbol "hello") ; => hello
(eq? (string->symbol "x") (string->symbol "x")) ; => #tnumber->stringprocedureConvert a number to its string representation.
(number->string 42) ; => "42"
(number->string 3.14) ; => "3.14"
(number->string 255 16) ; => "ff" (with radix)string->numberprocedureParse a string as a number, returning #f if invalid.
(string->number "42") ; => 42
(string->number "3.14") ; => 3.14
(string->number "ff" 16) ; => 255 (with radix)
(string->number "abc") ; => #fstring->listprocedureConvert a string to a list of characters.
(string->list "hello") ; => (#\h #\e #\l #\l #\o)
(string->list "") ; => ()list->stringprocedureConvert a list of characters to a string.
(list->string '(#\h #\i)) ; => "hi"
(list->string '()) ; => ""bytevector?procedureTest if a value is a bytevector.
(bytevector? #u8(1 2 3)) ; => #t
(bytevector? "string") ; => #fmake-bytevectorprocedureCreate a bytevector of the given size, optionally filled with a byte value.
(make-bytevector 3) ; => #u8(0 0 0)
(make-bytevector 3 42) ; => #u8(42 42 42)bytevector-u8-refprocedureReturn the byte at position k in the bytevector.
(bytevector-u8-ref #u8(10 20 30) 1) ; => 20bytevector-u8-set!procedureSet the byte at position k in the bytevector.
(define bv (make-bytevector 3 0))
(bytevector-u8-set! bv 1 255)
bv ; => #u8(0 255 0)bytevector-lengthprocedureReturn the number of bytes in the bytevector.
(bytevector-length #u8(1 2 3)) ; => 3srcloc?procedureTest if a value is a source location object.
srcloc-fileprocedureGet the file path from a source location.
srcloc-lineprocedureGet the line number (1-based) from a source location.
srcloc-columnprocedureGet the column number (0-based) from a source location.
srcloc-positionprocedureGet the byte position from a source location.
srcloc-spanprocedureGet the byte span (length) from a source location.
make-srclocprocedureCreate a source location object.
(make-srcloc "file.sgl" 10 0 100 25)Arguments: file, line, column, position, span.
capture-stack-traceprocedureCapture the current call stack as a stack trace object.
(capture-stack-trace 0) ; capture full stack
(capture-stack-trace 1) ; skip 1 framestack-trace?procedureTest if a value is a stack trace object.
stack-trace-lengthprocedureReturn the number of frames in a stack trace.
stack-trace-refprocedureGet a frame from a stack trace as a vector.
Returns #(proc-name srcloc) or #f if index is out of bounds.
foreign?procedureTest if a value is a foreign (C) object.
foreign-typeprocedureGet the type tag of a foreign object.
+procedureAdd zero or more numbers.
(+) ; => 0
(+ 1) ; => 1
(+ 1 2 3) ; => 6
(+ 1.5 2.5) ; => 4.0-procedureSubtract numbers or negate a single number.
(- 5) ; => -5 (negation)
(- 5 3) ; => 2
(- 10 3 2) ; => 5*procedureMultiply zero or more numbers.
(*) ; => 1
(* 2) ; => 2
(* 2 3 4) ; => 24/procedureDivide numbers or compute reciprocal.
(/ 2) ; => 0.5 (reciprocal)
(/ 10 2) ; => 5
(/ 24 2 3) ; => 4<procedureTest if arguments are in strictly increasing order.
(< 1 2) ; => #t
(< 1 2 3) ; => #t
(< 1 1) ; => #f>procedureTest if arguments are in strictly decreasing order.
(> 2 1) ; => #t
(> 3 2 1) ; => #t
(> 1 1) ; => #f<=procedureTest if arguments are in non-decreasing order.
(<= 1 2) ; => #t
(<= 1 1) ; => #t
(<= 2 1) ; => #f>=procedureTest if arguments are in non-increasing order.
(>= 2 1) ; => #t
(>= 1 1) ; => #t
(>= 1 2) ; => #f=procedureTest if all arguments are numerically equal.
(= 1 1) ; => #t
(= 1 1 1) ; => #t
(= 1 2) ; => #f
(= 1 1.0) ; => #tnull?procedureTest if a value is the empty list.
(null? '()) ; => #t
(null? '(1)) ; => #fpair?procedureTest if a value is a pair.
(pair? '(1 . 2)) ; => #t
(pair? '(1 2)) ; => #t
(pair? '()) ; => #fnumber?procedureTest if a value is a number.
(number? 42) ; => #t
(number? 3.14) ; => #t
(number? "42") ; => #fsymbol?procedureTest if a value is a symbol.
(symbol? 'foo) ; => #t
(symbol? "foo") ; => #fstring?procedureTest if a value is a string.
(string? "hello") ; => #t
(string? 'hello) ; => #fvector?procedureTest if a value is a vector.
(vector? #(1 2 3)) ; => #t
(vector? '(1 2 3)) ; => #fprocedure?procedureTest if a value is a procedure.
(procedure? +) ; => #t
(procedure? (lambda (x) x)) ; => #t
(procedure? 42) ; => #fboolean?procedureTest if a value is a boolean.
(boolean? #t) ; => #t
(boolean? #f) ; => #t
(boolean? 0) ; => #fchar?procedureTest if a value is a character.
(char? #\a) ; => #t
(char? "a") ; => #flist?procedureTest if a value is a proper list.
(list? '(1 2 3)) ; => #t
(list? '()) ; => #t
(list? '(1 . 2)) ; => #f (improper list)listprocedureCreate a list from the arguments.
(list 1 2 3) ; => (1 2 3)
(list) ; => ()
(list 'a 'b) ; => (a b)lengthprocedureReturn the length of a list.
(length '(1 2 3)) ; => 3
(length '()) ; => 0integer?procedureTest if a value is an exact integer.
(integer? 42) ; => #t
(integer? 3.0) ; => #f
(integer? 3.5) ; => #feof-object?procedureTest if a value is the end-of-file object.
(eof-object? (eof-object)) ; => #t
(eof-object? #f) ; => #feq?procedureTest object identity (same memory location).
(eq? 'a 'a) ; => #t
(eq? '() '()) ; => #t
(eq? "a" "a") ; => unspecified
(define x '(1 2))
(eq? x x) ; => #teqv?procedureTest equivalence (same type and value).
(eqv? 1 1) ; => #t
(eqv? 1 1.0) ; => #f
(eqv? #\a #\a) ; => #tequal?procedureTest structural equality (deep comparison).
(equal? '(1 2 3) '(1 2 3)) ; => #t
(equal? "hello" "hello") ; => #t
(equal? #(1 2) #(1 2)) ; => #tnotprocedureBoolean negation.
(not #f) ; => #t
(not #t) ; => #f
(not '()) ; => #f (only #f is false)
(not 0) ; => #fgensymprocedureGenerate a unique symbol, optionally with a prefix.
(gensym) ; => g1234 (unique each time)
(gensym "temp") ; => temp1235applyprocedureApply a procedure to a list of arguments.
(apply + '(1 2 3)) ; => 6
(apply list 'a 'b '(c d)) ; => (a b c d)valuesprocedureReturn multiple values.
(values 1 2 3) ; returns 3 values
(call-with-values (lambda () (values 1 2))
(lambda (a b) (+ a b))) ; => 3call-with-valuesprocedureCall producer with no args, pass its values to consumer.
(call-with-values
(lambda () (values 1 2))
(lambda (a b) (+ a b))) ; => 3make-parameterprocedureCreate a parameter object (dynamic variable).
(define my-param (make-parameter 10))
(my-param) ; => 10
(my-param 20) ; set to 20
(my-param) ; => 20parameter?procedureTest if a value is a parameter object.
promise?procedureTest if a value is a promise.
(promise? (delay (+ 1 2))) ; => #t
(promise? 42) ; => #fmake-promiseprocedureCreate an already-forced promise containing a value.
(force (make-promise 42)) ; => 42forceprocedureForce evaluation of a promise, caching the result.
(define p (delay (begin (display "computing...") (+ 1 2))))
(force p) ; prints "computing...", returns 3
(force p) ; returns 3 (cached, no print)eval-stringprocedureEvaluate a string as Sigil code.
(eval-string "(+ 1 2)") ; => 3eval-string-in-moduleprocedureEvaluate a string in a specific module's context.
(eval-string-in-module "(+ 1 2)" (find-module '(sigil core)))procedure-metadataprocedureGet the metadata alist of a procedure.
(procedure-metadata my-proc) ; => ((doc . "...") (name . my-proc))set-procedure-metadata!procedureSet the metadata alist of a procedure.
(set-procedure-metadata! my-proc '((custom . value)))procedure-moduleprocedureGet the module a procedure was defined in.
set-procedure-module!procedureSet the module attribution of a procedure.
procedure-arityprocedureGet the arity specification of a procedure.
(procedure-arity +) ; => (-1 . 0) (variadic, at least 0 args)procedure-nameprocedureGet the name of a procedure (symbol or #f).
(procedure-name car) ; => carmodule?procedureTest if a value is a module.
current-moduleprocedureGet the current module context.
set-current-module!procedureSet the current module context.
macroexpandprocedureExpand macros in an expression (for debugging).
(macroexpand '(when #t (display "hi")))disassembleprocedureDisassemble a procedure's bytecode (for debugging).
(disassemble my-proc)find-moduleprocedureFind a module by its name, returning #f if not loaded.
(find-module '(sigil core)) ; => #<module (sigil core)>load-moduleprocedureLoad a module by name, returning the module object.
(load-module '(sigil json)) ; => #<module (sigil json)>module-exportsprocedureGet the list of exported symbols from a module.
(module-exports (find-module '(sigil path))) ; => (path-join path-dirname ...)module-refprocedureLook up a binding in a module by name.
(module-ref (find-module '(sigil core)) 'car) ; => #<native car>module-nameprocedureGet the name of a module as a list of symbols.
(module-name (current-module)) ; => (sigil user)add-library-path!procedureAdd a directory to the library search path.
(add-library-path! "/my/modules")library-pathsprocedureReturn the list of library search paths.
(library-paths) ; => ("/usr/lib/sigil" "/home/user/lib")current-millisecondsprocedureGet the current time in milliseconds since epoch.
(current-milliseconds) ; => 1702834567890keyword?procedureTest if a value is a keyword.
(keyword? foo:) ; => #t
(keyword? 'foo) ; => #fkeyword->stringprocedureConvert a keyword to a string (without the colon).
(keyword->string foo:) ; => "foo"string->keywordprocedureConvert a string to a keyword.
(string->keyword "foo") ; => foo:keyword->symbolprocedureConvert a keyword to a symbol.
(keyword->symbol foo:) ; => foosplit-keyword-argsprocedureSplit an argument list into positional and keyword arguments Returns (positional-args . keyword-alist) where keyword-alist is ((kw . value) ...)
keyword-refprocedureGet a keyword value from a keyword alist Returns default if keyword not found
keyword-check-unknownprocedureCheck for unknown keywords and signal error if any found known-keywords is a list of keyword objects
keys-form?procedureUsage: (lambda ((keys: x (y 10))) (+ x y))
(define (add (keys: (x 0) (y 0))) (+ x y))
(add x: 1 y: 2) ; => 3 (add x: 5) ; => 5 (y defaults to 0)
The (keys: ...) form contains keyword parameter specs: name - required keyword (no default) (name default) - optional keyword with default value
When no (keys: ...) form is present, lambda/define work normally. Helper: Check if a form is (keys: ...)
opts-form?procedureHelper: Check if a form is (opts: ...)
rest-form?procedureHelper: Check if a form is (rest: ...)
special-form-param?procedureHelper: Check if a form is any special parameter form
parse-key-specsprocedureHelper: Parse keyword parameter specs from (keys: spec ...) Returns list of (name has-default default-expr keyword-obj)
parse-opt-specsprocedureHelper: Parse optional positional parameter specs from (opts: spec ...) Returns list of (name default-expr)
extract-positional-paramsprocedureHelper: Extract required positional params (everything before opts:/keys:/rest:)
extract-keys-formprocedureHelper: Find and extract the (keys: ...) form from formals
extract-opts-formprocedureHelper: Find and extract the (opts: ...) form from formals
extract-rest-formprocedureHelper: Find and extract the (rest: ...) form from formals
build-keyword-let-bindingsprocedureHelper: Build let bindings for keyword parameters parsed-specs: ((name has-default default kw-obj) ...)
build-required-bindingsprocedureHelper: Build let bindings to extract required positional params Returns (bindings . rest-var-name) where rest-var-name is the variable holding remaining args after required params are extracted
build-optional-bindingsprocedureHelper: Build let bindings for optional positional params opt-specs: ((name default) ...) Returns (bindings . rest-var-name)
lambda-transformerprocedurelambda-transformer - The core implementation for extended lambda Used by both 'lambda' and 'fn' macros. Supports extended parameter forms: (opts: (name default) ...) - optional positional parameters (keys: name (name default) ...) - keyword parameters (rest: name) - rest parameter for remaining positional args If no special forms, expands to plain %lambda.
lambdasyntaxlambda - Extended lambda with optional and keyword parameter support
Supports standard lambda syntax plus extended parameter forms: (opts: (name default) ...) - optional positional parameters (keys: name (name default) ...) - keyword parameters (rest: name) - rest parameter for remaining positional args
For simple parameter lists, expands directly to %lambda (no overhead).
;; Simple lambda (expands to %lambda directly)
(lambda (x y) (+ x y))
;; With optional parameter
(lambda (x (opts: (y 0))) (+ x y))
;; With keyword parameter
(lambda (name (keys: (greeting "Hello")))
(string-append greeting ", " name))define-transformerproceduredefine-transformer - Extended define with function shorthand and keyword support
Handles both simple definitions and function shorthand: (define x 42) => (%define x 42) (define (f x) body...) => (%define (f x) body...) (define (f x (opts: ...)) body...) => (%define f (lambda ...))
Simple function shorthand is preserved so the C compiler can apply docstrings. Extended parameter syntax (opts:, keys:, rest:) requires lambda conversion. The input form's docstring is propagated to the output for doc attachment.
chainsyntaxThread a value through a sequence of expressions.
Similar to SRFI-197's chain. Uses _ as the placeholder symbol. When a step contains _, the threaded value is substituted there. When no _ is present, the value is inserted as the first argument.
;; Thread-first (no placeholder)
(chain 5 (+ 3) (* 2))
; => 16 (same as (* (+ 5 3) 2))
;; Explicit placeholder
(chain '(1 2 3)
(map (lambda (x) (* x 2)) _)
(apply + _))
; => 12
;; Placeholder in various positions
(chain 10 (- 20 _)) ; => 10 (= 20 - 10)
(chain 5 (list 1 2 _ 4)) ; => (1 2 5 4)some->syntaxThread value through steps, short-circuiting on #f.
Like ->, but stops and returns #f if any step produces #f. Useful for optional/nullable value pipelines.
;; Returns #f if any step fails
(some-> user
(dict-ref _ name:)
(string-split " " _)
car)
;; Short-circuits on #f
(some-> #f (+ 1 _)) ; => #f (doesn't call +)
;; Continues while truthy
(some-> 5 (+ 1 _) (* 2 _)) ; => 12printsyntaxPrint a formatted string to standard output.
Uses format directives: ~a (display), ~s (write), ~% (newline). Does not add a trailing newline.
(print "Processing ~a..." filename)printlnsyntaxPrint a formatted string to standard output with a newline.
The recommended way to print output in Sigil programs.
(println "Hello, ~a!" name)
(println "Result: ~a" (* 6 7))eprintsyntaxPrint a formatted string to standard error.
Use for error messages and diagnostics.
(eprint "Warning: ~a" message)eprintlnsyntaxPrint a formatted string to standard error with a newline.
(eprintln "Error: ~a" error-message)