Skip to content

Instantly share code, notes, and snippets.

@amirouche
Last active November 27, 2015 18:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save amirouche/f686a7192429d3cd0502 to your computer and use it in GitHub Desktop.
Save amirouche/f686a7192429d3cd0502 to your computer and use it in GitHub Desktop.

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.

Disclaimer

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.

Basics

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

Introduction

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

Calling a procedure

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.

REPL

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.

Toplevel variable definition

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

List List List

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)

apply

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

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"))

Associations

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.

Toplevel procedure definition

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))

map procedure

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.

Making list out of a 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?

Wrapping up

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

title: Learn Scheme Guile: Forward data: 2015-10-12 20:23

Introduction

Backtracking

In the first tutorial we studied the basics of Guile. You studied:

  • how to call procedure (string->list "abcdefghijklmnopqrstuvxyz")

  • how to define a variable (define answer 42)

  • how to create list (list "abc" ruse 22)

  • how to create pairs (cons "guile" 1)

  • how to create associations (list (cons "functional" 1) (cons "OOP" 2))

  • how to define a procedure (define (decrement count) (- count 1))

  • how to use (map proc lst) to create new list out of a list and procedure

Continuation

In this tutorial you will study:

  • how to solve the last exercise

  • how to define variable inside procedures

  • a short hand to make recursive procedures

  • how to define mutable datastructures

Picking up breakfast

In the last tutorial we offered a breakfast box for every guiler. Remember it looked like the following:

scheme@(guile-user)> (define box (list (cons "apple" 1) (cons "donuts" 3) (cons "chai" 2)))

We created a procedure that picked a chai from the box, but it was picking something even if nothing was in the box:

scheme@(guile-user)> (define (pick-chai box)
                       (map pair-pick-chai box))
;;; <stdin>:202:23: warning: possibly unbound variable `pair-pick-chai'
scheme@(guile-user)> (define (pair-pick-chai pair)
  (if (equal? (car pair) "chai")
      (cons "chai" (1- (cdr pair)))
      pair))

Now when we apply the first procedure, it really pick a chai, but doesn't stop picking when there is no more chai, which leads to the strange situations where there is -1 chai in the box:

scheme@(guile-user)> (define box-v2 (pick-chai box))
scheme@(guile-user)> box-v2
$39 = (("apple" . 1) ("donuts" . 3) ("chai" . 1))
scheme@(guile-user)> (define box-v3 (pick-chai box-v2))
scheme@(guile-user)> box-v3
$40 = (("apple" . 1) ("donuts" . 3) ("chai" . 0))
scheme@(guile-user)> (define box-v4 (pick-chai box-v3))
scheme@(guile-user)> box-v4
$41 = (("apple" . 1) ("donuts" . 3) ("chai" . -1))

Remember that (map proc lst) returns a new list, hence pick-chai returns a new version of the box where one chai was picked. That's why we store the new version of the box and always pass its latest version to pick-chai to pick several chai.

We should only pick a chai, if there is actually a chai to pick. So we will rewrite the pair-pick-chai procedure and introduce the procedure pair-maybe-pick-chai that will return an empty chai pair if there's no more chai or a chai pair with one less chai if there is still some chai:

scheme@(guile-user)> (define (pair-pick-chai pair)
                       (if (equal? (car pair) "chai")
                           (pair-maybe-pick-chai pair)
                           pair))
;;; <stdin>:414:27: warning: possibly unbound variable `pair-maybe-pick-chai'
scheme@(guile-user)> (define (pair-maybe-pick-chai pair)
                       (if (equal? (cdr pair) 0)
                           (cons "chai" 0)  ;; there is no more chai
                           (cons "chai" (- (cdr pair) 1))))

box-v3 is the box version which has no more chai. Let's try (pick-chai box-v3). As you will see the REPL will use the new pair-pick-chai procedure defined above that makes use of pair-maybe-pick-chai:

scheme@(guile-user)> box-v3
$46 = (("apple" . 1) ("donuts" . 3) ("chai" . 0))
scheme@(guile-user)> (define box-v4.2 (pick-chai box-v3))
$47 = (("apple" . 1) ("donuts" . 3) ("chai" . 0))
scheme@(guile-user)> (equal? box-v3 $47)
$48 = #t

As you see box-v3 and $47 are equal which means that no chai was picked from the box.

Variable bindings

Variable bindings is the construction Guile use to define variables inside other constructions. This can be done thanks to let*, its syntax is the following:

(let* ((variable-one value-one)
       (variable-two value-two)
       ...)
 (expression-one)
 (expression-two)
 (return-value))

The first list that follows let* looks like a literal assoc without the dot between the variable key and the value. For instance you can try the following in the REPL:

scheme@(guile-user)> (let* ((chai 2)
                            (donut 3)
                            (apple 1))
                       (+ chai donut apple))
