Logo

0x5a.live

for different kinds of informations and explorations.

GitHub - brandonbloom/metaclj: Staged compilation for Clojure through environment & special-form aware syntax-quoting.

Staged compilation for Clojure through environment & special-form aware syntax-quoting. - brandonbloom/metaclj

Visit SiteGitHub - brandonbloom/metaclj: Staged compilation for Clojure through environment & special-form aware syntax-quoting.

GitHub - brandonbloom/metaclj: Staged compilation for Clojure through environment & special-form aware syntax-quoting.

Staged compilation for Clojure through environment & special-form aware syntax-quoting. - brandonbloom/metaclj

Powered by 0x5a.live ๐Ÿ’—

Meta-Clojure

Overview

Meta-Clojure provides staged compilation for Clojure. It includes a form of syntax quoting that is aware of both local environments and special-forms. Among other things, this makes many macros easier to write. Perhaps more importantly, it simplifies control over when code gets evaluated or compiled.

Usage

(require '[metaclj.core :refer [defmeta defbn syntax] :as meta])

Meta-Macros

The defmeta form is analogous to defmacro, but is expected to return Syntax objects (forms plus their environments) instead of plain forms.

(defmeta my-if [test then else]
  (syntax (if test then else)))

Note that you don't need to unquote any of the parameters to if, since the syntax form is aware of the meta-macro's environment.

Call-By-Name

Since it's common for macros to have a body that always templates code with a syntax-quoter, the convenience macro defbn provides a way to create "call-by-name" macros:

(defbn my-if [test then else]
  (if test then else))

Both versions of my-if have correct "lazy" behavior: they will only evaluate one arm of the conditional.

Staged Compilation

The meta/do macro will perform meta-quoting on zero or more forms, then evaluate each of them:

(meta/do 1 2 3)
;;=> 3

Combined with unquoting, this enables you to perform arbitrary computation at compile time:

(let [x 2 y 4]
  (meta/do ~(+ x y)))
;;=> 6

Unquoting is syntax aware and provides automatic splicing:

(let [args (syntax 2 4)]
  (meta/do ~(+ args)))
;;=> 6

You can use function expressions to defer computation. Note that the unquoted expression will still be evaluated at compile time:

(let [x 2 y 4]
  (meta/do (fn [] ~(+ x y))))
;=> #<Fn@32b2ad39 user/eval15784$fn__15788>

You can prove this to yourself by using meta/translate, which is a cousin of macroexpand-all:

(let [x 2 y 4]
  (meta/translate (fn [] ~(+ x y))))
=> ((fn* ([] 6)))

Note that the returned value is wrapped in a seq, since Meta-Clojure uniformly supports multiple expressions with implicit splicing:

(let [x (syntax 2 3)]
  (meta/translate 1 x 4))
;=> (1 2 3 4)

Status

  • The comments at the bottom of core.clj and the code in core_test.clj form my testbed.
  • Many known bugs and incomplete behavior.
  • Some special forms not yet supported: case, deftype, and reify.
  • No progress yet on Exotypes
  • Use of clojure.core/eval is unavoidable at the top level, but it could be compiled away for more interior forms.
  • Maybe someday I'll revive EClj and build its compiler on Meta-Clojure.

References

License

Copyright ยฉ 2016 Brandon Bloom

Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.

Clojure Resources

are all listed below.

Resources

listed to get explored on!!

Made with โค๏ธ

to provide different kinds of informations and resources.