hyper.dev [feed] [resume]

2020/02/10 - Arew Scheme Tutorial: Basics

Continuation

After reading this section you will be able to write basic Scheme programs. In particular, you will study:

How to comment code

You can comment code with the semi-colon, that is ;. Idiomatic code use two semi-colons:

;; Everything after one semi-colon is a comment.

The following sections will use two semi-colons with followed by an arrow => to describe the return value.

How to write literals for builtin types

number

boolean

characters

Characters can be written with their natural representation prefixed with #\, for instance the character x is represented in Scheme code as follow:

#\x

string

A string is written with double quotes, that is ", for instance:

"hello world"

symbol

A symbol is most of the time written with a simple quote prefix, that is '. For instance:

'unique

pair

A pair of the symbol 'pi and the value 3.1415 can be written as:

'(pi . 3.1415)

list

A list can be written as literals separated by one space and enclosed by parenthesis. For instance, the following list has three items:

'(unique "hello world" (pi . 3.1415))`

The first item is the symbol 'unique, the second item is a string, the third item is a pair. The empty list is written '().

vector

A vector looks somewhat like a list but without the explicit simple quote. It use a hash prefix. For instance, the following vector has three items:

#(unique "hello world" 42)

The first item is the symbol 'unique, the second item is a string, the third item is a number.

bytevector

A bytevector is like vector but can contain only bytes. It looks like a list of integers, prefixed with #vu8. For instance, the following bytevector has three bytes:

#vu8(0 42 255)

How to call a procedure

A procedure call looks like a list without the simple quote prefix. The following describe the addition 21 and 21:

(+ 21 21) ;; => 42

It returns 42. So does the following multiplication:

(* 21 2) ;; => 42

The first item is a procedure object. Most of the time, procedure names are made of letters separated with dashes. That usually called kebab-case.

Here is another procedure call:

(string-append "hello" " " "world")

It will return a string "hello world".

How to define a variable

The first kind of variables that you encountered are procedures things like +, * or string-append.

Variables can also contain constants. You can use define:

(define %thruth 42)

The above code will create a variable called %thruth that contains 42.

Look at this very complicated computation:

(+ %thruth 1 (* 2 647)) ;; => 1337

How to compare objects

Identity equivalence

To compare by identity, in pratice, whether two object represent the same memory location, you can use the procedure eq?.

In the case where you are comparing symbols you can use the procedure eq?:

(eq? 'unique 'unique) ;; => #t
(eq? 'unique 'singleton) ;; => #f

Equivalence

If you do not know the type of the compared objects, or the objects can be of different types, you can use the procedure equal?:

(equal? #t "true") ;; => #f

The string "true" is not equivalent to the boolean #t.

It is rare to use equal?, because, usually, you know the type of the compared objects and the compared object have the same type.

Equivalence predicates

The astute reader might have recognized a pattern in the naming of the equivalence procedures eq? and equal?: both end with a question mark. That is a convention that all procedures that can only return a boolean should end with a question mark. Those are called predicates.

They are predicates for every builtin types. For instance string type has a string equivalence predicate written string=?:

(string=? "hello" "hello world" "hello, world!") ;; => #f

The predicate procedure string=? will return #t if all arguments are the same string, in the sense they contain the same characters.

How to define a procedure

The simplest procedure ever, is the procedure that takes no argument and returns itself:

(define (ruse)
  ruse)

A procedure that takes not argument is called a thunk. Indentation and the newline are cosmetic conventions. If you call the procedure ruse, it will return ruse:

(eq? ruse (ruse))

One can define a procedure that adds one as follow:

(define (add1 number)
  (+ number 1))

The predicate to compare numbers is =. Hence, the following:

(= 2006 (add1 2005)) ;; => #t

Mind the fact that it returns a new number. It does not mutate the value even if it is passed as a variable.

Let's imagine a procedure that appends a name to the string "Hello ". For instance, given "Aziz" or a variable containing "Aziz", it will return "Hello Aziz".

(define name "Aziz")

(define (say-hello name)
  (string-append "Hello " name))

(string=? "Hello Aziz" (say-hello name)) ;; => #t

;; XXX: the variable name still contains "Aziz"

(string=? name "Aziz")) ;; => #t

It does not matter for the callee whether the arguments are passed as variables or literals:

(string=? "Hello John"  (say-hello "John")) ;; => #t

Backtrack

In this section you learned: