aboutsummaryrefslogtreecommitdiff

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.