[GoLUG] Yet again ... returning to the strange one: Guile 3.0 aka Scheme
Barry Fishman
barry at ecubist.org
Tue Aug 27 13:30:56 EDT 2024
On 2024-08-27 02:55:24 GMT, David Billsbrough wrote:
> ...
> Both these languages can easily run from the command line and
> 'compile' quickly. I have not really figured
> out the 'debugger' of Guile and just do a lot of print statements.
> Have not really master concatenation of
> strings in scheme! Getting a little better and integer conversion with "Tcl."
The Guile documentation is very terse, which makes it difficult to
learn. I started learning Guile after spending some time programming in
Common Lisp, which has very good documentation, so I had an easier time
picking up a different Lisp like language.
I also tend to use print statements in Guile rather than the debugger.
I tend to write a DEBUG function in the beginning of the file,
appropriate to the kinds of things I want to print and then use that to
print the debug output It lets me easily search for "DEBUG" to find all
the debug statements and remove them when I am done.
For example:
(define *debug* #t)
(define (DEBUG . lst)
(when *debug*
(display "DEBUG:")
(for-each (lambda (arg)
(display " ")
(display arg))
lst)
(newline)))
So I can print things more simply like:
(DEBUG "After adding:" "X:" x "Y:" y "Is:" (+ x y))
And disable them by changing the *debug* value to #f. This
is easier than dealing with any parenthesis issues when I want
to enable or disable the debug messages.
I start my Guile scripts with something like:
#! /bin/sh
## Guile -*- mode: scheme; coding: utf-8 -*-
## Time-stamp: <2024-06-30 10:19:32 barry>
exec ${GUILE:-guile} -e main -s $0 ${1+"$@"}
!#
This makes the script execute starting with an function
(define (main args)
...)
and use the proper Vim/Emacs editor setup. I can get both Vim and Emacs
to recognize it as a guile scheme program, and update the timestamp when
I save the file.
[It also uses the environment variable GUILE, when defined, to pick
the guile program. I needed it on systems when guiles2 and guile3
both existed, and the default guile was guile2. I have lots of
guile scripts and hand editing them for each system would be a pain.]
I can run the file directly as a script, or just load the program
by doing, (if the script is called my-script):
$ guile -l my-script
and then from the REPL, invoke the scheme code interactively (with tab
completion), or just run the program with:
scheme@(guile-user)> (main '("my-script" "arg1" "arg2"))
You can also use the results of a previous expression, so:
scheme$(guile-user)> (+ 3 2)
$1 = 5
scheme$(guile-user)> (+ $1 5)
$2 = 10
In this REPL mode, the help system and debugger are available
with "," prefixed commands. To get a list of the commands
available:
scheme@(guile-user)> ,help
or just:
scheme@(guile-user)> ,h
Get information on a possible command with ",a" or:
scheme(guile-user)> ,apropos concat
(guile): string-concatenate #<procedure string-concatenate (_)>
(guile): string-concatenate-reverse/shared #<procedure string-concatenate-reverse/shared (_ #:optional _ _)>
(guile): string-concatenate-reverse #<procedure string-concatenate-reverse (_ #:optional _ _)>
(guile): string-concatenate/shared #<procedure string-concatenate/shared (_)>
And then with ",d" or:
scheme@(guile-user)> ,describe string-concatenate
- Scheme Procedure: string-concatenate ls
Append the elements of LS (which must be strings) together into a
single string. Guaranteed to return a freshly allocated string.
May be not quite what you may want but
scheme@(guile-user)> ,a append
(guile): symbol-append #<procedure symbol-append args>
(guile): string-append #<procedure string-append _>
(guile): string-append/shared #<procedure string-append/shared _>
(guile): append! #<procedure append! _>
(guile): append #<procedure append _>
scheme@(guile-user)> ,d string-append
- Scheme Procedure: string-append . args
Return a newly allocated string whose characters form the
concatenation of the given strings, ARGS.
Note that "string-append . args" is Guile's way of showing
"(string-append arg1 arg2 ...)" with 0 or more args.
As far as debugging the command help works like:
scheme@(guile-user)> ,help debug
...
,break PROCEDURE [,br ,bp] - Break on calls to PROCEDURE.
,break-at-source FILE LINE
[,break-at ,bs] - Break when control reaches the given source location.
...
scheme@(guile-user)> ,help -c bs
Break when control reaches the given source location.
Starts a recursive prompt when control reaches line LINE of file FILE.
Note that the given source location must be inside a procedure.
Also note that if you do a lot of string concatenation in a program and
its a pain to type things like (string-append "foo" "bar") all the time,
you can do something like:
(define ++ string-append)
And use (++ "foo" "bar") and (++ "foo" "bar" "whatever" "else") instead.
Things like this are done all the time in normal scheme code.
------------------------------------------------
But if you like to live on the *edge*, (or as I do) have been programming
in Guile for 20 years, you can do things I'll preview in the rest of
this message.
(import (oop goops))
(define-method (+ (s <string>) . lst)
(string-concatenate (cons s lst)))
And then:
scheme@(guile-user)> (+ "3" "5")
$1 = "35"
scheme@(guile-user)> (+ 3 5)
$2 = 8
scheme@(guile-user)> (+ 3 5 9)
$3 = 17
scheme@(guile-user)> (+ "3" "5" "9")
$4 = "359"
But I *really* don't recommend it.
== Deeper in the rabbit hole ==
For example to allow primitive infix code in guile you can turn on
[ from Scheme srfi-105 https://srfi.schemers.org/srfi-105/ ]
curly-infix mode:
#!curly-infix
{"3" + "5" + "9"}
$5 = "359"
{3 + string-length("foo")}
$6 = 6
And your new overridden "+" function can be used as in infix function,
even though the infix code handling and your new + method are done
without any knowledge of each other. Curly-infix is really basic
so it doesn't deal with operator precedence, so
you need to do things like:
{{3 * 2} - 5}
--
Barry
More information about the GoLUG
mailing list