Lyn
Old swedish for maple.
Scheme is cool
...but has too many parentheses.
TCL is cool (yes, it really is)
...but littering $
all over the place is annoying.
Can we build something in-between?
Maybe. lyn
is effectively a pre-processor for GNU Guile, adopting TCL's
'everything is a command' attitude while keeping Scheme semantics.
Let's start with an example, examples/guile.lyn
:
define-syntax-rule (for init condition post body) {
expand init
do () ((not (expand condition))) {
expand body
expand post
}
}
define (sum n) {
define s 0
for (define i 0) (< i n) (set! i (1+ i)) {
set! s (+ s i)
}
get s
}
display (sum 1000000)
newline
If you're familiar with Scheme, this should look eerily familiar.
Effectively, a command starts with a thing to apply, follows with some arguments
and ends with a newline or ;
. ()
applies a command to be used as an
argument, and {}
specifies a list of commands. The top-level can also be
thought of as a block of commands.
When encountering a {
, a matching }
must be found before the command can be
finished. {}
can itself contain newlines just fine.
A command can also be continued over multiple lines by using \
, as in
examples/multiline.lyn
:
if (< 10 20) \
{display "math works"; newline} \
{display "what"; newline}
There are two 'builtins' beyond what GNU Guile provides (all GNU Guile functions
can be called as commands), namely get
and expand
. They're effectively the
identity commands, i.e. get
returns the value that was passed into it, and
expand
expands a macro as if expand
weren't there. These are useful because,
again, literally everythin is a command, so if you want to return a value you
can't just write arg
as the last element of a (begin ...)
or whatever,
because that value will be interpreted as a command. Same thing with macros.
Note that values
is effectively the same thing as get
, but I think get
has
a nicer symmetry with let
/set
. macroexpand
is (maybe somewhat
surprisingly) not equivalent to expand
, as macroexpand
can't handle
(define ...)
.
Since ;
ends commands, #
is used for comments instead.
The current implementation just outputs all code into /tmp/output.scm
.
Terrible, I know, but good enough to start playing around with it.
Have a look at that file to see just how closely this syntax matches Scheme,
while (IMO) being easier to read. In particular I find this style to be easier
to write, as I sometimes find that I have to count how many parentheses deep
some expression is. I'm aware that there are tools that help with this, but I
tend to want to have as little between me and my code.
Drawbacks
-
Code generation is arguably not as trivial as with S-expressions. Ideally traditional Scheme functions could be interleaved with this syntax, though I haven't really looked into this option.
-
Certain coding styles (like placing
{
on a new line) are more clumsy to use -
The current implementation parses
()
with multiple commands but produces incorrect Scheme. I'm not sure whether to fix the outputting or disallow multiple commands within()
(that's what{}
is for)
Isn't this all a bit silly?
Yes.
Schemers are presumably used to the parentheses and would prefer them over this, while the TCL crowd likely don't like the shift away from user interfaces. I don't expect anyone to really have any interest in this thing, but if I'd get to choose I would probably want to use a scripting language like this over any other current alternatives, of which there are many excellent ones.
Building and running
make
, after which ./lyn examples/guile.scm
for example. REPL not supported
at the moment, though it probably could be added.