Product Promotion
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.
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
, andreify
. - 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.
Made with โค๏ธ
to provide different kinds of informations and resources.