Andreas Schipplock
I'm not an assembly line worker. I'm a software developer.
I'm in favour of GNU.

Scheme Kickstart

by Andreas Schipplock

published on


Introduction

This text is for programmers who are already familiar with some other programming or scripting language and who want to learn Scheme without to be taught what a computer is and how to move the cursor with the mouse.

For all the examples I used Racket, a very complete scheme implementation. You can try different scheme runtimes, compilers and script interpreters but make sure they implement the R6RS standard. However, I recommend Racket; get it at: https://www.racket-lang.org

After the setup you will have a 'DrRacket' and 'Racket' binary. If you use the IDE 'DrRacket' make sure to load the correct language in the top edit window:

#lang racket

Or you start 'Racket' which loads the racket lang by default.

This publication isn't complete at all; it tries to make you curious enough to go on and study Scheme on your own. It's meant to get you started quickly.

Racket also has a great reference; make use of it:


Numbers

1: (+ 1 2)

This, as a whole, is an expression. + is a primitive operator. 1 and 2 are operands. This expression basically says: "1+2" and its result is "3".

Here are some operations you can do on numbers:

  1. -
  2. +
  3. /
  4. round
  5. modulo
  6. remainder
  7. square

Let's play with some:

1: (- 10 4)

This results to:

6

Let's see another simple example:

1: (* 2 10)

This results to:

20

Let's see another simple example:

1: (/ 2 1)

This results to:

2

Let's see another simple example:

1: (modulo 10 2)

This results to:

0

Let's see another simple example:

1: (modulo 11 2)

This results to:

1

Let's see another simple example:

1: (modulo 12 2)

This results to:

0

Let's see another simple example:

1: (abs -2)

This results to:

2

Let's see another simple example:

1: (ceiling 1.5)

This results to:

2.0

...and so on. Before I have a look at all the possibilities, I want to show some other code. In scheme you can "nest" your expressions. Most expressions return just one value and this value can be used for another expression:

1: (* 2 (+ 1 2))

So what does this expression do?

(+ 1 2) gets evaluated to 3 so we end up with: (* 2 3) and (* 2 3) gets evaluated to 6. Scheme will evaluate from the inside to the outside and from left to right.

To turn a number into a string you can do something like this:

(number->string 2)

This results to:

"2"

Or the other way around:

(string->number "2")

This results to:

2

Scheme knows exact and inexact numbers. An example for an exact number would be 2 and 2.0 for an inexact number.

To turn an inexact number into an exact number you can use:

1: (inexact->exact 2.0)

This results to:

2

To turn an exact number into an inexact number you can use:

1: (exact->inexact 2)

This results to:

2.0

To turn an exact number into an inexact number you can use:

1: (exact? 2)

This results to:

#t

Let's see another simple example:

1: (exact? 2.0)

This results to:

#f

Let's see another simple example:

1: (inexact? 2.0)

This results to:

#t

Let's see another simple example:

1: (inexact? 2)

This results to:

#f

#f means "false" and #t means "true".

There are also some generic questions you can ask:

1: (number? "1")

This results to:

#f

Let's see another simple example:

1: (number? 1)

This results to:

#t

Let's see another simple example:

1: (complex? 1)

This results to:

#t

Let's see another simple example:

1: (real? 1)

This results to:

#t

Let's see another simple example:

1: (rational? 1)

This results to:

#t

Let's see another simple example:

1: (integer? 1)

This results to:

#t

Makes sense? It does. Such questions are called "predicates". But it's not really important to know how it's called. It has a question mark so it's clear what it does.


Equal Or Not

A simple equal check:

1: (= 2 2)

This results to:

#t

Let's see another simple example:

1: (= 2 3)

This results to:

#f

Ok, that's obvious. 2 is equal 2 and 2 isn't equal 3.

But what if we compare expressions? Does that work?

1: (= (+ 2 2) (+ 2 2))

This results to:

#t

Another expression:

1: (= (+ 2 3) (+ 2 2))

This results to:

#f

Of course it works because (+ 2 2) and (+ 2 2) get evaluated first and then the = kicks in and simply compares 4 with 4.

