Chapter 3: Basics
Let's explore the fundamental building blocks of Sigil programs.
Atoms
The simplest expressions are atoms — values that can't be broken down further:
42 ; integer
3.14 ; floating-point
"hello" ; string
#t ; true
#f ; false
hello ; symbol
#\a ; characterNumbers
Sigil supports integers and floating-point numbers:
> (+ 1 2 3 4 5)
15
> (- 10 3)
7
> (* 2 3 4)
24
> (/ 10 4)
2.5
> (modulo 10 3)
1For floating-point:
> (+ 1.5 2.5)
4.0
> (/ 10.0 4)
2.5
> (sqrt 2)
1.41421Comparison:
> (< 1 2)
#t
> (> 5 3)
#t
> (= 42 42)
#t
> (<= 3 3)
#tStrings
Strings are sequences of characters:
> "Hello, World!"
"Hello, World!"
> (string-length "Sigil")
5
> (string-append "Hello, " "World!")
"Hello, World!"
> (string-ref "Hello" 0)
#\HThe str procedure is a convenient way to build strings:
> (str "The answer is " 42)
"The answer is 42"
> (str "Hello, " "World" "!")
"Hello, World!"Booleans
#t is true, #f is false:
> (not #t)
#f
> (not #f)
#t
> (and #t #t)
#t
> (or #f #t)
#tOnly #f is falsy. Everything else is truthy:
> (if 0 "yes" "no")
"yes"
> (if "" "yes" "no")
"yes"
> (if '() "yes" "no")
"yes"
> (if #f "yes" "no")
"no"Symbols
Symbols are like strings but are compared by identity:
> 'hello
hello
> (eq? 'hello 'hello)
#t
> (eq? 'hello 'world)
#f
> (symbol? 'hello)
#tThe quote ' prevents evaluation. Without it, hello would be treated as a variable name.
Variables with define
Create named values:
> (define x 10)
> (define y 20)
> (+ x y)
30Variables can be reassigned with set!:
> (define counter 0)
> counter
0
> (set! counter (+ counter 1))
> counter
1The ! in set! is a naming convention indicating mutation.
Procedures
Define procedures with define:
(define (add a b)
(+ a b))
(add 3 4) ; => 7This is shorthand for:
(define add
(lambda (a b)
(+ a b)))Multiple Expressions in a Body
A procedure body can have multiple expressions. The last one is returned:
(define (greet-and-compute name x)
(println "Hello, ~a" name)
(* x x)) ; This is returned
(greet-and-compute "Alice" 5)
; Prints: Hello, Alice
; Returns: 25Local Variables with let
Use let to create local bindings:
(define (circle-area radius)
(let ((pi 3.14159))
(* pi radius radius)))
(circle-area 5) ; => 78.53975let* allows sequential bindings where later ones can reference earlier:
(define (example)
(let* ((x 1)
(y (+ x 1))
(z (+ x y)))
z))
(example) ; => 3Variadic Procedures
Accept any number of arguments with a dotted parameter:
(define (sum . numbers)
(fold-left + 0 numbers))
(sum 1 2 3 4 5) ; => 15
(sum) ; => 0Or combine required and rest parameters:
(define (printf fmt . args)
(display (apply str fmt args)))Conditionals
if
The basic conditional:
(if (> x 0)
"positive"
"not positive")cond
Multiple conditions:
(cond
((< x 0) "negative")
((= x 0) "zero")
(else "positive"))when and unless
For one-sided conditionals:
(when (> x 0)
(println "x is positive"))
(unless (= x 0)
(println "x is not zero"))These return #f when the condition isn't met.
Putting It Together
Here's a complete program combining what we've learned:
;; Calculate factorial
(define (factorial n)
(if (<= n 1)
1
(* n (factorial (- n 1)))))
(define (print-factorial n)
(println "~a! = ~a" n (factorial n)))
(print-factorial 5) ; 5! = 120
(print-factorial 10) ; 10! = 3628800Practice Exercises
- Write a procedure
(celsius->fahrenheit c)that converts Celsius to Fahrenheit. - Write a procedure
(abs n)that returns the absolute value of n. - Write a procedure
(max a b)that returns the larger of two numbers.
What's Next
Now let's explore Sigil's data types, especially lists and records.