title: Learn Scheme Guile: Basics date: 2015-10-11 13:00
Welcome to (wanna be) Guile tutorial!
In a nutshell, Guile is an implementation of Scheme programming language part of the family of Lisp.
Guile is a cool language because:
-
it is expressed in small set of constructs
-
it covers a large set of programming paradigms
-
it allows to manipulate its code easily and safely namely macros
To be honest, it does most of what other dynamic programming language do but comes with its own philosophy.
This is a tutorial is for people that already have a programming experience.
This tutorial introduce the basic constructions available in Guile. That is, it's not exhaustive.
To start following the tutorial, install guile and launch the REPL with guile command. The following line of text will be displayed:
scheme@(guile-user)>
You can type for instance 1985
and you will now see the following:
scheme@(guile-user)> 1985
$1 = 1985
In this tutorial you will study how to:
-
call procedure
-
define a variable
-
how to create list
-
how to
apply
a procedure to a list -
how to create pairs
-
how to create scheme dictionary aka. assoc
-
how to define a procedure
-
how to to create new list with initial list and a procedure using
map
A procedure is equivalent to what other languages call a function. In Guile
the addition is a procedure. To add 27
to 15
one can do:
scheme@(guile-user)> (+ 27 15)
$2 = 42
There is also a minus procedure named -
and times named *
. Guile is a
perfect calculator and support arbitrary big numbers. If it doesn't speak to
you, suffice to say that it's very useful for doing science.
Imagine that you have 101
donuts for the year. You'd like to know how many donut
you can eat per hour without running out of donut. You solve this big problem
using the following code:
scheme@(guile-user)> (/ 101 (* 24 365))
$3 = 101/8760
As you can see it return an exact number. To convert the results to something
that is more readable, we can ask Guile to convert the results using the
procedure exact->inexact
:
scheme@(guile-user)> (exact->inexact $3)
$4 = 0.011529680365296804
In the above $3
reference the result of (/ (* 24 365) 101)
. If the result
count is not the same as the one used above, replace $3
with the value you
see.
The REPL is a very useful tool.
The default configuration doesn't use readline
. You can activate it using a
$HOME/.guile
configuration file. Create it and copy/paste the following:
(use-modules (texinfo reflection)) ;; help
(use-modules (ice-9 readline))
(activate-readline)
Restart the REPL.
You can try to use up and down arrow to navigate history. Use TAB to complete
the current input for instance, if you type exact
and hit TAB it will display
a list of procedures that start with exact
.
You can now also use help
procedure to get the documentation of procedure.
The first way to define variables is using the define
:
scheme@(guile-user)> (define guilers 1337)
define
returns nothing, that's why there no line with a dollar sign.
At the next Guile hackfest 1337 guilers will gather to hack the final cosmits for the Earth Software System. Every hacker is given an apple, three donuts and two chai. How many apples, donuts and chais are needed?
scheme@(guile-user)> (define apple-per-guiler 1)
scheme@(guile-user)> (define apples (* apple-per-guiler guilers))
scheme@(guile-user)> apples
$15 = 1337
scheme@(guile-user)> (define donut-per-guiler 3)
scheme@(guile-user)> (define donuts (* donut-per-guiler guilers))
scheme@(guile-user)> donuts
$16 = 6018
scheme@(guile-user)> (define chai-per-guiler 2)
scheme@(guile-user)> (define chai (* chai-per-guiler guilers))
scheme@(guile-user)> chai
$17 = 4012
How much food in total will be given? Try to guess how to compute the total...
scheme@(guile-user)> (+ chai donuts apples)
$18 = 12036
Scheme is made of list. Maybe you did not recognize it but the parens and what's inside the parens separated by space form a list.
To build your own list you can use the list
procedure:
scheme@(guile-user)> (list apples donuts chai)
$19 = (1337 6018 4012)
To retrieve the head of the list you can use the car
procedure:
scheme@(guile-user)> (car (list apples donuts chai))
$20 = 1337
Whereas cdr
procedure will retrive the tail:
scheme@(guile-user)> (cdr (list apples donuts chai))
$21 = (6018 4012)
Say you already have a list a values that you want to pass as arguments to a
procedure. How do you do? Well, for that you use the (apply proc lst)
procedure
which takes the list LST
of arguments to apply
to a procedure PROC
.
For instance you can compute the sum of a list of integers using:
scheme@(guile-user)> (define everything (list apples donuts chai))
scheme@(guile-user)> (apply + everything)
$22 = 11367
Strings in Guile are similar to string in other languages. The single particular
thing is that you can only define strings with double quotes "
:
scheme@(guile-user)> (define box (list "1 apples" "3 donuts" "2 chai"))
Another important datastructure of Guile is the association. It's actually only
a list of pairs. A pair is constructed with cons
procedure, for instance:
scheme@(guile-user)> (cons "apple" 1)
$20 = ("apple" . 1)
This means that "apple"
is associated with 1
.
Together cons
, car
and cdr
are list primitives. Higher level procedure
exists to deal with more complex situations.
An association is a list of cons
.
To define a better representation for the box, we can use the following code:
scheme@(guile-user)> (define box (list (cons "apple" 1) (cons "donuts" 3) (cons "chai" 2)))
scheme@(guile-user)> box
$21 = (("apple" . 1) ("donuts" . 3) ("chai" . 2))
scheme@(guile-user)>
Now we can retrieve the number of chai in a box using assoc-ref
procedure:
scheme@(guile-user)> (assoc-ref box "chai")
$22 = 2
Usually the first argument of a procedure is the primary object, the object against which the action is taken.
define
is also used to define procedure but with small syntax change.
Remember to define a variable the syntax is the following:
(define answer 42)
Let's define ruse
procedure that returns itself:
scheme@(guile-user)> (define (ruse) ruse)
scheme@(guile-user)> ruse
$24 = #<procedure ruse ()>
scheme@(guile-user)> (ruse)
$25 = #<procedure ruse ()>
scheme@(guile-user)>
This procedure is not useful, except to explicit the syntax of define
to
define procedure. The astute reader has noted that the procedure takes no
argument. Such procedure is called a thunk. Calling a procedure you defined
is done the same way as regular procedures. In this case the procedure takes
no argument so it looks different but the principle is the same.
Let's define a procedure that takes two arguments and return the mean:
scheme@(guile-user)> (define (mean a b) (/ (+ a b) 2))
scheme@(guile-user)> (mean 12 12)
$29 = 12
Mind the fact that space and newlines have no effect on the interpretation of scheme code. So the above one liner can be written as follow:
scheme@(guile-user)> (define (mean a b)
(/ (+ a b) 2))
One of the most important procedure of Guile is (map proc lst)
iterates over a
list and apply a procedure over each item. Give a list (list a b c)
it will
return a new list (list (proc a) (proc b) (proc c))
.
For instance we can increment of list of integers:
scheme@(guile-user)> (map 1+ (iota 5))
$30 = (1 2 3 4 5)
Mind the fact, that this is a new list. Try that:
scheme@(guile-user)> (define numbers (iota 5))
scheme@(guile-user)> (define others (map 1+ numbers))
scheme@(guile-user)> (equal? numbers others)
$31 = #f
scheme@(guile-user)> numbers
$32 = (0 1 2 3 4)
scheme@(guile-user)> others
$33 = (1 2 3 4 5)
map
create a new list out of the input list.
To finish the introduction to the basics we will define a procedure that will simulate a guiler picking a chai from her box. Remember the box is defined as an association:
scheme@(guile-user)> (define box (list (cons "apple" 1) (cons "donuts" 3) (cons "chai" 2)))
scheme@(guile-user)> box
$21 = (("apple" . 1) ("donuts" . 3) ("chai" . 2))
scheme@(guile-user)>
The association is a list, so it can go through map
procedure. We will mock
the procedure that we want to implement:
scheme@(guile-user)> (define (pick-chai box)
(map pair-pick-chai box))
pair-pick-chai
takes an item of the box ie. a pair, that's why it's prefixed with
pair-
. It must decrement the count of chai if it's a "chai"
pair or return
the pair as-is if it's not. Will need a conditional branch if
.
if
syntax is the following:
(if predicate
(if-true-expression)
(if-false-expression)
Live it looks like this:
scheme@(guile-user)> (if #true "ok" "ko")
$34 = "ok"
So, if we also use car
and cdr
, you guess that pair-pick-chai
can be
defined as:
scheme@(guile-user)> (define (pair-pick-chai pair)
(if (equal? (car pair) "chai")
(cons "chai" (1- (cdr pair)))
pair))
That's all! Well almost... This is a bit naive because there might be no chai left. Maybe you can find how to solve this issue?
In the first tutorial you studied the basics of Guile:
-
how to call procedure
(string->list "abcdefghijklmnopqrstuvxyz")
-
how to define a variable
scheme: (define answer 42)
-
how to create list
scheme: (list "abc" ruse 22)
-
how to create pairs
scheme: (cons "guile" 1)
-
how to create associations
scheme: (list (cons "functional" 1) (cons "OOP" 2))
-
how to define a procedure
scheme: (define (decrement count) (- count 1))
-
how to use
scheme: (map proc lst)
to create new list out of a list and procedure