$49 = 6

By the way, it's not very useful in the REPL.

For instance we can write a box-mean procedure that computes the mean count of food in the box:

scheme@(guile-user)> (define (box-mean box)
                           (let* ((chai (assoc-ref box "chai"))
                                  (donuts (assoc-ref box "donut"))
                                  (apple (assoc-ref box "apple"))
                                  (total (+ chai donuts apple)))
                             (/ total 3)))
scheme@(guile-user)> (box-mean box)
$53 = 2

See what was done? The values are returned by procedures and total re-use, the previously defined bindings. The latter is a specific feature of let*. The star * in let* means that it's the improved version of let. let without star doesn't allow to reference bindings defined in the same binding block.

Named let

Obviously it's possible to do recursive call with procedures that are defined using define. Using recursive behavior is widepread, Guile provides a helper syntax in the form of the named let. It replace the following syntax:

(define (recursive a)
  (if (equal? a 0)
    "that's all!"
    (recursive (- a 1))))
(recursive 5)

With the following syntax:

(let recursive ((a 5))
  (if (equal? a 0)
      "that's all!"
      (recursive (- a 1))))

It useful in situations where you would have defined a separate procedure to implement the recursive behavior.

Good! We need a way to unbox the breakfast. Instead of an association we'd like to have the food in list appearing as many times they appear in the box:

FIXME: example

Let's try map first. So map takes pairs so unbox will look like the following:

(define (unbox box)
  (map pair-unbox box))

I hope you see what will happen. Every pair must be turned into a list with correct count of food. Let's define pair-unbox:

(define (pair-unbox pair)
  (let loop ((food (car pair))
             (count (cdr pair))
             (plate (list)))
    (if (equal? count 0)
        plate
        (loop food (- count 1) (cons food plate)))))

Little did you know that cons could also build lists... REPL to the rescue! Let's try this code:

scheme@(guile-user)> (pair-unbox (cons "apple" 2))
$65 = ("apple" "apple")

So now we have everything in place we can try this first first version of unbox:

scheme@(guile-user)> (define (unbox box)
                      (map pair-unbox box))
scheme@(guile-user)> (unbox box)
$68 = (("apple") ("donut" "donut" "donut") ("chai" "chai"))

As you can see this doesn't look like a plate. They are nested lists. The simplest solution is to use apppend-map procedure instead of map.

Defining modules

Fire you favorite emacs editor and open a new file named ess.scm.

Modules are defined using (define-module (\o/)) where \o/ is replace with the module path of the defined module. In this case the ess module is a root module so you only have to add (define-module (ess)) to the top of the file.

If for instance, later, maybe, one day we create a ess/hyperloop.scm module. It will be defined with (define-module (ess hyperloop)).

Importing other modules

To import a module, you have to use the (use-module (intrisic thruth)). Don't try this one it doesn't exists! They are numerous modules in Guile a lot of them come from srfi and rnrs specifications. Some are specific to Guile.

Let's try the standard list module namely srfi 1:

scheme@(guile-user)> (use-modules (srfi srfi-1))
scheme@(guile-user)> (last box)
$54 = ("chai" . 2)
scheme@(guile-user)> (first box)
$55 = ("apple" . 1)
scheme@(guile-user)> (first (last box))
$56 = "chai"

Lists (and assoc) are useful but are not the best mean for every end. For the situations where a list is not enough they are records. Records comes in different flavors in Guile. Here we will use mutable records to explicit the fact that Guile can also work with mutable datastructures.

To use records you must add (use-modules (srfi srfi-9) to the top of ess.scm.

The Earth Software System is needs a todo appliation, we will implement one in the following paragraph.

A todo application is a list made todo items. We will describe a todo item as a title and a status:

(define-record-type <item>
  (make-item title status)
  atom?
  (title item-title-ref item-title-set!)
  (status item-status-ref item-status-set!))

The above define a record type named <item> which has:

  • make-item as constructor,

  • two fields title and status,

  • atom? as predicate,

  • title has (item-title-ref item) as getter and (item-title-set! item title) as setter,

  • similarly status has as getter and setter respectively item-status-ref and item-status-set!.

Let's define a few task:

(define todo (list (make-item "build earth software system" "ongoing")
                   (make-item "build gnunet backed database" "not started")
                   (make-item "find the answer" "postponed")
                   (make-item "make a mini todo list application" "not started")))

We want a procedure to print the content of todo list. For that, we can use the following:

(define (item-print item)
 (format #true "~a: ~a" (item-title item) (item-status item)))

(define (todo-print todo)
 (map item-print todo))

We will create a procedure that allows to set an item at "done":

(define (item-done! item)
 (item-status-set! item "done"))

The ! suffix means that the procedure mutate the item.

Let's add an expression putting all this together:

(item-done (list-ref todo 2))
(todo-print todo)

Wrapping Up

...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment