A Guile/Scheme mini-tutorial for Linux developers (2)

Published on july 2, 2019 at starynkevitch.net/Basile/guile-tutorial-2.html. this is work in progress and incomplete.

by Basile Starynkevitch (France). Send comments (with constructive suggestions or about any mistakes) by email to basile@starynkevitch.net

Refer also to the R5RS and its index, and to the Guile reference and tutorial.

Symbols

In Scheme, Guile, and all Lisp dialects (including Common Lisp, E-Lisp, Clojure, etc...), symbols are first-class values, and the reader component of guile manages a symbol table to keep them. Conceptually, symbols always exist. Practically, the guile interpreter would add a symbol to its symbol table when reading its first occurrence, and keep there that symbol indefinitely, until it exits. You can input a symbol as an identifier such as fact, foo-bar or even +. When some symbol is input twice, the reader is parsing it twice as the same and identical symbol value.

Beware that some languages, notably JavaScript, use the symbol terminology with a different meaning than Lisp dialects, but inspired by it.

handling symbols

Since Guile symbols are values, you can get a symbol from its string name using string->symbol, so (string->symbol "x") x, and if x was previously known, that same symbol is returned. That same string->symbol primitive is even usable to obtain a symbol difficult to type in usual Guile syntax, e.g. (string->symbol "(a") #{\x28;a}# which is still a weird-looking symbol. The reverse operation, getting as a string the name of a symbol, is of course symbol->string. You can also create a fresh symbol, different of all previously known ones, using gensym to generate a new symbol: for example, (gensym "x") x391 on my guile, but the next time the same expression is evaluated, it might give x438. Of course, the numbers 391 or 438 suffixing these generated symbols are unique and implementation-specific, and you are likely to get different ones.

Symbols are often used as lookup keys in hash tables or a-lists, and that explains a bit why Lisp or Guile excells in symbolic computation such as expert systems shells (re-invented today as business rule management systems), theorem provers or proof assistants, or discovery systems such as Eurisko or Cyc. Notice some similarity of symbols, when used as keys, with JavaScript objects' fields, because in JavaScript ob.field is defined to be the same as ob['field']. However, what JavaScript calls symbols is quite different of symbols in Scheme.

Symbols in Guile are actually quite complex -or at least composite- values with some semi-hidden content: they each have their property list (and this is inspired by Lisp). That property list of a symbol s is associating symbols to their corresponding value, and is generally implemented as some a-list specific to that s. But property lists tend to become deprecated in Guile. Use the binary (symbol-property symb prop) primitive to get in symbol symb the property given by symbol prop. Use (set-symbol-property! symb prop val) to put in symbol symb the property prop associated to value val. To remove from symbol symb the property prop, code (symbol-property-remove! symb prop). Using such arbitrary properties could be useful in frame-based applications, if every frame would be represented by gensym-ed symbols. But recent Guile is proposing a more general object system facilitating such applications.

quotations and evaluations

Since, as an expression, a symbol denotes a variable and is evaluated into the bound value of that variable. So a special syntax is required to get a symbol value without evaluating the variable denoted by it. The quote operator provides it, so (quote foo) foo. That operator is so common that a syntactic sugar provides a shorter notation, so (quote foo) can be abreviated as 'foo and both are exactly equivalent.

The quote operator can even be applied to an entire sub-expression, giving the list representing it. So (quote (+ 2 x)) or '(+ 2 x) (+ 2 x) which is a list of three elements whose first one is the symbol +. You could build the same list using (list '+ 2 'x) and this illustrates that Scheme is homoiconic.

The "reciprocal" of quote is the eval primitive, the evaluation operator. It takes two evaluated operands, the expression to be (re-)evaluated and (optionally) the environment -binding symbols denoting variables to their value- in which you would evaluate it. So (eval '(+ 2 3)) 5 and if some-number has been define-d as 5, (eval '(* 2 some-number) (interaction-environment)) 10, since interaction-environment obviously gives your current interaction environment as a first-class value. Notice that eval is rarely useful in practice, because Scheme has better metaprogramming facilities.

Lists

As explained before, the [linked] list is a major data type of Scheme and is built from pairs. The empty list is called nil and written () (and, counter-intuitively, Scheme considers nil as a true value in tests inside conditional expressions). Notice that lists are constructed from pairs, and that expressions are represented by lists, but arbitrary pairs are generally not forming a proper list, so are not always valid expressions. You can use the dotted pair syntax to make the reader parse a pair - usually as a quoted thing -, for example '(1 . a). Of course pair? is the predicate testing if some value is a pair, and list? is the predicate testing if that value is a proper list made from pairs, the last ending with nil. A pair contains exactly two values: its head, also called its car, and its tail, also called its cdr (pronounced "kooder"). So (car '(1 . a)) 1 and (cdr '(1 . a)) a. Composing these primitives is so common that several abbreviations are available, e.g. (cadr l) is equivalent to (car (cdr l)) and (cdadr l) is equivalent to (cdr (c ar (cdr l))), etc....

The car and cdr names are historical and relate to registers of the IBM 704 computer first running -in the late 1950s- some Lisp.

To make a pair from its head and tail, use the binary cons primitive (as "construct a pair"). To make a list from its elements, use the variadic list primitive. So (list 23 'a "str") (23 a "str") and, since it is a linked list of pairs, could have been obtained from (cons 23 (cons 'a (cons "str" ()))). Of course (car (list 23 'a)) 23 and (cdr (list 23 'a "str")) (a "str").

Pairs can even be mutated in place using set-car! to change the head, and set-cdr! to change their tail.
So (let ( (l (list 'a 'b)) ) (pretty-print l) (set-car! l 'c) l) (c b) after having pretty-printed (a b). However, the Guile interpreter is reading expressions as lists, but their in-place mutation is forbidden, so (set-car! '(a b) 3) is wrong and fails to evaluate.

The map binary primitive takes a procedure and a list and returns the list made by applying that procedure to each element of the second argument, the initial list. So (map (lambda (x) (* x 2)) (list 3 5 10)) (6 10 20).

Vectors

In Scheme, vector values are like arrays in many other languages, and their size is fixed, while their content is mutable and may contain arbitrary values. In particular, they are not resizable as C++ std::vector-s are. See also Guile' section on vectors and R5RS section on vectors.

To test if a value is a vector, use the unary vector? primitive. To create a mutable vector from its elements, use the variadic vector primitive, so (vector 1 'a "str") #(1 a "str"). To create a mutable vector of run-time known size with all elements initialized to the same value, use make-vector, for example (make-vector (+ 2 3) 'x) #(x x x x x).

The #( vector-elements ... ) notation can be even used at REPL time, so you can enter a literal vector constant as #(1 2 3) and that literal is, like string literals are, self-quoting (e.g. evaluates to itself). However, in Scheme such literal vector constants are immutable, for the same reasons literal strings or literal integer constants are immutable : since you don't want the behavior of your code to change during run-time, and some clever Scheme compilers would place such literal constants in the read-only code segment of the produced executable.

To get the length of a vector, use vector-length so (vector-length #(1 a)) 2. To get some n-th element, use vector-ref like (vector-ref #(1 a (+ b 3) "str") 2) (+ b 3). To change some n-th element, use vector-set! so (let ( (v (vector 'a 1 "str")) ) (vector-set! v 1 'b) v) #(a b "str").

to be completed!