Home
修改页面(Jason E. Aten,6 年之前)

zygomys is a dialect of LISP, but one that contains significant built-in infix syntax support, making it exceptionally easy to read. 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.

Sandboxing the repl

When you don't want script to read/write the filesystem or issue system calls, you can sandbox it.

Start the repl with zygo -sandbox. (See also the -quiet flag).

When embedding use the NewZlispSandbox() method in place of NewZlisp().

Differences from traditional LISP syntax

  • Using = for assignment and multiple assignment

The = sign represents assignment. To support Go-like multiple assignment, the = assignment operator supports multiple values on either side of the = sign. After the following assignment,

(a b c = 1 "two" 3)

a will be 1, b will be "two", and c will be 3.

  • Using dot-symbols for nested member selection

Dot-symbols 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> 

  • 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"))
>
> // or using dot-symbol syntax
> acre100.friend
`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. The newer dot-symbol syntax now offers the same functionality with even more compact notation. Both are retained as one may be easier to generate programmatically.
> (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 use '=' for assignment.

  • 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.