LispSyntax.jl: A clojure-like lisp syntax for julia
This package provides a lisp-to-julia syntax translator with convenience macros that let you do this:
lisp"(defn fib [a] (if (< a 2) a (+ (fib (- a 1)) (fib (- a 2)))))" @test lisp"(fib 30)" == 832040 @test fib(30) == 832040
LispSyntax.jl is implemented as an expression translator between lisp/clojure-like syntax and julia's AST. Julia's compiler, JIT and multiple-dispatch infrastructure is used for code generation and execution. Because of this, LispSyntax.jl is not really clojure or lisp in most meaningful ways. The semantics are entirely julia-based (which are very similar to scheme/lisp in many ways). The net result is that LispSyntax.jl is really an alternative S-expression-like syntax for julia, not an implemention of clojure or lisp.
(def symbol init)
(defn symbol [param*] expr*)
(defmacro symbol [param*] expr*)
(lambda [param*] expr*)
(fn [param*] expr*)
(let [binding*] expr*)
(while test expr*)
(for [binding*] expr*)
- Symbol names cannot have -, *, /, ? ... - Julia symbol naming is used for
everything, as a result, Julia syntax restrictions are maintained
- Reference to global variables in function scopes - Julia requires
declaration of global symbols that are referenced in function
scope. Because of this functions need to declare which symbols are
global. This is done via the special form
- Binding forms not implemented - Clojure has very awesome
destructuring binds that can used in most special forms requiring
fnparameter lists, etc.). This is not currently implemented.
- Lack of loop/recur - Currently, this is not implemented. As with Clojure, julia does not currently support TCO, so something like this may be needed (but a macro-implementation of tail call rewriting may be more appropriate for julia).
- Optional typing - Currently not implemented.
- Named functions are julia methods - For efficiency, functions defined with
defnare translated to normal julia
functionexpressions. This means the act as named lambdas in local scope.
- Method definition - Also not currently implemented. If implemented it will probably not be a full implementation of Clojure's sophisticated dispatch system.
- Macros differences - Macros defined in
LispSyntax.jllook like standard Lisp macros but because expressions are special objects in julia, S-expressions returned from macros require a special translation step to generate julia expression trees. The result is that
LispSyntax.jlmacros are directly translated into Julia macros and must be called via special syntax (e.g.
(@macro expr)). Macro hygiene follows the Julia approach of hygenic-by-default with explicit escaping using
esc. This is the opposite of Clojure's macros which use explicit hygiene with specially named variables.
- Julia's string macro dispatch not supported (yet) - for macros
@r_strwhich in Julia can be called via
r"", it is currently necessary to call these via standard macro syntax:
LispSyntax.jl provides a convenience REPL, alleviating one from having to
lisp"( ... )" for each top level expression. In order to use REPL
mode, simply initialize it:
julia> using LispSyntax julia> LispSyntax.init_repl() REPL mode Lisp Mode initialized. Press ) to enter and backspace to exit.
At this point, type
), and you're ready to Lisp:
jλ> (* 2 (reduce + (: 1 6))) 42 jλ> (defn fib [a] (if (< a 2) a (+ (fib (- a 1)) (fib (- a 2))))) fib (generic function with 1 method) jλ> (fib 10) 55
To return to the Julia prompt, simply type the backspace type or
Ctrl-C. Once there, you'll still have access to the fuctions you
julia> fib fib (generic function with 1 method) julia> fib(10) 55
You may also create a customized REPL.
- Support for exceptions: this is straight forward but not currently implemented.
- Optional typing to support method definition
- Structs and aggregate types
- Special dispatch for string macro forms
- import vs. using vs. include -- only
usingis currently implemented and confusingly, it matches Clojure's import form.
- varargs and named arguments