Ok, let's try to get this straight: my final intent is to provide a macro as an API to users which will look like:
(defscript [a b]
(println a))
The result has to be an instance of a Script protocol, which looks like:
(defprotocol Script
(run [this model]))
The idea being that the first argument to defscript is a list of symbols that needs to be bound to correspondent keys in the model:
(.run (defscript [a b] (println a)) {:a 1}) ;; yields 1
I can't come up with any code that can effectively produce such effect, as I'm constantly hitting a wall when trying to use the model parameter, since at macro expansion time it's just a symbol:
(defmacro invoke-
[params model body]
(let [p (flatten (map (fn [x] [x (model (keyword x))]) params))]
`(let [~#p]
~body)))
(defmacro defscript
[params & body]
`(reify Script
(run [~'this ~'model]
(invoke- ~params ~'model ~#body))))
invoke- works fine if called directly:
(invoke- [a] {:a 1} (println a)) ;; prints 1
but it doesn't work when used within defscript as model can't be expanded correctly:
(.run (defscript [a] (println a)) {:a 1}) ;; prints nil
How can I get past this point and glue the pieces together?
It seems that basically, your argument vector is a shortcut for a destructuring binding:
(defscript [a b] body) -> (reify Script (run [this {:keys [a b]}] body))
That way, model is destructured at run time, as it should be.
Related
I'm experimenting with clojure macros, and wondered what I might be doing wrong?
I have a simple example, of attempting to dynamically create functions based on a map.
For example:
(def units {:cm 100
:mm 1000
:m 1
:km 1/1000})
(defn m-to-unit-helper [[k v]]
(let [f (symbol (str "to-" (name k)))]
`(defn ~f [m#] (* ~v m#))))
(defmacro m-to-units [units-map]
(let [funcs (map m-to-unit-helper units-map)]
`(do ~#funcs)))
; complains with: Don't know how to create ISeq from: clojure.lang.Symbol
(m-to-units units)
; To try and debug
(defn debug [units-map]
(let [funcs (map m-to-unit-helper units-map)]
(clojure.pprint/pprint `(do ~#funcs))))
; see below
(debug units)
The macro doesn't work, but the debug output look like it should create the correct structure:
(do
(clojure.core/defn
to-mm
[m__32709__auto__]
(clojure.core/* 1000 m__32709__auto__))
(clojure.core/defn
to-m
[m__32709__auto__]
(clojure.core/* 1 m__32709__auto__))
(clojure.core/defn
to-cm
[m__32709__auto__]
(clojure.core/* 100 m__32709__auto__))
(clojure.core/defn
to-km
[m__32709__auto__]
(clojure.core/* 1/1000 m__32709__auto__)))
Any advice would be greatly appreciated. Thanks.
m-to-units is a macro which means that every parameter will be passed without being evaluated, meaning that inside the macro the value of units-map is actually the symbol units.
Now, if you pass the the map directly, it'll work as intended:
(m-to-units {:mm 1000, :m 1, :cm 100, :km 1/1000})
;; => #'user/to-km
(to-cm 10)
;; => 1000
What you could do - although I'd consider it bad practice - is using eval to get the actual value of the units map, no matter whether it's passed as a map or via a symbol:
(defmacro m-to-units
[units-map]
(let [funcs (map m-to-unit-helper (eval units-map))]
`(do ~#funcs)))
(m-to-units units)
;; => #'user/to-km
Probably the title is not 100% correct, but let me show you the issue:
(defmacro nvp!
[n width height]
`(q/defsketch (symbol (str (name '~n) "-viewport" ))
:title (name '~n))
In short: there's a macro called defsketch (it's part of the lib quil, but that does not matter). It's signature is basically defsketch [applet-name & options], and it creates something and binds it to a var called applet-name. For some reason, I want to wrap this into another macro that -- among other things -- takes an applet-name parameter, extends that name into applet-name-viewport and passes that to defsketch. I, however, am unable to figure out how to do that correctly (using macroexpand on the code above ((pprint (macroexpand(nvp test-name 500 500))), I get
(def(clojure.core/symbol (clojure.core/str (clojure.core/name 'my-namespace.core/test-name) "-viewport"))
(quil.applet/applet
:title (clojure.core/name 'my-namespace.core/test-name)))
(clojure.core/symbol (clojure.core/str (clojure.core/name 'my-namespace.core/test-name) "-viewport") -- this part looks good, but it should be evaluated somehow before passing it to the inner macro...
You need to unquote the form that generates the new symbol instead of the original symbol itself.
Here is a small example of how to accomplish this using a defn like macro for the inner macro:
(defmacro mydefn
[name & body]
`(defn ~name ~#body))
(defmacro defnview
[n & body]
`(mydefn ~(symbol (str (name n) "-viewport")) ~#body))
;; OR
(defmacro defnview
[n & body]
(let [n (symbol (str (name n) "-viewport"))]
`(mydefn ~n ~#body)))
Example:
(defnview foo [a] a)
;; => #'user/foo-viewport
(foo-viewport 1)
;; => 1
(macroexpand '(defnview foo [a] a))
;; => (def foo-viewport (clojure.core/fn ([a] a)))
Most of my application state is stored in a large complex map. For the purposes of this question, I will use a simple structure:
(def data
{:a 1
:b {:c {:d 3}}})
I have a large number of functions which all follow the same pattern:
(defn update-map
[my-map val]
(let [a (:a my-map)
d (-> my-map :b :c :d)]
(assoc-in
(assoc my-map :a (+ a val))
[:b :c :d] (+ d val))))
I retrieve one or more values from the map, perform some calculations, and create a new map with updated values. There are two problems with this approach:
I have a lot of repetitive let bindings across different function definitions
If the schema of the map changes, I will have a lot of code to refactor
I've written a macro to reduce the boilerplate code required to define these functions. It works by looking up predefined getter and setter functions, and automatically generating a let block:
(def getters
{'a #(:a %)
'd #(-> % :b :c :d)})
(def setters
{'a #(assoc % :a %2)
'd #(assoc-in % [:b :c :d] %2)})
(defmacro def-map-fn
[name [& args] [& fields] & code]
(let [my-map 'my-map
lookup #(reduce % [] fields)
getter-funcs (lookup #(conj % %2 (list (getters %2) my-map)))
setter-funcs (lookup #(conj % (symbol (str "update-" %2)) (setters %2)))]
`(defn ~name [~my-map ~#args]
(let [~#getter-funcs ~#setter-funcs]
~#code))))
I can now define my functions more elegantly:
(def-map-fn update-map
[val] ; normal function parameters
[a d] ; fields from the map I will be using
(update-d
(update-a my-map (+ a val))
(+ d val)))
When expanded, it will produce a function definition looking something like this:
(defn update-map
[my-map val]
(let [a (#(:a %) my-map)
d (#(-> % :b :c :d) my-map)
update-a #(assoc % :a %2)
update-d #(assoc-in % [:b :c :d] %2)]
(update-d
(update-a my-map (+ a val))
(+ d val))))
One thing that is nagging me about my macro is that it is not intuitive to the programmer that the my-map function parameter is available for use within the function body.
Is this a good use of macros, or should I be using a different approach entirely (like dynamic var bindings)?
You could perhaps use lenses; the getters and setters then become composable functions. Have a look here or here.
Following the first link you can set up the lens as follows:
; We only need three fns that know the structure of a lens.
(defn lens [focus fmap] {:focus focus :fmap fmap})
(defn view [x {:keys [focus]}] (focus x))
(defn update [x {:keys [fmap]} f] (fmap f x))
; The identity lens.
(defn fapply [f x] (f x))
(def id (lens identity fapply))
; Setting can be easily defined in terms of update.
(defn put [x l value] (update x l (constantly value)))
(-> 3 (view id))
; 3
(-> 3 (update id inc))
; 4
(-> 3 (put id 7))
; 7
; in makes it easy to define lenses based on paths.
(defn in [path]
(lens
(fn [x] (get-in x path))
(fn [f x] (update-in x path f))))
(-> {:value 3} (view (in [:value])))
; 3
(-> {:value 3} (update (in [:value]) inc))
; {:value 4}
(-> {:value 3} (put (in [:value]) 7))
; {:value 7}
You can see form the above the the lens can be adapted to use get/set methods (such as get-in/update-in) based on the data structure you are working with. The real power of lenses which seems to also be what you are after is that you can compose them. In the same example the composition function can be defined as follows:
(defn combine [outer inner]
(lens
(fn [x] (-> x (view outer) (view inner)))
(fn [f x] (update x outer #(update % inner f)))))
(defn => [& lenses] (reduce combine lenses))
The => function can now be used to combine any arbitrary lenses such as:
(-> {:new {:value 3}} (view (=> (in [:new]) (in [:value]))))
; 3
(-> {:new {:value 3}} (update (=> (in [:new]) (in [:value])) inc))
; {:new {:value 4}}
(-> {:new {:value 3}} (put (=> (in [:new]) (in [:value])) 7))
; {:new {:value 7}}
The fact that (in [:new]) is just a function means that you could, for example, store it and manipulate it in various ways. For example, it would be possible to walk your nested map structure and create the lens functions which correspond to accessing the value at each level in the nested map and then at the end compose these functions together to create your getter/setter api. With this set up, your lenses could automatically adapt to any changes in your schema.
The ability to compose lenses can also make it easy to interact with the nodes of your nested map. For example, if you were to ever change the node from an atom to a list, you could simply add a new lens to work with it as follows:
(def each (lens seq map))
(-> {:values [3 4 5]} (view (=> (in [:values]) each)))
; (3 4 5)
(-> {:values [3 4 5]} (update (=> (in [:values]) each) inc))
; {:values (4 5 6)}
(-> {:values [3 4 5]} (put (=> (in [:values]) each) 7))
; {:values (7 7 7)}
I highly recommend looking at the full gist to see more examples of what you can do with lenses.
In this situation, my preference is to avoid the use of macros. They often obfuscate code, but more importantly they aren't composable. An ideal solution here would allow you to use the getter and setter functions outside of functions being defined in def-map-fn. I'd stick with regular functions and data as much as possible.
To begin with, you're concerned about having to rewrite a bunch of code if your schema changes. Fair enough. To address that, I'd start with a data representation of your map's schema. See Prismatic schema for a full-featured schema library for Clojure, but for now something along these lines should do:
(def my-schema
{:a :int
:b {:c {:d :int}}})
From this, you can compute the paths for all the properties in your schema:
(defn paths [m]
(mapcat (fn [[k v]]
(conj (if (map? v)
(map (partial apply vector k) (paths v)))
[k]))
m))
(def property-paths
(into {} (for [path (paths my-schema)] [(last path) path])))
Now, to get or set a property, you can look up its path and use that in conjunction with get-in, update-in, etc. as appropriate:
(let [d (get-in my-map (property-paths :d))]
;; Do something with d.
)
If you get tired of always calling get-in, assoc-in, etc., then you can pretty easily generate a bunch of getter functions:
(doseq [[p path] property-paths]
(eval `(defn ~(symbol (str "get-" (name p)))
[m#] (get-in m# ~path))))
(doseq [[p path] property-paths]
(eval `(defn ~(symbol (str "set-" (name p)))
[m# v#] (assoc-in m# ~path v#))))
(doseq [[p path] property-paths]
(eval `(defn ~(symbol (str "update-" (name p)))
[m# tail#] (apply update-in m# ~path #tail))))
Now you have your get-a, set-a, update-a functions available everywhere in your code, without having to call into some uber-macro to set up the bindings for you. For instance:
(let [a (get-a my-map)]
(-> my-map
(set-a 42)
(update-d + a)))
If you really find setting up the above let binding tedious, you could even write a with-properties macro that accepts a map and a list of property names and executes the body in a context that binds values for those names. But I probably wouldn't bother.
Advantages of this approach include:
It's schema-driven, so the schema is defined in one central place and used to generate other code as needed.
It prefers pure functions over macros, so code is more re-usable and composable.
It's an incremental approach that allows your application to grow more naturally. Rather than starting with an uber-macro that tries to anticipate all possible features you might want, you start with data and functions and sprinkle in macros to alleviate some of the repetitiveness as you see usage patterns emerge.
Why don't you just use update-in?
(defn update-map [my-map val]
(-> my-map
(update-in [:a] + val)
(update-in [:b :c :d] + val)))
actually i am trying to perfectly understand clojure and particularly symbols
(def a 1)
(type a)
;;=>java.lang.Long
(type 'a)
;;=>clojure.lang.Symbol
I know that type is a function so its arguments get evaluated first so i perfectly understand why the code above work this way .In the flowing code i decided to delay the evaluation using macro
(defmacro m-type [x] (type x))
(m-type a)
;;==>clojure.lang.Symbol
and i am fine with that but what i fail to uderstand is this:
(m-type 'a)
;;=>clojure.lang.Cons
why the type of 'a is a cons
the character ' is interpreted by the clojure reader as a reader-macro which expands to a list containing the symbol quote followed by whatever follows the ', so in your call to (m-type 'a) the 'a is expanding to:
user> (macroexpand-1 ''a)
(quote a)
then calling type on the list (quote a) which is a Cons.
This may be a bit more clear if we make the m-type macro print the arguments as it sees them while it is evaluating:
user> (defmacro m-type [x] (println "x is " x) (type x))
#'user/m-type
user> (m-type 'a)
x is (quote a)
clojure.lang.Cons
I'm working on some java library wrapper.
We have some object with methods and can invoke them with (.method object ...). I want to create more convenient api with number of functions (method object ...).
I wrote a simple macro:
(defmacro ^{:private true} gen-method [method & argv]
`(def ~method (memfn ~method ~#argv)))
Now I can invoke (gen-method charAt i) for example and after that I can use (charAt "string" 1).
But I have a lot of method to generate and want to use something like:
(map #(gen-method (-> %1 name symbol arg)) [:charAt :substring ...])
But this is not working. Next example is not working too:
(map #(let [fname (-> %1 name symbol)] (gen-method fname arg)) [:charAt :substring ...])
What I need to change in macro or add something for correct behaviour ?
Thanks.
You will have to take the map operation inside the macro itself as the map operation should happen at compile time i.e form expanding time and not at runtime.
Something like below:
(defmacro ^{:private true} gen-methods [methods]
`(do
~#(map (fn [[x & i]] (let [m (-> x name symbol)] `(def ~m (memfn ~m ~#i)) )) methods)))
(gen-methods [ [:charAt i] [:substring i]])