Beginner

Hello World in Scheme

Your first Scheme program - the classic Hello World example with Docker setup

Every programming journey starts with Hello World. Let’s write our first Scheme program and experience one of the most elegant and influential programming languages ever created.

The Code

Create a file named hello.scm:

1
2
(display "Hello, World!")
(newline)

Understanding the Code

Let’s break down this simple but foundational program:

  • (display "Hello, World!") - Outputs the string to standard output. Like all Lisp dialects, Scheme uses S-expressions where the function comes first, followed by arguments.
  • (newline) - Outputs a newline character. Scheme’s I/O primitives are minimal and composable.

Why Two Lines?

Unlike many languages where print includes a newline, Scheme’s display is more primitive - it just outputs the value. This is intentional: Scheme provides minimal building blocks that you compose into larger solutions.

You can combine them into one expression:

1
2
3
(begin
  (display "Hello, World!")
  (newline))

The begin form sequences multiple expressions and returns the value of the last one.

Running with Docker

The easiest way to run this without installing Scheme locally:

1
2
3
4
5
# Pull the Guile Scheme Docker image
docker pull weinholt/guile:latest

# Run the program
docker run --rm -v $(pwd):/app -w /app weinholt/guile:latest guile --no-auto-compile hello.scm

Running Locally

If you have GNU Guile installed:

1
2
3
4
5
# Run the script directly
guile hello.scm

# Or with explicit no-compile flag
guile --no-auto-compile hello.scm

For Chicken Scheme:

1
csi -script hello.scm

For Chez Scheme:

1
scheme --script hello.scm

Expected Output

Hello, World!

Understanding S-Expressions

Scheme uses prefix notation with parentheses, called S-expressions:

1
2
3
4
; Traditional (most languages)     ; Scheme
; add(1, 2)                         (+ 1 2)
; print("Hello")                    (display "Hello")
; max(3, 5, 7)                      (max 3 5 7)

This uniform syntax is what makes Lisp’s macro system possible - code and data share the same structure.

Alternative Approaches

Using format (GNU Guile)

GNU Guile provides a format function similar to Common Lisp:

1
(format #t "Hello, World!~%")
  • #t - Output to standard output (like t in Common Lisp)
  • ~% - Newline directive

Defining a Main Procedure

For a more structured program:

1
2
3
4
5
(define (main)
  (display "Hello, World!")
  (newline))

(main)
  • define - Creates a binding (variable or procedure)
  • (main) - Empty parameter list means no arguments
  • The procedure body is implicitly a begin block

Using write vs display

Scheme has multiple output functions:

1
2
3
(display "Hello, World!")  ; Outputs: Hello, World!
(write "Hello, World!")    ; Outputs: "Hello, World!" (with quotes)
(newline)
  • display - Human-readable output (no quotes on strings)
  • write - Machine-readable output (includes quotes, escapes)

Key Concepts

  1. S-Expressions - Code is written as nested lists: (function arg1 arg2)
  2. Prefix Notation - The operator/function always comes first
  3. Everything is an Expression - Every construct returns a value
  4. Minimal Core - Few primitives, build everything from them
  5. Immutability Preferred - Though mutation is possible

Scheme Booleans

In Scheme:

  • #t - True
  • #f - False

Unlike Common Lisp where NIL is false, only #f is false in Scheme. Even '() (empty list) is true!

1
2
3
(if #f "yes" "no")   ; Returns "no"
(if '() "yes" "no")  ; Returns "yes" - empty list is true!
(if 0 "yes" "no")    ; Returns "yes" - 0 is true!

The REPL Experience

Scheme shines in the REPL (Read-Eval-Print Loop):

$ guile
GNU Guile 3.0.9
scheme@(guile-user)> (display "Hello, World!")
Hello, World!
scheme@(guile-user)> (+ 1 2 3)
$1 = 6
scheme@(guile-user)> (define (greet name) (display name) (newline))
scheme@(guile-user)> (greet "Schemer")
Schemer

Interactive development is central to the Scheme experience.

Scheme vs Other Languages

Python:

1
print("Hello, World!")

JavaScript:

1
console.log("Hello, World!");

Common Lisp:

1
(format t "Hello, World!~%")

Scheme:

1
2
(display "Hello, World!")
(newline)

Notice how Scheme is more primitive than Common Lisp’s format but follows the same S-expression syntax.

Why display and newline?

Scheme’s philosophy is to provide minimal, orthogonal primitives:

  • display - Output a datum
  • newline - Output a newline
  • write - Output a datum in machine-readable form

You combine these to achieve what print does in other languages. This composability is central to Scheme’s design philosophy.

GNU Guile

Our Docker image uses GNU Guile, which is:

  • Full R7RS Support - Modern Scheme standard
  • Fast - JIT compilation in recent versions
  • Extensible - The official GNU extension language
  • Practical - Rich library ecosystem
  • Well-documented - Excellent manual and tutorials

Other popular implementations include Chez Scheme (fastest), Chicken (practical), and Racket (educational).

Historical Note

When Sussman and Steele created Scheme in 1975, they were studying the Actor model. Their insight that actors and closures are equivalent led to Scheme’s emphasis on lambda expressions and first-class procedures. This influenced everything from JavaScript to modern functional programming.

Next Steps

Continue exploring Scheme’s elegant features:

  • Variables and data types (numbers, symbols, pairs, lists)
  • Lambda expressions and closures
  • Recursion and tail-call optimization
  • Lists and list processing (car, cdr, cons)
  • Higher-order functions (map, filter, fold)
  • Macros and metaprogramming

Scheme rewards those who appreciate elegance and simplicity. Its influence on modern programming is profound - understanding Scheme helps you understand why languages are designed the way they are.

Welcome to the world of Scheme!

Running Today

All examples can be run using Docker:

docker pull weinholt/guile:latest
Last updated: