As part of one of the projects we’ve been pushing at Canonical, I spent a few days researching about the possibility of extending a compiled Go application with a tiny language that would allow expressing simple procedural logic in a controlled environment. Although we’re not yet sure of the direction we’ll take, the result of this short experiment is being released as the twik language for open fiddling.
The implementation is straightforward, with under 400 lines for the parser and evaluator at the time of this writing, and under 350 lines in the default functions provided for the language skeleton: var, func, do, if, and, or, etc.
It also comes with an interactive interpreter to play with. You can install it with:
$ go get gopkg.in/twik.v1/cmd/twik
This is a short sample session:
> (var x 1) > x 1 > (set x 2) > x 2 > (set x (func (n) (+ n 1))) > x #func > (x 1) 2 > (func inc (n) (+ n 1)) #func > (inc 42) 43
Another one demonstrating the lexical scoping:
> (var add . (do . (var n 0) . (func (m) (set n (+ n m)) n) . ) . ) > (add 5) 5 > (add -1) 4 > n twik source:1:1: undefined symbol: n
New functionality may be plugged in by providing Go functions. For example, here is a simple printf function:
func printf(args []interface{}) (interface{}, error) { if len(args) > 0 { if format, ok := args[0].(string); ok { _, err := fmt.Printf(format, args[1:]...) return nil, err } } return nil, fmt.Errorf("printf takes a format string") } func main() { ... err = scope.Create("printf", printf) ... }
It can now greet the world:
$ cat test.twik (func hello (name) (printf "Hello %s!\n" name) ) (hello "world") $ time ./twik test.twik Hello world! ./twik test.twik 0.00s user 0.00s system 74% cpu 0.005 total
This would be great for implementing dynamic rules engine in go.
My main gripe with s-expression syntax is not the overabundance of parenthesis, but the lack of syntactic clues about rules of evaluation. For example, given the expression (foo bar baz) the human reader does not know whether bar and baz will be evaluated. If foo is a function, bar and baz will be evaluated before the function is applied. If foo is not a function, all bets are off. A simple enhancement to s-expression syntax is to use parenthesis only for function application, and braces or brackets for other constructs. Here is an example adapted from yours:
{func inc [n] {+ n 1}}
#func
> (inc 42)
43
That line should be:
{func inc [n] (+ n 1)}
It is now an even better example of how using other braces enhances readability.