Hi everyone, Recent threads 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 ...": [1] + you can do "... where ...": [2] - uses indentation (though it doesn't have to; see for example [3]) - the function syntax isn't `function(arg, ...)` [1] https://github.com/tonyg/racket-something/blob/4a00a9a6d37777f5aab4f28b03c53555b87e21d7/examples/if-then-else.rkt [2] https://github.com/tonyg/racket-something/blob/4a00a9a6d37777f5aab4f28b03c53555b87e21d7/examples/where.rkt [3] https://github.com/tonyg/racket-something/blob/4a00a9a6d37777f5aab4f28b03c53555b87e21d7/src/something/shell2.rkt (More links at the bottom of this message) 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: #lang something for { x: 1 .. 10 } def y: x + 1 printf "x ~a y ~a\n" x y ... reads as this S-expression: (module something-module something/base (#%rewrite-body (for (block (x (block (1 .. 10)))) (block (def y (block (x + 1))) (printf "x ~a y ~a\n" x y))))) 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. 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: #lang something provide for require for-syntax something/base prefix-in base_ racket/base def-syntax for stx syntax-case stx (block) _ (block (v (block exp)) ...) (block body ...) (syntax (base_for ((v exp) ...) body ...)) def-operator .. 10 nonassoc in-range 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 x y z x y z: x y z { a; b } a a b b and they are read as (x y z (block a b)) 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: a b (c d: e f) = (a b (c d (block e f))) a b (c d e f) = (a b (c d e f)) Conversely, long lines may be split up and logically continued over subsequent physical lines with a trailing `\`: a b c \ d \ e = (a b c d e) Semicolons may also appear in vertically-laid-out suites; these two are equivalent: x y z a b; c d x y z { a; b; c; d } 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: x y z: a b c d e reads as (x y z (block (a b (block (c d) e)))) Square brackets are syntactic sugar for a `#%seq` macro: [a; b; c; d e f] → (#%seq a b c (d e f)) [ → (#%seq a (b (block c)) (d e f)) a b c d e f ] Forms starting with `block` in expression context expand into `match-lambda*` like this: { pat1a pat1b exp1a exp1b pat2a exp2 } → (match-lambda* [(list pat1a pat1b) exp1a exp1b] [(list pat2a) exp2]) The `map*` function exported from `something/lang/implicit` differs from `map` in `racket/base` in that it takes its arguments in the opposite order, permitting maps to be written map* [1; 2; 3; 4] item: item + 1 map* [1; 2; 3; 4] item: item + 1 map* [1; 2; 3; 4]: item: item + 1 map* [1; 2; 3; 4] { item: item + 1 } A nice consequence of all of the above is that curried functions have an interesting appearance: def curried x:: y:: z: [x; y; z] require rackunit check-equal? (((curried 1) 2) 3) [1; 2; 3] Anyway, thought it worth mentioning. A few more links: - The repository: https://github.com/tonyg/racket-something - Implementation of the shell support: https://github.com/tonyg/racket-something/blob/4a00a9a6d37777f5aab4f28b03c53555b87e21d7/src/something/shell.rkt - An example "shell script": https://github.com/tonyg/racket-something/blob/4a00a9a6d37777f5aab4f28b03c53555b87e21d7/examples/sh.rkt - Another small example "shell script", which I used while working on the module today: #lang something/shell def in-hashlang? re :: source-filename zero? (cat source-filename | grep -Eq (format "^#lang.*(~a).*" re)) (find "." -iname "*.rkt" | port->lines |> filter (in-hashlang? "something") |> ag -F "parameterize") Cheers, Tony -- You received this message because you are subscribed to the Google Groups "Racket Users" group. To unsubscribe from this group and stop receiving emails from it, send an email to racket-users+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/racket-users/f45aeec0-e1ee-5109-5ce6-255c2d05f87f%40leastfixedpoint.com.