Gary Houston has written a module called goonix which implement the POSIX system calls (a standardized set of UNIX system calls, see [AdvancedProgrammingUNIX], chapter 2 and [POSIX]) in scheme. This makes it easy to do things like list directories, create processes and so forth.
Goonix calls are described in great detail in the Guile Programmer's Manual, and in the Goonix Manual. The former is more accurate, since not all of the original goonix implementation is included in Guile).
Here is an example of using the goonix opendir, readdir
and closedir calls (available in
`examples/misc/list-dir.scm').
;; list-dir takes a path and returns a list of files in that directory
(define (list-dir path)
(letrec ((dport (%opendir path))
(form-dlist ; a procedure that forms the list
(lambda (dl)
(let ((fname (%readdir dport)))
(if (eof-object? fname)
dl
(form-dlist (cons fname dl))))))
)
(define output-list (form-dlist '()))
(%closedir dport)
output-list
)
)
To run it, just load the file ((load "list-dir.scm") and type
(list-dir "/tmp") (it will return a list with the filenames in
/tmp).
I now return to trendy features in Guile.
Many programs can be naturally split into several tasks, which are best run as separate processes.
The traditional way of doing this in the UNIX environment is to fork a new process, and exec the other program in it.
Another approach is to set up a client-server pair of programs, where the server offers information, and the client uses and displays that information. Many servers then fork off copies of themselves to service connections from new clients.
But the UNIX fork/exec mechanisim is limited by the fact that after a
fork() system call, the child process receives copies of
the parent's variables. This has a few consequences: (1) process
creation with fork() is slow, because so much data (which almost
never gets used) has to be duplicated; (2) parent and child cannot
examine each other's data structures.
Traditional UNIX processes are frequently called heavyweight processes for the reasons mentioned above. More recently, several implementations of light-weight processes, or threads, have become available. A thread is an execution path which has its own stack and local variables, but shares global variables with the other threads in the process. Creating a new thread is very inexpensive, and the process has mechanisms to control the behaviour of the new thread.
The Posix comittee has put forth a standard for UNIX threads (POSIX 1003.1c standard). The implementations available in Sun's Solaris 2 and Novell's UnixWare do not follow the POSIX standard, but the free software thread libraries used in Guile are POSIX compliant.
Though they are useful, threads have some serious disadvantages right now. The main one is that they are not deeply ingrained in many fundamental UNIX libraries. For example, the Tk library is not thread-safe, i.e. it does not behave correctly when used in a multi-threaded program. When programming with threads these days (for example, when you mix Guile threads with Tk) you have to worry about these issues.
By default Guile does not build with threads, since they can be tricky on some architectures. The UNIX variants that support Guile threads right now are Solaris 2.5, Linux and SGI IRIX 5.3.
To configure Guile to use threads you could use:
./configure --prefix=your-prefix=dir --with-threads
or
./configure --prefix=your-prefix=dir --with-threads=pthreads
The first approach uses the qt thread library, the second uses
the MIT pthreads library. You should notice no difference when
you program in scheme.
The first example demonstrates creating new threads to execute scheme procedures. I define a procedure that prints some stuff, sleeps, and then prints some more stuff.
Then I use the make-thread procedure to start up many copies of
that routine (with different arguments), each in its own thread.
You will notice that the monitor procedure is used. Using
monitor guarantees that simultaneous invocations of (print
...) will not step on each other. This kind of resources
locking is frequently necessary when programming with threads.
Also notice that at the end of this program I use the join-thread
procedure. This tells the original thread to yield control to the other
threads until they have all returned.
;; A first example of using threads that really shows that two threads
;; are running. This example works with cooperative threads (should
;; also work with preemptive threads).
(require 'debug)
(define (sample-thread delay)
(begin
(monitor
(print "this thread was invoked with a delay of " delay)
(print "About to go to sleep:"))
(force-output)
(sleep delay)
(monitor
(print delay "-> done with sleeping"))
(force-output)))
;; arrange the threads we are creating in a list
(define list-of-threads (list (make-thread (lambda () (sample-thread 40)))
(make-thread (lambda () (sample-thread 90)))
(make-thread (lambda () (sample-thread 70)))
(make-thread (lambda () (sample-thread 55)))))
;; now invoke join-thread: this makes sure that the guile "read"
;; statement (which waits for the next scheme expression) does not
;; block out the threads: join-thread will make sure they have all
;; finished executing.
(for-each join-thread list-of-threads)
(newline) (newline)
(print "all threads seem to have terminated")
Here's what it looks like when you run it:
guile> (load "sample-threads.scm") ;;; loading sample-threads... ;;; loading debug... ;;; ...loaded /nh/toaster/packages-root/.packages/guile/guile-r0.4/work/lib/slib/debug. ; type (init-debug) "this thread was invoked with a delay of " 90 "About to go to sleep:" "this thread was invoked with a delay of " 70 "About to go to sleep:" "this thread was invoked with a delay of " 55 "About to go to sleep:" "this thread was invoked with a delay of " 40 "About to go to sleep:" 40 "-> done with sleeping" 55 "-> done with sleeping" 70 "-> done with sleeping" 90 "-> done with sleeping" "all threads seem to have terminated" ;;; ...loaded sample-threads. #<unspecified> ;Evaluation took 90070 mSec (140 in scm_gc) 30918 cells work, 4557 bytes other guile>
OpenGL is a version of the famous Silicon Graphics (SGI) GL library which can run on many architectures. SGI developed OpenGL so they could market the software to people who do not use SGI hardware. OpenGL has almost entirely replaced GL now.
Brian Paul (http://www.ssec.wisc.edu/~brianp/) has developed a high-quality and almost complete free software implementation of OpenGL, called Mesa. The Guile distribution includes Mesa, and there is a set of scheme bindings so you can use Mesa from scheme.
As with other graphical systems, it is frequently tedious to set up windows, event handlers, and the framework in which we then draw. Mark Kilgard at SGI has developed a very simple toolkit called glut, which makes it extremely easy to set up windows and other widgets. You then use ordinary OpenGL calls to draw in those windows.
The references, and Brian Paul's web page, say more about OpenGL, Mesa and glut, so here I just give you an example of a program that uses glut. This program (`examples/misc/glut-sample.scm' in the examples) sets up a window and draws a sphere in it. It is based on one of the C examples in the glut distribution.
I figured out how to use the Mesa/glut bindings in Guile by simply using all the C routines in scheme: the mapping is almost trivial. By looking at this program, and referring to the glut and OpenGL documentation, you should be able to get started with Guile/OpenGL/glut programming. The only difficulty I found in this "direct translation" approach is that the C code to set up a "refreshing" function:
glutDisplayFunc(display_proc);
in scheme should look like:
(define (glutDisplayFunc-callout) (display-proc)) (use-glutDisplayFunc my-display)
So the set of steps to use Mesa and glut is:
gscm_init_glconsts (); gscm_init_glfns (); gscm_init_glutconsts (); gscm_init_glutfns ();
From scheme you should:
logical package from slib: this
provides bit manipulation routines. So at the top of your file, put:
(require 'logical) ; we do some bit manipulation
glutInit(&argc, argv): this call does not
have a scheme equivalent because it is invoked in the `ggl' main
program.
(glutInitDisplayMode mode-bits) and
(glutCreateWindow window-name).
(define (glutDisplayFunc-callout) (my-display)) (use-glutDisplayFunc my-display)
(glutMainLoop)
Here's the code:
#!/packages/bin/ggl
;;
;; an example of using the OpenGL and glut packages; this program
;; should be loaded into "ggl", which is the Guile image with the mesa
;; and glut libraries loaded
;;
;; This program draws a sphere in a window. The window is managed by
;; glut, and the sphere is drawn with OpenGL calls.
;;
;; Note: this was written in scheme by Mark Galassi (1996/05/26), but
;; transliterated from Mark Kilgard's glut example written in C.
;;
(require 'logical) ; we do some bit manipulation
(define light-diffuse #s(1.0 0.0 0.0 1.0))
(define light-position #s(1.0 1.0 1.0 0.0))
;; the actual display routine, used to refresh the window
(define (my-display)
(begin
(display "refreshing\n")
(glClear (logior GL_COLOR_BUFFER_BIT GL_DEPTH_BUFFER_BIT))
(glCallList 1)
(glutSwapBuffers)))
;; now the workhorse which creates the sphere display list
(define (gfxinit)
(let ((qobj (gluNewQuadric)))
(display "entering gfxinit\n")
(gluQuadricDrawStyle qobj GLU_FILL)
(glNewList 1 GL_COMPILE)
(gluSphere qobj 1.0 20 20) ; qobj, radius, slices, stacks
(glEndList)
(glLightfv GL_LIGHT0 GL_DIFFUSE light-diffuse)
(glLightfv GL_LIGHT0 GL_POSITION light-position)
(glEnable GL_LIGHTING)
(glEnable GL_LIGHT0)
(glEnable GL_DEPTH_TEST)
(glMatrixMode GL_PROJECTION)
;; FIXME: gluPerspective seems to give arithmetic exceptions
(gluPerspective 40.0 1.0 ; field of view (deg), aspect ratio,
1.0 10.0) ; Z near, Z far
(glMatrixMode GL_MODELVIEW)
(gluLookAt 0.0 0.0 5.0 ; eye at (0, 0, 5)
0.0 0.0 0.0 ; center is at (0, 0, 0)
0.0 1.0 0.0) ; up is in +Y direction
(glTranslatef 0.0 0.0 -1.0)
(display "leaving gfxinit\n")
)
)
;; note that glutInit is already called by ggl, so it is not part of
;; the scheme interface.
(glutInitDisplayMode (logior (logior GLUT_DOUBLE GLUT_RGB) GLUT_DEPTH))
(glutCreateWindow "sphere") ; "sphere" is just the window's name
;; still trying to figure out the DisplayFunc stuff
(define (glutDisplayFunc-callout)
(my-display))
;; glutDisplayFunc is a special name used by glut to redraw the screen
(use-glutDisplayFunc my-display)
(gfxinit) ; workhorse: creates the sphere
;; now go into the glut main loop
(glutMainLoop)
Here is the equivalent C code (slightly modified from Mark Kilgard's example):
/* Copyright (c) Mark J. Kilgard, 1994. */
/* This program is freely distributable without licensing fees
and is provided without guarantee or warrantee expressed or
implied. This program is -not- in the public domain. */
#include <GL/glut.h>
GLfloat light_diffuse[] =
{1.0, 0.0, 0.0, 1.0};
GLfloat light_position[] =
{1.0, 1.0, 1.0, 0.0};
GLUquadricObj *qobj;
void
display(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glCallList(1); /* render sphere display list */
glutSwapBuffers();
}
void
gfxinit(void)
{
qobj = gluNewQuadric();
gluQuadricDrawStyle(qobj, GLU_FILL);
glNewList(1, GL_COMPILE); /* create sphere display list */
gluSphere(qobj, /* radius */ 1.0, /* slices */ 20, /* stacks
*/ 20);
glEndList();
glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
gluPerspective( /* field of view in degree */ 40.0, /* aspect
ratio
*/ 1.0,
/* Z near */ 1.0, /* Z far */ 10.0);
glMatrixMode(GL_MODELVIEW);
gluLookAt(0.0, 0.0, 5.0, /* eye is at (0,0,5) */
0.0, 0.0, 0.0, /* center is at (0,0,0) */
0.0, 1.0, 0.); /* up is in positive Y direction */
glTranslatef(0.0, 0.0, -1.0);
}
int
main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutCreateWindow("sphere");
glutDisplayFunc(display);
gfxinit();
glutCreateWindow("a second window");
glutDisplayFunc(display);
gfxinit();
glutMainLoop();
return 0; /* ANSI C requires main to return int. */
}
Guile provides a pair of primitive procedures, (catch ...) and
(throw ...), which can be used to set up very clean exception
handling mechanisms.
The paradigm is this: suppose you want to call a procedure which might encounter an exceptional circumstance. This procedure would like to invoke an exception handler and then exit.
Here's how you would do it with catch/throw:
;; an example of a program which handles exceptions with Guile's
;; catch/throw mechanisms.
;; run this by loading the file with (load "catch-sample"). It will run
;; the program (by running (catch ...). You just type strings at the
;; program, and see them echoed back. If you type the word "err" (without
;; quotes), it will (throw ...) an exception, and thus invoke the
;; handler. If you type "quit", it will exit normally.
(require 'debug)
(require 'scanf)
(require 'line-i/o)
(newline) (newline)
;; an example procedure; it's not particularly exciting: the user types
;; words, and those are echoed back.
;; The exciting thing here is that the procedure does a (throw ...)
;; when the user types the word "err", and thus involves the handler
;; set up by (catch ...).
(define (some-long-running-procedure)
(do ((str "")
(i 0 (+ i 1)))
((equal? str "quit") "done")
(if (equal? str "err")
(throw 'hi))
(display "prompt> ")
(set! str (car (scanf "%s")))
(display "one word was \"") (display str) (display "\"")
(newline)))
;; use (catch ...) to run our procedure (some-long-running-procedure),
;; so that when it throws an exception, we catch that exception with
;; our handler (the (lambda (key) ...) expression is the handler).
(catch 'hi
some-long-running-procedure
(lambda (key)
(begin
(newline) (newline)
(display "-----------------------------------------------\n")
(display "This is the HANDLER associated with (catch ...)\n")
(display "the key is ") (display key) (newline)
(display "-----------------------------------------------\n")
(newline) (newline))))
This program is in the examples directory, under `examples/misc/catch-sample.scm'. Try running this program in Guile, type some words at it (they will be echoed back). When you feel like it, type the word `err', or `quit'.
You will notice that typing `quit' will cause an ordinary return,
whereas typing `err' causes (throw ...): the procedure
(some-long-running-procedure ...) is escaped (and will never
return), and the handler is invoked. In fact, the string `we are
done with the do loop' will not be printed when you quit with
`err'.
So throw is an example of escape procedure: the procedure
that invokes throw will never regain control, and it will never
return.
guile> (load "examples/exceptions/catch-sample.scm") ;;; [junk output] prompt> some words "some" prompt> "words" prompt> some more words "some" prompt> "more" prompt> "words" prompt> err one word was "err" ----------------------------------------------- This is the HANDLER associated with (catch ...) the key is hi ----------------------------------------------- #<unspecified> ;Evaluation took 10 mSec (0 in scm_gc) 1209 cells work, 1131 bytes other guile>
guile> (load "examples/exceptions/catch-sample.scm") ;;; [junk output] prompt> some words "some" prompt> "words" prompt> some more words "some" prompt> "more" prompt> "words" prompt> quit one word was "quit" ---------------------------- we are done with the do loop ---------------------------- #<unspecified> ;Evaluation took 10 mSec (0 in scm_gc) 947 cells work, 1128 bytes other guile>
Here's an excercise: fix this program so that it will not print
unnecessary prompt> strings when you have typed more than one
word on a line.
The catch/throw formalism has a lot in common with the ANSI C
setjmp()/longjmp() mechanism ([AdvancedProgrammingUNIX],
section 7.10, page 174, also see section `Non-Local Exits' in GNU C Library). The scheme approach is defined more clearly (no standard
specifies the behaviour of setjmp()/longjmp() well enough to
allow programmers to write portable code), and benefits from scheme's
lambda () expressions.
Exception handling methods like catch and throw are
frequently built with the scheme primitive
call-with-current-continuation (usually abbreviated to
call/cc): this is a very powerful procedure which allows escape
and reentry into escaped procedures. The directory
`examples/exceptions' has a couple of implemetations of
catch/throw using call/cc.
I will not discuss continuations here, since the catch/throw
paradigm accounts for the most frequent use of continuations. The more
advanced books on scheme (see Scheme and the Art of Programming
and The Seasoned Schemer in section Where to find more Guile/scheme resources) intruduce continuations, and the Revised(4) Report on
the Algorithmic Language Scheme (R4RS, see section `Control features' in Revised(4) Report on the Algorithmic Language Scheme) gives the exact definition
of the call/cc behaviour.
In Guile catch and throw are provided as primitives and
not defined in terms of call/cc, mostly for performance reasons.
They are documented in recent versions of the SCM manual
(see section `Exceptions' in SCM).
Another included with Guile is Tom Lord's rx library. Rx implements POSIX regular expressions using the regular expression compiler paradigm: a regular expression is compiled into a finite automaton which is capable of accepting input strings that belong to that regular language.
Here's how you call the rx library from scheme. Let us take the POSIX
regular expression "abc." (which matches any string that starts
"abc" and then has a single character) and see if it matches
certain strings:
guile> (define r (regcomp "abc.")) r ;Evaluation took 16 mSec (0 in scm_gc) 179 cells work, 345 bytes other guile> r #<regex 1bb388> ;Evaluation took 0 mSec (0 in scm_gc) 45 cells work, 40 bytes other guile> (regexec r "abc") #f ;Evaluation took 0 mSec (0 in scm_gc) 96 cells work, 55 bytes other guile> (regexec r "abcd") #(0 4) ;Evaluation took 0 mSec (0 in scm_gc) 82 cells work, 64 bytes other guile>
So the procedure is:
regcomp. This will return a
finite automaton which will recognize the given language.
regexec) to check for matches.
Here is the definition of the two most user-visible procedures that implement the regular expression interface:
A pedagogical introduction to the sytax of regular expressions can be found in the Emacs manual (see section `Syntax of Regular Expressions' in The Emacs Editor).
I have mentioned frequently that Guile has many bundled packages, and I have described several of them in more or less detail. But how does one add a package to the Guile source tree? How does one plug it in?
If your package is entirely written in Scheme, like slib, then you don't have to compile it and link it with guile. In that case, you can distribute it any way you want.
But if your package contains C code (or Fortran code, or any compiled language code), as it would if you were writing a set of scheme bindings to a C-callable library, you would have to compile it and link it with Guile.
Guile provides a PLUGIN system which defines an orderly way of adding packages to the Guile tree.
Tom Lord has written an extensive document on how to add new PLUGINs to the Guile distribution (see section `Overview' in Guile Scheme PLUGINs).
Guile offers primitives to create new dynamic roots. A procedure can be
called with (call-with-dynamic-root proc error-handler), in
which case the calling environment is discarded (unwound), and a new
environment is started for the evaluation of (proc).
The rationale behind dynamic roots is mainily support for multi-threading (see section Threads in Guile): each thread should execute in its own dynamic root. But they can also be used to get around escape procedures if you are invoking procedures that do not want to be escaped (see section `Dynamic Roots' in SCM for more detail on this).
Another possible use for dynamic roots is to throw in a quick bit of code which you would like executed with a clean slate.
Dynamic roots are described in recent versions of the SCM manual (see section `Dynamic Roots' in SCM). @paragraphindent 2