How to pass optional macro args to a function - clojure

Clojure macro noob here. I have a function with some optional parameters, e.g.
(defn mk-foo [name & opt]
(vec (list* name opt)))
giving this:
user> (mk-foo "bar" 1 2 3)
["bar" 1 2 3]
I'm trying to write a macro which takes the same optional arguments and passes them transparently to an invocation of mk-foo. So far I have this:
(defmacro deffoo [name & opt]
`(def ~name ~(apply mk-foo (str name) opt)))
which has the desired effect:
user> (macroexpand '(deffoo bar 1 2 3))
(def bar ["bar" 1 2 3])
The use of apply to flatten the list opt feels clumsy. Is there an idiomatic way to do this? I'm guessing ~# is needed, but I can't get the quoting right. Many thanks.

Your intuition about using apply served you well in this case. When you have a quoted form ` and then unqote all of them it can help to think about moving the un-quoting down to the smallest part or the list. This avoids using code to generate forms that could be simply written.
user=> (defmacro deffoo [name & opt] `(def ~name [~(str name) ~#opt]))
#'user/deffoo
user=> (macroexpand '(deffoo "bar" 1 2 3))
(def "bar" ["bar" 1 2 3])
and here it is with the call to mk-foo:
(defmacro deffoo [name & opt] `(def ~name (mk-foo ~(str name) ~#opt)))
#'user/deffoo
user=> (macroexpand '(deffoo "bar" 1 2 3))
(def "bar" (user/mk-foo "bar" 1 2 3))
in this second case we move the ~ in one level and let the call to mk-foo stay quoted and only unquote the args required to build the parameter list (using splicing-unquote as you suspected)

Related

macros in clojurescript - does not expand properly

I have created a macro in clojure
(ns macro-question.map)
(defmacro lookup [key] (list get (apply hash-map (range 1 5)) key))
in the clojure repl it works as expected
$ clj
Clojure 1.9.0
user=> (require 'macro-question.map)
nil
user=> (macro-question.map/lookup 1)
2
So I create a clojurescript module like this to try to use it:
(ns macro-question.core (:require-macros [macro-question.map]))
(defn lookup1 [] (macro-question.map/lookup 1))
and when I try that in the repl, I get this
$ clj -m cljs.main --repl-env node
ClojureScript 1.10.520
cljs.user=> (require 'macro-question.core)
Execution error (ExceptionInfo) at cljs.compiler/fn (compiler.cljc:304).
failed compiling constant: clojure.core$get#3df1a1ac; clojure.core$get is not a valid ClojureScript constant.
meanwhile back in clojure, there is a clue why this might be
user=> (macroexpand '(macro-question.map/lookup 1))
(#object[clojure.core$get 0x101639ae "clojure.core$get#101639ae"] {1 2, 3 4} 1)
I can create macros that start with '( instead of (list. However, I want the map to be expanded at build time.
What is going on? And what do I have to do to get something like the following:
user=> (macroexpand '(macro-question.map/lookup 1))
(get {1 2, 3 4} 1)
or what should I be doing to use this macro in clojurescript?
(list get (apply hash-map (range 1 5)) key)
Creates a list where the first position is the function object that the var get refers to. What you actually want to return is a list with the fully qualified symbol for get as the first position. Change your macro definition to
(defmacro lookup [key]
`(get ~(apply hash-map (range 1 5)) ~key))
(macroexpand '(lookup 1))
=> (clojure.core/get {1 2, 3 4} 1)
The reference guide for the reader is helpful here https://clojure.org/reference/reader
You can just put 'get!!
(ns macro-question.map)
(defmacro lookup [key] (list 'get (apply hash-map (range 1 5)) key))
(https://stackoverflow.com/posts/58985564 is a better answer about why it happens, and a much better way to do it. This just shows a simple solution that only solves the immediate issue, that I realised right after I had asked the question)

Is there a way to have metadata of values at compile time?

I wrote a specialized function construct, which under the hood is really just a Clojure function. So basically I have a function that makes (similar to fn) and a function that calls my specialized functions (similar to CL's funcall).
My constructor assigns metadata (at compile-time) so I could distinguish between "my" functions and other/normal Clojure functions.
What I want to do is to make a macro that lets users write code as if my functions were normal functions. It would do so by walking over the code, and in functions calls, when the callee is a specialized function, it would change the call so it would use my caller (and also inject some extra information). For example:
(defmacro my-fn [args-vector & body] ...)
(defmacro my-funcall [myfn & args] ...)
(defmacro with-my-fns [& body] ...)
(with-my-fns
123
(first [1 2 3])
((my-fn [x y] (+ x y))) 10 20)
; should yield:
(do
123
(first [1 2 3])
(my-funcall (my-fn [x y] (+ x y)) 10 20))
I run into problems in lexical environments. For example:
(with-my-fns
(let [myf (my-fn [x y] (+ x y))]
(myf))
In this case, when the macro I want to write (i.e. with-my-fns) encounters (myf), it sees myf as a symbol, and I don't have access to the metadata. It's also not a Var so I can't resolve it.
I care to know because otherwise I'll have to put checks on almost every single function call at runtime. Note that I don't really care if my metadata on the values are actual Clojure metadata; if it's possible with the type-system and whatnot it's just as good.
P.S. I initially wanted to just ask about lexical environments, but maybe there are more pitfalls I should be aware of where my approach would fail? (or maybe even the above is actually an XY problem? I'd welcome suggestions).
As #OlegTheCat already pointed out in the comment section, the idea to use meta-data does not work.
However I might have a solution you can live with:
(ns cl-myfn.core)
(defprotocol MyCallable
(call [this magic args]))
(extend-protocol MyCallable
;; a clojure function implements IFn
;; we use this knowledge to simply call it
;; and ignore the magic
clojure.lang.IFn
(call [this _magic args]
(apply this args)))
(deftype MyFun [myFun]
MyCallable
;; this is our magic type
;; for now it only adds the magic as first argument
;; you may add all the checks here
(call [this magic args]
(apply (.myFun this) magic args)))
;;turn this into a macro if you want more syntactic sugar
(defn make-myfun [fun]
(MyFun. fun))
(defmacro with-myfuns [magic & funs]
`(do ~#(map (fn [f#]
;; if f# is a sequence it is treated as a function call
(if (seq? f#)
(let [[fun# & args#] f#]
`(call ~fun# ~magic [~#args#]))
;; if f# is nonsequential it is left alone
f#))
funs)))
(let [my-prn (make-myfun prn)]
(with-myfuns :a-kind-of-magic
123
[1 2 3]
(prn :hello)
(my-prn 123)))
;; for your convenience: the macro-expansion
(let [my-prn (make-myfun prn)]
(prn (macroexpand-1 '(with-myfuns :a-kind-of-magic
123
[1 2 3]
(prn :hello)
(my-prn 123)))))
the output:
:hello
:a-kind-of-magic 123
(do 123 [1 2 3] (cl-myfn.core/call prn :a-kind-of-magic [:hello]) (cl-myfn.core/call my-prn :a-kind-of-magic [123]))

Defining a function from an expression stored in a var

I asked a similar question yesterday but it seemed I had to change my approach, so I did, but now I'm sort of stuck again.
Anyway, what I want to do is something along the line of
(def bar '(* 2 %))
(#(bar) 2) ;this line doesn't work.
(#(* 2 %) 2) ;this is what I want the line that doesn't work to do.
Thing is, I want the expression stored in a var so I could do something like
(def bar2 (list (first bar) 3 (nth bar 2)))
(#(bar2) 2) ;this line obviously doesn't work either.
Maybe there's another approach than the # anonymous function reader macro.
I'm doing a project in genetic programming for uni so what I need to do is have an expression that I can change and make into a function.
(def bar '(* 2 %))
((eval (read-string (str "#" bar))) 3)
=> 6
If you used named parameter(s) in expression it would look cleaner:
(def bar '(* 2 p))
(def f-expr (concat '(fn [p]) [bar]))
((eval f-expr) 3)
=> 6
If you want to evaluate quoted expressions at run time (as opposed to compile time, à la macros), you can just use eval:
(eval '(* 10 12))
;=> 120
(def arg 12)
(def bar '(* 10 arg))
(eval bar)
;=> 120
Normally one steers clear of eval (macros will perform better, for one thing), but it might be what you want for playing with genetic algorithms.

Clojure: Wrong number of args (4) passed to: core$rest

Write a function which allows you to create function compositions. The
parameter list should take a variable number of functions, and create
a function applies them from right-to-left.
(fn [& fs]
(fn [& args]
(->> (reverse fs)
(reduce #(apply %2 %1) args))))
http://www.4clojure.com/problem/58
=> (= [3 2 1] ((_ rest reverse) [1 2 3 4]))
clojure.lang.ArityException: Wrong number of args (4) passed to: core$rest
What's causing this error? I can't see it.
It's in your use of apply - this turns the last parameter into a flattened list of parameters, creating a call that looks like:
(rest 1 2 3 4)
Which is presumably not what you intended..... and explains the error you are getting.

Make a namespace qualified function name in a macro

I have a bunch of functions that map to and from some codes defined by an external system:
(defn translate-from-ib-size-tick-field-code [val]
(condp = val
0 :bid-size
3 :ask-size
5 :last-size
8 :volume))
(defn translate-to-ib-size-tick-field-code [val]
(condp = val
:bid-size 0
:ask-size 3
:last-size 5
:volume 8))
I'd like to make a macro to remove the duplication:
#_ (translation-table size-tick-field-code
{:bid-size 0
:ask-size 3
:last-size 5
:volume 8})
I started the macro like this:
(defmacro translation-table [name & vals]
`(defn ~(symbol (str "translate-to-ib-" name)) [val#]
(get ~#vals val#)))
The resulting function body seems right, but the function name is wrong:
re-actor.conversions> (macroexpand `(translation-table monkey {:a 1 :b 2}))
(def translate-to-ib-re-actor.conversions/monkey
(.withMeta (clojure.core/fn translate-to-ib-re-actor.conversions/monkey
([val__10589__auto__]
(clojure.core/get {:a 1, :b 2} val__10589__auto__))) (.meta ...
I'd like the "translate-to-ib-" to appear as part of the function name, instead of a prefix to the namespace, as it turned out.
How can I do this with clojure macros? If I am just doing it wrong and shouldn't use macros for this for some reason, please do let me know, but I would also like to know how to create function names like this to just improve my understanding of clojure and macros in general. Thanks!
The macro issue is twofold:
1) You're using a backtick when quoting the form passed to macroexpand, which namespace-qualifies the symbols within:
`(translation-table monkey {:a 1 :b 2})
=> (foo.bar/translation-table foo.bar/monkey {:a 1, :b 2})
where foo.bar is whatever namespace you're in.
2) You're constructing the name of the defn item using the symbol name, which, when it is namespace-qualified, will stringify to "foo.bar/monkey". Here's a version that will work:
(defmacro translation-table [tname & vals]
`(defn ~(symbol (str "translate-to-ib-" (name tname))) [val#]
(get ~#vals val#)))
Notice that we're getting the name of tname without the namespace part, using the name function.
As for whether a macro is the right solution here, probably not :-) For a simple case like this, I might just use maps:
(def translate-from-ib-size-tick-field-code
{0 :bid-size
3 :ask-size
5 :last-size
8 :volume})
;; swap keys & vals
(def translate-to-ib-size-tick-field-code
(zipmap (vals translate-from-ib-size-tick-field-code)
(keys translate-from-ib-size-tick-field-code)))
(translate-from-ib-size-tick-field-code 0)
=> :bid-size
(translate-to-ib-size-tick-field-code :bid-size)
=> 0
If speed is of the essence, check out case.
Some unsolicited advice on a different point: (get ~#vals val#) is extremely suspicious. Your macro alleges to take any number of arguments, but if it gets more than two it will just do something that doesn't make any sense. Eg,
(translation-table metric {:feet :meters}
{:miles :kilometers}
{:pounds :kilograms})
aside from being a terrible translation table, expands to code that always throws an exception:
(defn translate-to-ib-metric [val]
(get {:feet :meters}
{:miles :kilometers}
{:pounds :kilograms}
val)))
get doesn't take that many arguments, of course, and it's not really what you meant anyway. The simplest fix would be to only permit two arguments:
(defmacro translation-table [name vals]
(... (get ~vals val#)))
But note that this means the value map gets reconstructed every time the function is called - problematic if it's expensive to compute, or has side effects. So if I were writing this as a macro (though see Justin's answer - why would you?), I would do it as:
(defmacro translation-table [name vals]
`(let [vals# ~vals]
(defn ~(symbol (str "translate-to-ib-" name)) [val#]
(get vals# val#))))