Scheme also provides some equality predicates that work on objects and have different rules on what they regard as "equal". eqv?, eq? and equal? is what you can use to compare object equality.

It's best to show it by examples:

1: (eqv? "foo" "foo")

This results to:

#t

oh, true? Yes. But I show this example because depending on your scheme environment this might return #f. This is simply not specified strictly enough. In Racket this will return #t.

I know I haven't mentioned it yet but you can define "variables" like this:

1: (define foo "foo")
2: (eqv? foo foo)

This results to:

#t

You define a variable called foo with the value "foo". If you now compare its equality to itself it will evaluate to #t because it's the same object.

equal? is much more easygoing:

1: (equal? "foo" "foo")

This results to:

#t

Another example:

1: (equal? 1 1)

This results to:

#t

And another tiny example:

1: (equal? 1 2)

This results to:

#f

And eq? is regarded as the strictest one.

I decided for myself to ignore eqv?, eq? and equal? as of now because they have far too many rules and I don't see a use-case for me right now. I want to compare numbers and strings and my brain has very simple rules to decide what's equal and what isn't. I don't care if "foo" is in a different location in memory than "foo". If I have this in my source I won't make that differenciation. Technically, of course, it's stored in a different location in memory but I don't care in most cases so that's my decision to ignore eqv?, eq? and equal?. Some day I'll probably be in the need to differenciate but not yet.

If I want to compare a number, I simply use '=':

1: (= 1 1)

This results to:

#t

Another example:

1: (= 1 2)

This results to:

#f

If I want to compare a string, I simply use 'string=?':

1: (string=? "foo" "foo")

This results to:

#t

Another example:

1: (string=? "foo" "bar")

This results to:

#f

Strings

I like strings, so do you, right?

"foo"

Ok, that's a string but what can I do with it? Let's see...

  1. string
  2. string?
  3. make-string
  4. string-length
  5. substring
  6. string-append
  7. string-copy
  8. string-fill
  9. string-upcase
  10. string-downcase
  11. string-foldcase
  12. string->number
  13. string->utf8
  14. string-ref
  15. string-set!
  16. string=?

When we are talking about strings we should play with chars as well:

#\a

This is a char. So we build a char with "#\" and append our char to it. And of course there are some functions for chars as well:

  1. char=?
  2. char>?
  3. char<?
  4. char>=?
  5. char<=?
  6. char-ci=?
  7. char-ci<?
  8. char-ci>?
  9. char-ci<=?
  10. char-ci>=?
  11. char-alphabetic?
  12. char-numeric?
  13. char-whitespace?
  14. char-upper-case?
  15. char-lower-case?
  16. digit-value
  17. char?
  18. char->integer
  19. integer->char
  20. char-upcase
  21. char-downcase
  22. char-foldcase

Let's play!

