-
Notifications
You must be signed in to change notification settings - Fork 81
Home
zygomys is a dialect of LISP. It is designed as an embedded extension language for Go. It is implemented in pure Go as a bytecode interpreter. As a result, the interpreter can be compiled or cross-compiled for any platform Go runs on.
The zygo library REPL can easily be used either standalone or embedded as a library in your Go application. This facilitates creating an interactive command-line for your application.
- dot-symbols
Dot-symbols are one of the most innovative, modern features of zygomys. Dot-symbols avoid
the need for macros in many cases, provide object-oriented style compact notation,
and allow assignment with =
to be expressed naturally.
Dot-symbols are simply symbols that start with dot .
For example, these are dot-symbols:
.hello
.hugs!
.welcome.home
In contrast, regular symbols are not allowed to start with a dot.
What is special about dot-symbols is that they typically evaluate just to themselves. For example, at the repl, if we ask for the value of a dot symbol, we get back itself:
zygo> .a
.a
zygo> (defn f [d] d)
zygo> (f .a)
.a
zygo>
Notice how the dot-symbol .a
passed through the function call completely unchanged.
However things change when dot-symbols are called like a function.
zygo> (.a) ;; refers now to the value of `a`, but it's not yet been defined
error in __main:36: symbol `a` not found
zygo> (.a = 10) ;; bind `a` to 10. Equivalent to (def a 10).
10
zygo> a
10
zygo> (.a) ;; not a function, so just gets the value of `a`
10
zygo>
As the example above shows, when dot-symbols are used as the function name (first-position) in a function call, then they evaluate to their underlying reference.
- Using dot-symbols for nested member selection
Dot-symbols that have multiple dots act like path descriptors for referencing nested objects.
Like Go/C++/Java, dot-symbols let one easily pick out members of nested objects:
zygo> ;; make an nested object
zygo> (def sn (snoopy asst: (hornet forecast: (weather desc:"sunny"))))
(snoopy asst: (hornet forecast: (weather desc:"sunny")))
zygo> (.sn.asst)
(hornet forecast: (weather desc:"sunny"))
zygo> (.sn.asst.forecast)
(weather desc:"sunny")
zygo> (.sn.asst.forecast.desc)
`sunny`
zygo>
- Using dot-symbols for method calls
A object-oriented-style call like (.baby cry)
will call the method cry
with the de-referenced
value of baby as its first argument. This works for (.nursery.baby cry arg1 arg2)
as well. Here we assume that cry
is a function defined in zygomys. If it refers to a bulitin Go extension function, then the method will get the dot-symbol instead of the dereference. This allows builtin functions to easily act like macros when need be.
Notice that (.baby)
alone, without any arguments, is just the value of baby
.
zygo> (def baby 12)
12
zygo> (.baby)
12
zygo> ;; but note it must refer to a function if given arguments
zygo> (.baby "hi")
error in __main:1: dot-symbol '.baby' was followed by non-function '"hi"'.
zygo>
Let's illustrate object-oriented style method invocation. Notice that currently methods can be associated with any type of object. This may evolve in the future. (We are considering adding an optional type discipline.)
zygo> (defn myMethod [self arg1] (concat "self:" (str self) " called with arg1:" (str arg1)))
zygo>
zygo> (def han 'solo)
solo
zygo> (def chew 'bacca)
bacca
zygo> (.chew myMethod "rocks")
`self:bacca called with arg1:"rocks"`
zygo> (def obj "I-am-an-object")
`I-am-an-object`
zygo> (obj myMethod "first-param") ;; no dot-symbol, therefore no call of myMethod in the 2nd position.
error in __main:14: obj is not a function
zygo> (.obj myMethod "first-param")
`self:"I-am-an-object" called with arg1:"first-param"`
zygo>
- Minor syntax improvement: ^ replaces `
In macros, templates (syntax-quotes) are introduced by the caret ^ instead of the backtick. This allows the convenient use of the backtick to delimit verbatim, newline and double quote containing strings, just as in Go.
So instead of this:
(defmac require [sympath]
`(source (sym2str (quote ~sympath)))) ; not supported
Do this instead:
(defmac require [sympath]
^(source (sym2str (quote ~sympath)))) ; way better!
- Raw string literals using a pair of backticks
Just as in Go, you may submit strings like this:
> (def a `hello
readable
"quotes"`)
> a
"hello\n readable\n\ "quotes\""
>
-
defmap
to create records.
The defmap macro defines a new record type that is a hash table with a new name. Field definition order is always preserved upon display.
> (defmap ranch)
> (def lazy8 (ranch cowboy:"Jim" cowgirl:"Jane"))
> lazy8
(ranch cowboy:"Jim" cowgirl:"Jane")
>
- clojure like field access with the ':' colon operator
Instead of writing (hget acre100 :friend)
, instead there is the briefer (:friend acre100)
> (defmap wood)
> (def acre100 (wood tractor:"Bessie" combine:"Steve" friend:"Eeyore"))
> (assert (== (:friend acre100) "Eeyore"))
>
- clojure like threading with (-> hash field: field2: ...) for picking out nested fields. Note a difference from clojure however: the colon is always on the trailing (right) side of the symbol. Note that a space after the -> operator is required.
> (defmap ranch)
> (def hogwild (ranch cowboy:"Harry" cowgirl:"Hermonie"))
> (defmap bunkhouse)
> (hset! hogwild bunk1:(bunkhouse bed1:"Luciuos" bed2: "Dumbledore"))
> (defmap closet)
> (hset! (:bunk1 hogwild) closet1:(closet broom:"Nimbuz2"))
> (assert (== (-> hogwild bunk1: closet1: broom:) "Nimbuz2"))
> (assert (== (-> hogwild bunk1:closet1:broom:) "Nimbuz2"))
> hogwild
(ranch cowboy:"Harry" cowgirl:"Hermonie" bunk1: (bunkhouse bed1:"Luciuos" bed2:"Dumbledore" closet1: (closet broom:"Nimbuz2")))
>
-
==
instead of=
for equality checking.
Notice this in the repl snippet above. Its just less confusing to eyes accustomed to more common Go, C, etc. And we may wish to use '=' later for useful purposes.
- Symbols can be invoked directly
Like scheme, in a function call, we evaluate the (1st) position just like all the other positions. The function position isn't special. This means if a symbol refers to a function's name, you can use that symbol in the first (function name) position, without fuss. No (funcall sym)
needed as in lisp. There is a single namespace for all symbols.
> (def myprintsymbol 'printf)
> (myprintsymbol "in hex: %x\n" 32) ;; invokes printf
in hex: 20
>
See also this reference.