#lang something: An alternative syntax for Racket
Sun 21 Jul 2019 17:03 BST
Recent discussions (e.g.
1,
2)
about potentially revising Racket syntax for Racket2 have reminded me
I never properly announced #lang something
,
an experiment from back in 2016.
The main idea is S-expressions, but with usually-implicit parentheses and support for prefix/infix/postfix operators. Indentation for grouping is explicitly represented in the S-expression returned from the reader.
- (+) keeps a semi-structured input format: reader yields ordinary syntax
- (+) macros Just Work
- (+) you can do “if … then … else …”: an example
- (+) you can do “… where …”: an example
- (-) uses indentation (though it doesn’t have to; see for example this module)
- (-) the function syntax isn’t
function(arg, ...)
(More links at the bottom of this post.)
In addition to the reader, #lang something
provides a small
selection of special forms that take advantage of the new syntax, and
#lang something/shell
adds Unix-shell-like behaviour and a few
associated utilities.
This program:
… reads as this S-expression:
The #%rewrite-body
macro, together with its companion
#%rewrite-infix
, consults an operator table, extendable via the
def-operator
macro, to rewrite infix syntax into standard prefix
S-expressions using a Pratt parser.
The block
syntax has many different interpretations. It has a macro
binding that turns it into a Racket match-lambda*
, and it is used as
literal syntax as input to other macro definitions.
For example, here’s one possible implementation of that for
syntax:
Notice how the block
S-expressions are rewritten into a normal
S-expression compatible with the underlying for
from racket/base
.
Generally, all of these forms are equivalent
and they are read as
and are then made available to the normal macro-expansion process (which involves a new infix-rewriting semi-phase).
Colons are optional to indicate a following suite at the end of an indentation-sensitive line. Indentation-sensitivity is disabled inside parentheses. If inside a parenthesised expression, indentation-sensitivity can be reenabled with a colon at the end of a line:
Conversely, long lines may be split up and logically continued over
subsequent physical lines with a trailing \
:
Semicolons may also appear in vertically-laid-out suites; these two are equivalent:
Suites may begin on the same line as their colon. Any indented subsequent lines become children of the portion after the colon, rather than the portion before.
This example:
reads as
Square brackets are syntactic sugar for a #%seq
macro:
Forms starting with block
in expression context expand into
match-lambda*
like this:
The map*
function exported from something/base
differs from map
in racket/base
in that it takes its arguments in the opposite order,
permitting maps to be written
A nice consequence of all of the above is that curried functions have an interesting appearance:
A few more links:
#lang something/shell
in action: https://asciinema.org/a/83450- Implementation of
#lang something/shell
- An example “shell script”
Another small example “shell script”, which I used while working on the module today: