Last active
July 19, 2020 23:54
-
-
Save Solaxun/5db71e31f6dd7e2719f95ed86b2734b5 to your computer and use it in GitHub Desktop.
Toy implementation of reify, doesn't check the methods are actually part of the protocol, doesn't do namespacing, etc. Just mimicks the interface.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;;; | |
;;; implementation | |
;;; | |
(defn build-fn [[mname args body]] | |
{(keyword mname) (list 'fn mname args body)}) | |
(defn make-generic-fn [[mname args body]] | |
`(defn ~mname ~args | |
(let [f# (get ~(first args) ~(keyword mname))] | |
(f# ~@args)))) | |
(defmacro reifoo [proto-name & fns] | |
`(do ~@(map make-generic-fn fns) | |
~(apply merge (map build-fn fns)))) ; returns map of fns which | |
;;; ; close over locals at runtime | |
;;; usage | |
;;; | |
(defn foo1 [y] | |
(reifoo Foo | |
(foo [_ x] (+ x y)) | |
(boo [_ x] (* x y)))) | |
(def foo1-impl (foo1 85)) | |
(foo foo1-impl 10) | |
(boo foo1-impl 10) | |
(defn foo2 [y] | |
(reifoo Foo | |
(foo [_ x] (- x y)) | |
(boo [_ x] (/ x y)))) | |
(def foo2-impl (foo2 85)) ; foo2-impl is a map of kw->funcs | |
; since the map and funcs were created | |
(foo foo2-impl 10) ; when the call to foo2 occured, the | |
(boo foo2-impl 10) ; funcs close over that environment | |
(def foo3-impl | |
(let [y 200] | |
(reifoo Foo | |
(foo [this x] (str x y)) | |
(boo [this x] (vector x y))))) | |
(foo foo3-impl "foo") | |
(boo foo3-impl :boo) | |
(comment Example macroexpansion: | |
calling: | |
(reifoo Foo | |
(foo [_ x] (+ x y)) | |
(boo [_ x] (* x y))) | |
yields: | |
(do | |
(clojure.core/defn | |
foo | |
[_ x] | |
(clojure.core/let | |
[f__1625__auto__ (clojure.core/get _ :foo)] | |
(f__1625__auto__ _ x))) | |
(clojure.core/defn | |
boo | |
[_ x] | |
(clojure.core/let | |
[f__1625__auto__ (clojure.core/get _ :boo)] | |
(f__1625__auto__ _ x))) | |
{:foo (fn foo [_ x] (+ x y)), :boo (fn boo [_ x] (* x y))})) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The generic functions created as part of the macroexpansion will end up receiving the map of functions returned from
reifoo
on line 70 as their first argument. Then, they look up the keyword corresponding to their function name inside this map, and call the resulting function with the map and any arguments. The functions inside the map on line 70 are created where and when the macro is called from, e.g. globally, inside a let, inside a defn, etc. and so they will capture any bindings in the context of that environment (they are lexical closures).