Scheme Kickstart


by Andreas Schipplock

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:

http://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 booklet isn't complete at all; it tries to make you curious enough to go on and study Scheme on your own.

Racket has a great reference; make use of it:

https://docs.racket-lang.org/reference/index.html


Numbers

> (+ 1 2)
3

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:

Let's play with some:

> (- 10 4)
6
> (* 2 10)
20
> (/ 2 1)
2
> (modulo 10 2)
0
> (modulo 11 2)
1
> (modulo 12 2)
0
> (abs -2)
2
> (ceiling 1.5)
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:

> (* 2 (+ 1 2))
6

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)
"2"
> (string->number "2")
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:

> (inexact->exact 2.0)
2

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

> (exact->inexact 2) 2.0

You can also "ask" if your number is exact or inexact:

> (exact? 2)
#t

> (exact? 2.0)
#f

> (inexact? 2.0)
#t

> (inexact? 2)
#f

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

There are also some generic questions you can ask:

> (number? "1")
#f

> (number? 1)
#t

> (complex? 1)
#t

> (real? 1)
#t

> (rational? 1)
#t

> (integer? 1)
#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:

> (= 2 2)
#t

> (= 2 3)
#f

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

But what if we compare expressions? Does that work?

> (= (+ 2 2) (+ 2 2))
#t

> (= (+ 2 3) (+ 2 2))
#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:

> (eqv? "foo" "foo")
#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:

> (define foo "foo")
> (eqv? foo foo)
#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:

> (equal? "foo" "foo")
#t

> (equal? 1 1)
#t

> (equal? 1 2)
#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)
#t

> (= 1 2)
#f

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

> (string=? "foo" "foo")
#t

> (string=? "foo" "bar")
#f

Strings

I like strings, so do you, right?

> "foo"
"foo"

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

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

> #\a
#\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:

Let's play!

> (char=? #\a #\a)
#t

> (char=? #\a #\b)
#f

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

> (char->integer #\a)
97

> (char->integer #\b)
98

> (char>? #\b #\a)
#t

> (char>? #\a #\b)
#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)
"a"

> (string #\f #\o #\o #\b #\a #\r)
"foobar"

> (substring "foobar" 0 3)
"foo"

> (string-length "foobar")
6

> (string-append "foo" "bar")
"foobar"

To output strings you can use 'display':

> (display "foobar")
foobar

Conditionals

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

The not conditional:

> (not (= 1 2))
#t

> (not #t)
#f

> (not (= 1 1))
#f

The and conditional:

> (and (= 1 1) (> 2 1))
#t

> (and #t #t)
#t

> (and #f #f)
#f

> (and #t #f)
#f

The or conditional:

> (and (or (= 1 1) (= 2 2)) (= 1 2))
#f

> (and (or (= 1 1) (= 2 2)) (= 1 1))
#t

> (define height 100)
#undefined

> height
100

> (and (or (= height 100) (= height 200)) (= height 100))
#t

> (and (or (= height 101) (= height 200)) (= height 100))
#f

The if conditional:

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

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

The when conditional:

(define height 100)
(when (= height 100)
  (display "height is correct")
  (newline)
  (display "let's build the building")
  (newline))

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

The unless conditional:

(define height 90)
(unless (= height 100)
  (display "the height is not 100")
  (newline)
  (display "calling the police")
  (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))
greater

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

Here a more useful example:

(cond ((= (* 2 10) 20) (display "2*10 is 20")
       (> (* 2 10) 10) (display "2*10 is > 10"))
       (else (display "nope")))

(newline)

(cond ((= (* 2 10) 21) (display "2*10 is 20")
       (> (* 2 10) 20) (display "2*10 is > 20"))
       (else (display "nope")))

(newline)

Will output:

2*10 is 202*10 is > 10

nope


Functions

Defining a function in Scheme is very easy:

(define (user-name id)
  (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)
'root

> (user-name 2)
'nobody

Default argument value is possible:

> (define (user-name [id 1])
>   (if (= id 1) 'root 'nobody))
> (user-name)
'root

Anonymous Functions

In Scheme an anonymous function looks like this:

> (define greet
    (lambda (first last [greeting "Howdy "])
      (display (string-append greeting first " " last))))

> (greet "Andreas" "Schipplock")
Howdy Andreas Schipplock

> (greet "Andreas" "Schipplock" "Hola ")
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:

http://docs.racket-lang.org/reference/for.html

Example:

> (for ([value (list "foo" "bar")])
>   (display value)
>   (newline))

Output:

foo
bar

Loops With Recursion

You can also do loops with vanilla Scheme:

> (define names (list "Andreas" "Holger"))
> (define (print-list list [index 0])
>   (when (> (length list) index)
>     (display (list-ref list index))
>     (newline)
>     (print-list list (+ index 1))))
> (print-list names)
Andreas
Holger

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

> (map
>   (lambda (list-item)
>     (display list-item)
>     (newline))
>     (list "Foo" "Bar"))
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 booklet 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/