Starting a wisp project
Wisp combines the beauty of Python with the power of Scheme (a Lisp). If you want the most powerful language while enabling non-programmers to read and edit your code, Wisp could be your best choice. It is implemented on top of GNU Guile and gives you access to all the capabilities of Guile Scheme (the official GNU extension language), including new ones as they are added.
All the while, it can look like this:
import : shakespeare Enter : Old Hand New Star New Star What does it take to change the world? Old Hand A wisp and a dream, and the persistence to make them real.
Here I’ll show you how to start your first wisp project.
Table of Contents
Get Guile and Wisp
Begin by installing Guile 2.0 or later and downloading wisp-0.9.5 or later. Then unpack wisp within your project and get the language folder:
mkdir PROJ
cd PROJ
tar xf wisp*.tar.gz
cp -r wisp*/language language
The wisp REPL
Now your project is prepared. You can already experiement with wisp by calling
guile -L .
Then try the following:
,L wisp define : hello world __ cons 'Hello world hello 'Eris .
That’s it. You just ran your first wisp code. The first line is a meta-command which switches to the language wisp (discovered via ./language/wisp/spec.scm
).
But you won’t only want to run interactively. You’ll also want to build tools — to use wisp in your programs.
A Wisp script: hello
To get that, exit the REPL (the interactive read-eval-print-loop
you’re running right now) with CTRL-D
, then create a text file named hello
. To execute it via Guile with wisp via ./hello
, you can use shell indirection. Start the file with the following header.1
#!/bin/sh # -*- wisp -*- # precompile the wisp spec guile -L "$(dirname "$0")" \ -c '(import (language wisp spec))' # run your file via wisp exec guile -L "$(dirname "$0")" \ -x '.w' --language wisp \ -s "$0" "$@" # the sh indirection header ends here !#
Then call chmod +x hello
. This allows you to run wisp code directly; if you install wisp systemwide, you can leave out the pre-compilation step.
Let’s discuss how the header works.
- The first line (
#!/bin/sh
) tells Unix to run the file through the shell. - The second line (
# -*- wisp -*-
) tells your editor to use the wisp mode. - The third and fourth line precompile the wisp spec with the scheme language.
- The fifth and sixth line execute the file as wisp in a new process and switch to that process. The shell won’t see any further lines. We’ll go in depth through that file after finishing the header.
- The seventh line is never seen by the shell (thanks to
exec
). It ends in!#
, the terminator for a multi-line comment in Guile; the comment started with the hashbang line (#!/bin/sh
).
Now for line number 6: The line which runs this script as wisp:
exec guile -L "$(dirname "$0")" \ -x '.w' --language wisp \ -s "$0" "$@"
exec
ends the shell script after calling its arguments.guile
uses Guile from the environment.- -L "$(dirname "$0")" tells Guile to add the directory with the script “to the front of the module load path” (quoted from
guile -h
). -x .w
adds ".w" to the front of the load extensions, so guile will consider .w files for loading. This allows writing modules in wisp.--language wisp
switches to using wisp as language.- -s "\(0" "\)@" runs this script with the arguments given to
./hello
.
By adding this header, you can write working scripts like the following:
import : only (srfi srfi-1) second display "Hello " display if : pair? : cdr : command-line second : command-line . "World" display "!" newline
Call this file hello, then run ./hello Eris
.
A Wisp module: hello.w
If you later want to re-use parts of your scripts, you can move them into modules. Create a file hello.w
.
One advantage is that this often starts much faster because it can skip processing the source and jump right to the bytecode.
The module-header looks just like the previous header, except for the following exec-line:2
exec -a "$0" guile -L "$(dirname "$0")" \ -L "$(dirname "$0")" \ -x '.w' --language wisp \ -e '('"$(basename "$0" .w)"')' \ -c '' "$@"
The body defines a module and wraps the parts to execute in a main
procedure.
define-module : hello . #:export : main say-hello import : only (srfi srfi-1) second define : say-hello world display "Hello " display world display "!" newline define : main args say-hello if : pair? : cdr args second args . "World"
You can now call this file as ./hello.w Eris
, but also import the say-hello
procedure from another file. For a version with included module-header, see the complete example: htmlo.w.
You can now import say-hello
only by using
import : only (hello) say-hello
or import all exported symbols with
import : hello
A complete Wisp example: htmlo.w
Let’s do that as a final example. Call the file htmlo.w
.
#!/bin/sh # -*- wisp -*- # precompile the wisp spec guile -L "$(dirname "$0")" \ -c '(import (language wisp spec))' # run your file via wisp exec -a "$0" guile -L "$(dirname "$0")" \ -L "$(dirname "$0")" \ -x '.w' --language wisp \ -e '('"$(basename "$0" .w)"')' \ -c '' "$@" # the sh indirection header ends here !# define-module : htmlo . #:export : main htmlo import : hello define : say-htmlo world display "<html><head><title>" say-hello world display "</title></head><body>" say-hello world display "</body></html>" newline ;; this main replaces the imported one define : main args say-htmlo if : pair? : cdr args car : cdr args . "World"
And that is it: Your first full-fledged wisp project!
Thank you!
You can find more information about Wisp on its website: http://draketo.de/english/wisp
For day-to-day programming, refer to the Guile Reference manual,
either online or via your friendly info reader (in Emacs that’s
C-h i m Guile
, in the terminal it’s info Guile
). Some basic tasks
in Guile are explained in Guile basics and py2guile.
Thank you for reading, and Happy Hacking!
License: cc by-sa
Footnotes:
This shell indirection combines hashbang syntax with Guile inline comment syntax to allow defining argument handling in the script directly within the header with all the flexibility from bash scripts – without having to implement special handling inside Guile. This reaches a level of technical elegance that has me completely in awe of Guile.
Note that this header can be used with different module names, as long as the module with the main procedure to call is named after the file (without extension).