Planting Flowers in Guile
February 13, 2018
Guile is a sugary sweet programming language! We can do a lot of cool things in it, so let's jump right in!
Guile can be installed from the official downloads page. Guile comes with something called "The REPL", which stands for "Read, Evaluate, Print, Loop", as in "Read the user's input, Evaluate it, Print the output, and repeat the process". Start the REPL by opening a terminal and typing:
After hitting Return, you'll be greeted with a prompt that looks sort of like:
Welcome to the REPL! Let's do some programming.
Math & Strings
(First off, note that indentation is ignored by Guile. Spaces and tabs (known as "whitespace") don't matter)
A string is a bunch of letters. "Hello" is a string. To make Guile say something, we use (display). Open the REPL and type:
(display "Field of tulips")
The parenthesis () are necessary. Once this executes, you'll see the not-so-surprising output: "Field of tulips"!
We can also do math fast in the REPL. To add a few numbers, just use a plus sign (+) and any amount of numbers afterwards:
(+ 2 3 5) ;;Anything after two semicolons is a comment. ;;Comments are for humans to read.
The reason we use the "addition operator" (+) at the left side and all the numbers on the right is because, if we wanted to add a lot of numbers together, most programming languages would look like:
1 + 2 + 4 + 6 + 22 + 35
Well, that's a lot of plus signs! But in Guile it's easy and clean:
(+ 1 2 4 6 22 35)
This may take some time to get used to. Just time, that's all it is!
Guile can also multiply, divide and subtract
(* 2 6) ;;Multiply 2 by 6, which is 12 (/ 6 2) ;;Divide 6 by 2, which is 3 (- 6 2) ;;Subtract 6 from 2, which is -4
If we want to do something a bit more crazy, like "4 + 2 - 6", we can do that, too!
(- (+ 4 2) 6) ;; The output is 0 because 4 + 2 is 6 and 6 - 6 is 0
You know how most terminals have "tab completion" and "command history"? The REPL has that, too, it's called Readline! It's not activated by default (although I don't know why), but most people will tell us that we should activate it! In a text editor, in your home directory, create a file named ".guile" (it might already exist), inside that, at the very bottom, type:
(use-modules (ice-9 readline)) (activate-readline)
We're all done! We'll never need to worry about this again!
A list is a bunch of... things. Think of a flower bed, we have a tulip, a rose, a daisy, and a sunflower. We can start a list by using an apostrophe '
'(tulip rose daisy sunflower)
Well now we have a list! If we type:
(car '(tulip rose daisy sunflower)) ;;This outputs 'tulip
You'll notice that only tulip shows up. This is because "car" only gives us the first element in a list.
car has an evil twin, too! "cdr" (pronounced "could-er") that says "give me everything except for the first element!"
(cdr '(tulip rose daisy sunflower)) ;;This value is: '(rose daisy sunflower)
Well that's nice, but so far it's been hard to write anything bigger than one-liners! The REPL may be nice for doing small things, but it's not for writing long programs (if you make a single syntax error in the REPL, you have to start all over!). For that we use .scm files! (SCM is short for Scheme. Guile is part of the Scheme family of languages) Lets keep this in mind so we can put it into good use later.!
For this bit, open up a text editor. My favourite is Emacs because it's obviously the best editor for Guile! You should use it, too (okay fine, you can use whichever editor you prefer.)
If you want to use the REPL inside Emacs, try Geiser. Geiser supports syntax highlighting! If you want a colorful REPL but don't use Emacs, try guile-color!
Start up a new file called "flowers.scm". Guile files can be named anything as long as they have the extension ".scm" at the end.
Inside the file, type the following:
(define flowers '(tulip rose daisy sunflower)) (define replace-car (cons 'lily (cdr flowers)))
This code says "Define a function called 'flowers'." The function simply contains a list.
Define a new function called replace-car
Cons the element 'lily onto the cdr of flowers (cons means 'Take this and put it on to the beginning of that)
Now you can run the program by opening a terminal and typing
You should see the output "(lily rose daisy sunflower)", which is exactly what we wanted!
Lambda is a wonderful gem. It acts as a way to add inputs to our procedures ((define) creates procedures). Lets start with an example. Say we want to find the square of a number (any number multiplied by itself). We can write a function that allows us to use any number! Open up a new file called "mysquare.scm", and inside, type:
(define mysquare (lambda (x) (* x x)))
This procedure takes a number and squares it. Let's try it out! Open your terminal & type
From there, type
You now loaded your function! In your REPL, use it with
We used 3 for this example, but use any number you want! You should see the desired output (in this case, 9)
In this bit, I want to talk about recursion. Recursion is a procedure that "does itself" until a certain condition is met. That sounds weird but you can think of it like a loop. I'm going to give you an example, it'll seem a little odd but I'll explain it after.
Create a file named weeds.scm
(define weeding (lambda (new old lat) (cond ((null? lat) (quote ())) (else (cond ((eq? (car lat) old) (cons new (cdr lat))) (else (cons (car lat) (weeding new old (cdr lat)))))))))
Now inside your REPL, load it up (with (load "weeds.scm")). While we're here, let's define new, old, and lat.
(bdefine new 'tulip) (define old 'weed) (define lat '(rose sunflower weed daisy))
Alrighty. So lat is a list, but we have a weed right where we want to plant our tulip! This is where our recursion comes in!
First we (define weeding) and add a (lambda) with some inputs (new, old and lat)
Next, we use cond (which is short for Conditional). cond asks questions
((null? lat) This one says "Is lat (the list) an empty list?" (often called "the null list") If it is, return nothing at all, because there are no weeds to replace.
Next we use (else), as in "If lat is empty, return nothing, or else, if it isn't empty, do the following:"
(eq? (car lat) old) eq? is short for "equal?". This asks if the car of our list is equal to old (old is 'weed), and if (car lat) is 'weed, cons new onto the cdr of lat (cdr lat is a list itself, and since (car lat) is 'weed, we don't want it, so we attach 'tulip to cdr lat
Else, if (car lat) is not 'weed, do it again, but this time, do it with (cdr lat), which means lat is now '(sunflower weed daisy) instead of '(rose sunflower weed daisy)
Notice that we (cons the car of lat onto our recursion. This is because we only want to get rid of 'weed, not rose or sunflower
This may be a little confusing still, but write it down a few times, change things up, maybe use numbers instead of other things, and you'll find that even though it takes some time to explain, the individual pieces are simple!
A few handy tips
The cdr of a list is always another list, so (cdr '(a b c d e)) is '(b c d e)
So (cdr (cdr '(a b c d e))) is '(c d e)! (car (cdr '(a b c d))) is (b).
car and cdr may be a little weird to get used to, but they're very handy!
(cons) adds something to the start of a list. cons always has two arguments, an element and a list. (cons 'a '(b c d)) is '(a b c d).
(cons 'a (cons 'b (cons 'c ()))) is the same as '(a b c)
Pattern matching is a helpful method used to... Well, match patterns! We start with loading the (ice-9 match) module. It's included in the default Guile installation, so there's no need to download any external library!
After the match module is loaded, we tell Guile "I want you to find needle x in haystack y!" Take this program as an example:
(use-modules (ice-9 match)) (match 3 (1 'one) (2 'two) (3 'three) (4 'four))
Since the "case" we specified is 3, our output is "three"! Pattern matching makes for beautiful, readable code. This is what our file would have looked like without pattern matching:
(patmat (lambda (a b) (cond ((null? b) (quote ())) (else (cond ((eq? a (car (car (car b)))) (car (car (car b)))) (else (patmat a (cdr b)))))))) (define mylist '(((1) 'one) ((2) 'two) ((3) 'three) ((4) 'four))) (patmat 3 mylist)
Well, that's a little cryptic! You can probably tell now that pattern matching is very nice to have!
Let's write our match program with a lambda, to give us the option to use more numbers than just 3!:
(use-modules (ice-9 match)) (define patmat (lambda (a) (match a (1 'one) (2 'two) (3 'three) (4 'four) (else 'no))))
Very nice! Lets run some tests real quick:
(patmat 2) (patmat 4) (patmat 8)
Alrighty, let's go a bit deeper! Let's find a pattern and add the two middle numbers together! We can do this with letter variables:
(use-modules (ice-9 match)) (define patmatch (match '(1 2 3 0) ((1 x y 0) (+ x y)) ((2 x y 8) (+ x y)) ((5 x y 9) (+ x y))))
Pretty cool, pretty cool!
There's a lot of cool things you can do with pattern matching, but it would be too long to explain them all in this tutorial. If you want to learn some extra tips, I'd recommend This article by Ceaude or This short video introduction. Anyway, we're nearing the end of the article, so let's continue...
A text adventure game!
Let's take what we learned & create a short text adventure game. Most of it is pattern matching (the same method can be used to make an adventure game much, much larger!)
First, here's the "game.scm" file:
(use-modules (ice-9 match)) (define inventory '(rose lily poppy)) (display "You stand in a field of rolling hills, dark grass, and flowers. It's drizzling softly, the cool breeze gently ruffles your hair. i: Leave and go inside the house. t: Pick tulip l: Pick lilac d: Pet dog >") (match (read-char) (#\i (display "You went inside. -The end")) (#\t (display "You picked a tulip!") (cons 'tulip inventory)) (#\l (display "You picked a lilac!") (cons 'lilac inventory)) (#\d (display "You petted a nearby dog! Good puppy!")))
#\ tells the computer "The following letter is a character, not a variable or a list.", so when you press "A" on your keyboard, Guile sees "#\A".
(read-char) is a way to say "Stop the program and 'read' the next character from the user's input!"
This adventure is very short because I want the article to be very short. There are no Good ending/Bad ending's or different paths. As a word of advice: when making an adventure game, you can break it down to multiple files by using (load "filename.scm"), for instance:
(#\i (load "InsideHouse.scm"))
I hope this all makes sense! If it doesn't, don't sweat it! It just takes time.
If you want to master Guile, try these resources! Guile is part of the Scheme family, and as such, books on Scheme are also books on Guile! Every example in them can be used in Guile (more or less)!
The Little Schemer. This book is fun and short! You'll learn to master the basics of Scheme, which is pretty much all Scheme is. Practice the methods used in this book & you'll become brilliant in no time! (Also try the sequel The Seasoned Schemer, admittedly I have never read it)
Structure and Interpretation of Computer Programs. This one is a little bit dry & technical, but if you're a dork like me, you'll definitely enjoy it!
Learn Scheme in 15 Minutes by Artanis. This one is extremely well written & the Artanis framework itself is wonderful (and written in Guile)! Check it out!
Having Fun with Guile by Chris Webber! This super fun and short online tutorial is super fun and short!
Ceaude - Programming Language Adventures is a blog with very well written & straight forward articles!
David Thompson's collection of Guile frameworks, Seriously, my website was written with his site generator & there are tools that allow you to easily make video games on the fly!
I hope this helped at least a little bit! After all this talk about flowers, I really feel like wearing some flowery perfume & steeping myself a flowery tea, you can do the same if you like! Until next time, stay kind! -Muto