macros in clojurescript - does not expand properly - clojure

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)

Related

Find out where the error happened in Clojure

For the most part I understand what Clojure is telling me with it's error messages. But I am still clueless as to find out where the error happened.
Here is an example of what I mean
(defn extract [m]
(keys m))
(defn multiple [xs]
(map #(* 2 %) xs))
(defn process [xs]
(-> xs
(multiple) ; seq -> seq
(extract))) ; map -> seq ... fails
(process [1 2 3])
Statically typed languages would now tell me that I tried to pass a sequence to a function that expects a map on line X. And Clojure does this in a way:
ClassCastException java.lang.Long cannot be cast to java.util.Map$Entry
But I still have no idea where the error happened. Obviously for this instance it's easy because there are just 3 functions involved, you can easily just read through all of them but as programs grow bigger this gets old very quickly.
Is there a way find out where the errors happened other than just proof reading the code from top to bottom? (which is my current approach)
You can use clojure.spec. It is still in alpha, and there's still a bunch of tooling support coming (hopefully), but instrumenting functions works well.
(ns foo.core
(:require
;; For clojure 1.9.0-alpha16 and higher, it is called spec.alpha
[clojure.spec.alpha :as s]
[clojure.spec.test.alpha :as stest]))
;; Extract takes a map and returns a seq
(s/fdef extract
:args (s/cat :m map?)
:ret seq?)
(defn extract [m]
(keys m))
;; multiple takes a coll of numbers and returns a coll of numbers
(s/fdef multiple
:args (s/cat :xs (s/coll-of number?))
:ret (s/coll-of number?))
(defn multiple [xs]
(map #(* 2 %) xs))
(defn process [xs]
(-> xs
(multiple) ; seq -> seq
(extract))) ; map -> seq ... fails
;; This needs to come after the definition of the specs,
;; but before the call to process.
;; This is something I imagine can be handled automatically
;; by tooling at some point.
(stest/instrument)
;; The println is to force evaluation.
;; If not it wouldn't run because it's lazy and
;; not used for anything.
(println (process [1 2 3]))
Running this file prints (among other info):
Call to #'foo.core/extract did not conform to spec: In: [0] val: (2
4 6) fails at: [:args :m] predicate: map? :clojure.spec.alpha/spec
#object[clojure.spec.alpha$regex_spec_impl$reify__1200 0x2b935f0d
"clojure.spec.alpha$regex_spec_impl$reify__1200#2b935f0d"]
:clojure.spec.alpha/value ((2 4 6)) :clojure.spec.alpha/args ((2 4
6)) :clojure.spec.alpha/failure :instrument
:clojure.spec.test.alpha/caller {:file "core.clj", :line 29,
:var-scope foo.core/process}
Which can be read as: A call to exctract failed because the value passed in (2 4 6) failed the predicate map?. That call happened in the file "core.clj" at line 29.
A caveat that trips people up is that instrument only checks function arguments and not return values. This is a (strange if you ask me) design decision from Rich Hickey. There's a library for that, though.
If you have a REPL session you can print a stack trace:
(clojure.stacktrace/print-stack-trace *e 30)
See http://puredanger.github.io/tech.puredanger.com/2010/02/17/clojure-stack-trace-repl/ for various different ways of printing the stack trace. You will need to have a dependency such as this in your project.clj:
[org.clojure/tools.namespace "0.2.11"]
I didn't get a stack trace using the above method, however just typing *e at the REPL will give you all the available information about the error, which to be honest didn't seem very helpful.
For the rare cases where the stack trace is not helpful I usually debug using a call to a function that returns the single argument it is given, yet has the side effect of printing that argument. I happen to call this function probe. In your case it can be put at multiple places in the threading macro.
Re-typing your example I have:
(defn extract [m]
(keys m))
(defn multiply [xs]
(mapv #(* 2 %) xs))
(defn process [xs]
(-> xs
(multiply) ; seq -> seq
(extract))) ; map -> seq ... fails ***line 21***
(println (process [1 2 3]))
;=> java.lang.ClassCastException: java.lang.Long cannot be cast
to java.util.Map$Entry, compiling:(tst/clj/core.clj:21:21)
So we get a good clue in the exception where is says the file and line/col number tst.clj.core.clj:21:21 that the extract method is the problem.
Another indispensible tool I use is Plumatic Schema to inject "gradual" type checking into clojure. The code becomes:
(ns tst.clj.core
(:use clj.core tupelo.test)
(:require
[tupelo.core :as t]
[tupelo.schema :as tsk]
[schema.core :as s]))
(t/refer-tupelo)
(t/print-versions)
(s/defn extract :- [s/Any]
[m :- tsk/Map]
(keys m))
(s/defn multiply :- [s/Num]
[xs :- [s/Num]]
(mapv #(* 2 %) xs))
(s/defn process :- s/Any
[xs :- [s/Num]]
(-> xs
(multiply) ; seq -> seq
(extract))) ; map -> seq ... fails
(println (process [1 2 3]))
clojure.lang.ExceptionInfo: Input to extract does not match schema:
[(named (not (map? [2 4 6])) m)] {:type :schema.core/error, :schema [#schema.core.One{:schema {Any Any},
:optional? false, :name m}],
:value [[2 4 6]], :error [(named (not (map? [2 4 6])) m)]},
compiling:(tst/clj/core.clj:23:17)
So, while the format of the error message is a bit lengthy, it tells right away that we passed a parameter of the wrong type and/or shape into the method extract.
Note that you need a line like this:
(s/set-fn-validation! true) ; enforce fn schemas
I create a special file test/tst/clj/_bootstrap.clj so it is always in the same place.
For more information on Plumatic Schema please see:
https://github.com/plumatic/schema
https://youtu.be/o_jtwIs2Ot8
https://github.com/plumatic/schema/wiki/Basics-Examples
https://github.com/plumatic/schema/wiki/Defining-New-Schema-Types-1.0

Clojure Null Pointer Exception Due to Function Key Mismatch

I am working through the beginning of a Clojure for the Brave and True example:
(ns the-divine-cheese-code.visualization.svg
(:require [clojure.string :as s])
(:refer-clojure :exclude [min max]))
(defn comparator-over-maps
[comparison-fn ks]
(fn [maps]
(zipmap ks
(map (fn [k] (apply comparison-fn (map k maps)))
ks))))
(def min (comparator-over-maps clojure.core/min [:lat :lng]))
(def max (comparator-over-maps clojure.core/max [:lat :lng]))
I am getting a Null Pointer Exception, though, when I try to run the following code in a CIDER REPL:
(min [{:a 1 :b 3} {:a 5 :b 0}])
I am trying to identify the source of the error within the code. Any help would certainly be appreciated.
The function comparator-over-maps uses the vector of keywords that you pass it to look up values in the map. In this case the maps you're passing have keys :a and :b, but your definition of min is requesting the keys :lat and :lng, which don't exist -- so it receives nil, which is the cause of the NPE. If you change one or the other set of keywords to match, then the example should work, e.g.:
(min [{:lat 1 :lng 3} {:lat 5 :lng 0}])
Based on #BlackBear's comment, I reran the code in the CIDER REPL as:
(min [{:lat 1 :lng 3} {:lat 5 :lng 0}])
and it produced the correct answer:
=> {:lat 1, :lng 0}
Thanks for your help!

Clojure caret as a symbol?

So I thought it would be a nice idea to name a function that calculates the exponential ^, but it seems like the caret actually does something special, as the Clojure REPL generates an error when evaluating '^. Googling mostly gave me this, so I was wondering what the actualy use for the caret in Clojure is.
(Also, would it be possible after all to name a function ^?)
^ is "the meta character" it tells the reader to add the symbol starting with ^ as metadata to the next symbol (provided it is something that implements IMetas)
user=> (def x ^:IamMeta [1 2 3])
#'user/x
user=> x
[1 2 3]
user=> (meta x)
{:tag :IamMeta}
user=>
You can learn a lot about how clojure works under the hood by looking at the meta of things, for instance functions:
user=> (meta foo)
{:ns #<Namespace user>,
:name foo, :file "NO_SOURCE_PATH",
:line 5, :arglists ([s])}
this is very often used for type hints
(defn foo [^String s] (.charAt s 1))
it is generally a good idea to turn on reflection warnings (set! *warn-on-reflection* true) and then add type hints until the warnings go away. without these Clojure will look up the type of the function operands at run-time, which saves you the trouble of fussing with types though at a slight cost.
PS: My next favorite reader character is the "dispatch" character #, it is well worth learning about it next :)
PPS: this is different in clojure 1.2.x vs clojure 1.3.x
in Clojure 1.2.1 metadata does not compose when you use the meta-character:
user=> (def foo ^:foo ^:bar [1 2 3])
#'user/foo
user=> (meta foo)
{:tag :foo}
and in 1.3 it "does the right thing" and also keywords are options instead of "tags":
user=> (def foo ^:foo ^:bar [1 2 3])
#'user/foo
user=> (meta foo)
{:foo true, :bar true}
It seems to me that the answer to your question is, unfortunately, no. In Clojure, you cannot name a function ^.
I tried the following in the REPL:
user=> (println \^)
^
nil
This seems to imply that you can escape the carat (^) with a backslash. However, if I try to declare a function using \^ as a name then I get an error message:
user=> (defn \^ [n e] (cond (= e 0) 1 :else (* n (\^ n (- e 1)))))
IllegalArgumentException First argument to defn must be a symbol
clojure.core/defn (core.clj:277)
The same code works with a regular text name:
user=> (defn exp [n e] (cond (= e 0) 1 :else (* n (exp n (- e 1)))))
#'user/exp
user=> (exp 3 3)
27
I would be delighted if someone with better Clojure-fu than mine could prove me wrong! :)

How to pass optional macro args to a function

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)

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#))))