Musings on Scheme, CHICKEN, and a hypothetical language

Posted on Tue Apr 30, 2019

I have ported some old Scheme code under CHICKEN. The result is a small set of packages: MMCK Checks; MMCK Fectors; MMCK PFDS, which is still under development. It was somewhat fun; I still enjoy reorganising code.

I’m making little progress understanding CHICKEN’s programming features and ecosystem. Native executables and shared libraries are good, but I notice that they are somewhat bigger in file size that I thought; not really a problem (being that I am used to the huge files of Vicare), but still I wonder what’s in there to make binary code so big.

I’m starting to get the hang of exceptional–condition handling and exceptional–condition objects; I can confirm my first impression: I do not like it. The handlers and condition objects defined by r6rs are better in my humble opinion; and maybe I’m not the only one. In the small amount of CHICKEN code I have seen so far, written by people with much more experience than myself, there is very little design of exceptional–condition object–types; mostly, there is just a raw error call in which the exception description is the message string: not an object we can use programmatically to react differently to different error kinds.

So far, the one CHICKEN egg I cannot live without is COOPS: a library implementing CLOS–like language extensions. It is incomplete, more metaobject protocol must be implemented. Its documentation is ugly and incomplete. It is based upon ScmObj by Dorai Sitaram, a code base I know because I, too, have used it as base to implement extensions for Vicare.

In the past, when approaching new Scheme implementations, I built a library of language extensions to adapt the programming environment to my needs; I will try the same with CHICKEN and see what will happen.

How a statically typed Scheme language could look

Lately I’ve been thinking about how a statically typed Scheme language could be implemented to suit my taste. Would it still be a “Scheme” language, or would it be a non–Scheme dialect of Lisp? Maybe the latter.

Let’s consider the function string->number as defined by r6rs:

(string->number "123")  ⇒ 123
(string->number "ciao") ⇒ #f

this behaviour allows us to write a typical Scheme idiom:

(or (string->number arg)
    (raise-an-error))

A type signature for this function would be something like:

(<string>) -> (or <number> <false>)

no exception is raised if the argument is not a number. I do not like this complex type signature: I want a single, known, type for each argument and each return value. But then the typical Scheme idiom above would not be possible.

How would one implement string->number then? The simpler way would be to raise an exception:

(try
   (let* ((arg (do-this-thing))
          (num (string->number arg)))
     (do-that-thing))
  (catch E
    ((&string-does-not-represent-a-number)
     (react-this-way))
    (else
     (react-that-way))))

nowadays every programmer can understand this code, even if he/she is not a Scheme adept. A problem with raising exceptions is that it is hard to distinguish the form that raised the exception: which chunk of code inside the body raised &string-does-not-represent-a-number?

A more general design is to hand an escape function as second argument: if the input string cannot be converted to a string, string->number calls the escape function, let’s say with no arguments:

(with-escape-handlers
   (let* ((arg (do-this-thing))
          (num (string->number arg escape-string-conversion)))
     (do-that-thing))
  ((escape-string-conversion)
   (react-this-way)))

we can imagine the syntax with-escape-handlers to expand into something like (untested):

(call/cc
    (lambda (return)
      (call/cc
          (lambda (escape-string-conversion)
            (let* ((arg (do-this-thing))
                   (num (string->number arg escape-string-conversion)))
              (return (do-that-thing)))))
      (react-this-way)))

not easy to understand if you are not into Scheme; somewhat messy, when multiple functions can call multiple escape functions; very general because each form inside the body of with-escape-handlers can cause the execution flow to branch into a different execution path, but still multiple forms can call the same escape function.

An even more general design is to hand a continuation function as second argument:

(let* ((arg (do-this-thing))
       (num (string->number arg (lambda (exception-object)
                                  (react-this-way)))))
  (do-that-thing))

and string->number should tail–call its continuation if a conversion error occurs. Weird…

Enough for today.