Early draft
Some example might be broken, but should give a quick glimpse of the features of Gambit Scheme.
So far, there is no crash course for Gambit Scheme. The following readings will help:
Reading the next questions will give you an overview of Gambit Scheme specifics.
Use the gsi
command to start the REPL:
% gsi Gambit v4.9.3-1371-g1dbeaabd > bytevector-u8-ref #<procedure #2 u8vector-ref> > (+ 40 2) 42 > *** EOF again to exit
Hit twice Ctrl+D
to exit the REPL.
Create a file called for example main.scm
with the following
content:
#!
(import (scheme base))
(import (scheme process-context))
(define (first-command-line-argument)
(cadr (command-line)))
(define (second-command-line-argument)
(caddr (command-line)))
(display (+ (string->number (first-command-line-argument))
(string->number (second-command-line-argument))))
(newline)
Mind the #!
at the beginning of the file. Then run the following
gsi
command:
% gsi main.scm 40 2 42
Given the following program name main.scm
:
#!
(import (scheme base))
(import (scheme process-context))
(define (first-command-line-argument)
(cadr (command-line)))
(define (second-command-line-argument)
(caddr (command-line)))
(display (+ (string->number (first-command-line-argument))
(string->number (second-command-line-argument))))
(newline)
You can compile it to a native binary executable called main
with the following command:
% gsc -exe main.scm
Then you can run the produced program with the following command:
% ./main 1331 6 1337
In Scheme parlance, a library (also known as module) is a way to gather Scheme forms (procedures, macros or constants) inside a file, it is defined with four components:
(scheme base)
Here is an example library called (mylib)
inside a file called
mylib.sld
that exports a constant called magic:
(define-library (mylib)
(export magic)
(import (scheme base))
(begin
(define magic 42)))
Mind the begin
that wraps the body of the library where magic
is
defined.
Both the interpreter gsi
, and compiler gsc
will take directories
and source files as arguments. Such as gsi dir1/ dir2/ program.scm
will look for libraries inside dir1/
and dir2/
and execute
program.scm
.
Mind the fact that the default behavior is to NOT look for libraries in the current working directory. To look for libraries in the current working directory you need to specify it in the command line, such as:
gsi . program.scm
Mind the the dot .
that describe the current directory.
Gambit is its own package manager, in the sense you can distribute and
re-use libraries directly with gsi
or gsc
because if you allow
Gambit, Gambit can install and run program relying on libraries from
remote git repositories directly from the command line.
Here is an example program that will re-use the hello library:
#!
(import (scheme base))
(import (github.com/gambit/hello@1.0))
(hi "schemer!")
The github.com/gambit
organization is in the default allowed list of
URLs, so Gambit will download libraries from there without
asking. Other URLs require your acknowledgement.
Gambit support any remote git repository as a source of libraries.
Here is an example from the above hello
library:
(define-library (github.com/gambit/hello test)
(import (..)) ;; relative import of hello (preserves the version)
(import (_test)) ;; for test-equal and test-error-tail
(import (gambit)) ;; for lambda, with-output-to-string, and
;; wrong-number-of-arguments-exception?
(begin
(test-equal "hello you!\n"
(with-output-to-string (lambda () (hi "you"))))))
You can run the tests directly by passing the test.scm
as argument
of gsi
such as:
% gsi test.scm
TODO
The easiest way to target JavaScript, nodejs or the browser, is via
docker. The following script will compile Scheme to JavaScript the
input FILENAME.scm
into FILENAME.js
:
#!/bin/sh
docker run -v $(pwd):/mnt --rm -it schemers/gambit:head gsc -:r7rs -target js -exe /mnt/$1
tail -n +2 $(basename $1 .scm) > "$(basename $1 .scm).js"
rm -f $(basename $1 .scm)
TODO: document limitations if any (macro support?)
The easiest way to call JavaScript from Scheme is to use a Scheme
Infix eXpression (SIX). Where the backslash \
change the reader
syntax into infix notation, and the backtick allows to include scheme
variables inside the infix expression. It works in a way that is
similar to quasiquote and unquote:
(import (_six js))
(define magic 42)
\console.log(`magic)
Objects are automatically converted back and forth between Scheme and
JavaScript. That behavior can be controlled with the JavaScript
function foreign
.
TODO: document type conversions
ref: https://zenodo.org/record/4711425
TODO: Use javascript function foreign
?
.js
file for a big Scheme project?TODO: Use static compilation...
fetch
?To do AJAX, also known as XHR, and under the new name fetch
,
you can do something such as the following:
\fetch("https://example.org").then(function(response) { return response.text() })
Mind the fact that the default behavior is to resolve JavaScript
promises. Hence it will block the current Gambit thread. You might
want do the call to fetch
inside a dedicated Gambit thread, or wrap
the promise with foreign
.
Gambit Termite is a library that provides an actor model similar to Erlang.
set!
be used in code using Termite?No.
Yes, but it is behind a configuration flag.
Yes.
Yes!
Gambit implement lightweight threads, also known as user-space threads that support non-blocking network input-output. Gambit threads are different from both green threads and POSIX threads.
Compared to green threads: Gambit threads are also cheap. Like green
threads, Gambit threads will not block on network input or
output. Unlike green threads, Gambit threads can be preempted because
their priority became higher. Unlike most green thread approaches,
Gambit threads do not require a special syntax such as yield
,
async
or await
to yield control, that is procedures are
non-colored.
Compared to POSIX threads: Gambit threads are cheap. Gambit threads will not block on network input-output if you rely on builtin network procedures.
Gambit master
includes behind a configuration flag, the ability to
run Gambit threads on all cores. NB: Without multicore support, you
need to rely on multiple Gambit processus to take advantage of
multiple core, given it is easy to communicate data and code to a
local or remote process, the ability to run on multiple cores, is
merely an optimization.
Gambit threads are exposed with an interface very similar to POSIX threads. On top Gambit threads is implemented the actor model with the Termite library.
It is equivalent [citation needed].
Gambit support define-macro
, and syntax-rule
. Also, syntax-case
is behind a configuration flag.
Promise
?Use Gambit threads.