(char=? #\a #\a)

Results in: #t

(char=? #\a #\b)

Results in: #f

a = a, so it's true. "a" isn't "b", so this evaluates to false.

(char->integer #\a)

Result is: 97

(char->integer #\b)

Result is: 98

(char>? #\b #\a)

Result is: #t

(char>? #\a #\b)

Result is: #f

The numerical represantation of "a" is 97 and 98 for "b" so (char>? #\b #\a) evaluates to true as 98 is bigger than 97 and (char>? #\a #\b) evaluates to false as 97 isn't bigger than 98.

Try to play around with the other char procedures. Try to look it up in the racket reference which you can find at: https://docs.racket-lang.org/reference/index.html

Now let's play with strings:

(string #\a)

Result is: "a"

(string #\f #\o #\o #\b #\a #\r)

Result is: "foobar"

(substring "foobar" 0 3)

Result is: "foo"

(string-length "foobar")

Result is: 6

(string-append "foo" "bar")

Result is: "foobar"

To output strings you can use 'display':

(display "foobar")

Result is: foobar


Conditionals

Now that you know a bit about numbers and strings you can go on with conditionals.

The not conditional:

(not (= 1 2))

Result is: #t

(not #t)

Result is: #f

(not (= 1 1))

Result is: #f

The and conditional:

(and (= 1 1) (> 2 1))

Result is: #t

(and #t #t)

Result is: #t

(and #f #f)

Result is: #f

(and #t #f)

Result is: #f

The or conditional:

(and (or (= 1 1) (= 2 2)) (= 1 2))

Result is: #f

(and (or (= 1 1) (= 2 2)) (= 1 1))

Result is: #t

(define height 100)

Result is: #undefined

height

Result is: 100

(and (or (= height 100) (= height 200)) (= height 100))

Result is: #t

(and (or (= height 101) (= height 200)) (= height 100))

Result is: #f

The if conditional:

(if (> 3 2) 'yes 'no)

Result is: yes

(if (> 2 3) 'yes 'no)

Result is: no

The when conditional:

1: (define height 100)
2: (when (= height 100)
3:   (display "height is correct")
4:   (newline)
5:   (display "let's build the building")
6:   (newline))

"When" the expression after "when" evaluates to "true" all following expressions are executed; there is no "else" here.

The unless conditional:

1: (define height 90)
2: (unless (= height 100)
3:   (display "the height is not 100")
4:   (newline)
5:   (display "calling the police")
6:   (newline))

This will print 'the heigh is not 100' and 'calling the police'.

The cond conditional:

The cond-conditional is a nicer version of if then else if else if else if...or a different 'switch case'.

(cond ((> 3 2) 'greater))

Result is: greater

(cond ((string=? "foo" (string #\f #\o #\o)) 'yup))

Result is: yup

Here a more useful example:

01: (cond ((= (* 2 10) 20) (display "2*10 is 20")
02:        (> (* 2 10) 10) (display "2*10 is > 10"))
03:        (else (display "nope")))
04:
05: (newline)
06:
07: (cond ((= (* 2 10) 21) (display "2*10 is 20")
08:        (> (* 2 10) 20) (display "2*10 is > 20"))
09:        (else (display "nope")))
10:
11: (newline)

Result is:

2*10 is 202*10 is > 10
nope

Functions

Defining a function in Scheme is very easy:

1: (define (user-name id)
2:   (if (= id 1) 'root 'nobody))

This defines the function 'user-name'. This function takes one argument 'id'. It returns 'root' in case you call it with 'id' = 1 and 'nobody' when called with a different id.

Let's call this function now:

(user-name 1)

Result is: 'root

(user-name 2)

Result is: 'nobody

Default argument value is possible:

1: (define (user-name [id 1])
2:   (if (= id 1) 'root 'nobody))
3: (user-name)

Result is: 'root


Anonymous Functions

In Scheme an anonymous function looks like this:

1: (define greet
2:   (lambda (first last [greeting "Howdy "])
3:     (display (string-append greeting first " " last))))

Let's call it:

(greet "Andreas" "Schipplock")

Result is: Howdy Andreas Schipplock

Let's call it differently:

(greet "Andreas" "Schipplock" "Hola ")

Result is: Hola Andreas Schipplock

Do you see the 'lambda'? That is your anonymous function.


Loops

So how do you loop? Racket implements SRFI-42 so you can make use of all the constructs presented here: for.html

Example:

1: (for ([value (list "foo" "bar")])
2:   (display value)
3:   (newline))

Result is:

foo
bar

Loops with recursion

You can also do loops with vanilla Scheme:

1: (define names (list "Andreas" "Holger"))
2: (define (print-list list [index 0])
3:   (when (> (length list) index)
4:     (display (list-ref list index))
5:     (newline)
6:     (print-list list (+ index 1))))
7: (print-list names)

Result is:

Andreas
Holger

Or in case of a list you can just use a predefined list loop:

1: (map
2:   (lambda (list-item)
3:     (display list-item)
4:     (newline))
5:     (list "Foo" "Bar"))

Result is:

Foo
Bar

Where To Go From Here?

Racket is a wonderful Scheme implementation. If you liked the previous pages, I invite you to go on with: https://docs.racket-lang.org/guide/index.html and: https://docs.racket-lang.org/reference/index.html.

This tiny publication can't cover what the Racket guide and reference offers but if it only made you curious, that's enough for me.

If you couldn't follow me on these few pages, I recommend: http://www.ccs.neu.edu/home/matthias/HtDP2e/

I hope this text was useful.