sigildocs

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         ; character

Numbers

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)
1

For floating-point:

> (+ 1.5 2.5)
4.0
> (/ 10.0 4)
2.5
> (sqrt 2)
1.41421

Comparison:

> (< 1 2)
#t
> (> 5 3)
#t
> (= 42 42)
#t
> (<= 3 3)
#t

Strings

Strings are sequences of characters:

> "Hello, World!"
"Hello, World!"
> (string-length "Sigil")
5
> (string-append "Hello, " "World!")
"Hello, World!"
> (string-ref "Hello" 0)
#\H

The 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)
#t

Only #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)
#t

The 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)
30

Variables can be reassigned with set!:

> (define counter 0)
> counter
0
> (set! counter (+ counter 1))
> counter
1

The ! in set! is a naming convention indicating mutation.

Procedures

Define procedures with define:

(define (add a b)
  (+ a b))

(add 3 4)  ; => 7

This 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: 25

Local Variables with let

Use let to create local bindings:

(define (circle-area radius)
  (let ((pi 3.14159))
    (* pi radius radius)))

(circle-area 5)  ; => 78.53975

let* allows sequential bindings where later ones can reference earlier:

(define (example)
  (let* ((x 1)
         (y (+ x 1))
         (z (+ x y)))
    z))

(example)  ; => 3

Variadic 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)            ; => 0

Or 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! = 3628800

Practice Exercises

  1. Write a procedure (celsius->fahrenheit c) that converts Celsius to Fahrenheit.
  2. Write a procedure (abs n) that returns the absolute value of n.
  3. 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.

Next: Data Types →