I'm a Clojure beginner and I want to understand the -> macro
This code works:
(-> '(1 2 3) reverse)
But this doesn't even compile and I don't know how to deal with the error message:
user=> (-> '(1 2 3) (fn [x] (reverse x)))
CompilerException java.lang.IllegalArgumentException: Parameter declaration quote should be a vector, compiling:(NO_SOURCE_PATH:1:1)
How can I fix this?
I use macroexpand and friends a lot when unexpected things start happening. If you use these here it becomes really obvious what's going on.
user=> (macroexpand-1 '(-> '(1 2 3) (fn [x] (reverse x))))
(fn (quote (1 2 3)) [x] (reverse x))
I think seeing this it's pretty obvious that the (quote (1 2 3) should not be the first arg to fn.
We can also see that the ugly double-parens approach fixes it:
user=> (macroexpand-1 '(-> '(1 2 3) ((fn [x] (reverse x)))))
((fn [x] (reverse x)) (quote (1 2 3)))
Side note that tripped me up: you have to use macroexpand-1 to get a result here. If you use macroexpand or clojure.walk/macroexpand-all it'll blow up (with your exception) after the first expansion, since fn itself is a macro, and is being called with bad syntax after the first expansion.
Related
I am currently trying to make a simple Clojure macro that applies the inputted function twice: (f (f args)), (e.g (inc (inc 4)) => 6)
The problem is, when I run the below code using (reapply-function '(inc 4)), I get nil. This doesn't make sense to me since I can print out both the value of f and the result to get inc and 5. There must be a really simple thing I'm missing. Can anyone see the issue?
(defmacro reapply-function
[args]
(list 'let ['f (list 'first (list 'quote args))
'result args]
(list 'f 'result)))
Initial Note
This answer is provided assuming that you're trying to learn to use macros for its own sake. I fully agree with #Thumbnail's answer: Don't use macros except when you absolutely, positively cannot avoid it -- and this is not one of those times.
Shorter Implementation
Consider:
(defmacro reapply-function [[func & rest]]
`(~func (~func ~#rest)))
macroexpand demonstrates how it works:
user=> (macroexpand '(reapply-function (inc 4)))
(inc (inc 4))
...and it functions in the repl:
user=> (reapply-function (inc 4))
6
...But Why Didn't The Original Work?
With your original implementation, macroexpand-1 gives us this:
(let [f (first (quote (inc 4)))
result (inc 4)]
(f result))
...which does indeed evaluate to nil.
But why? In short: f is, in this code, a symbol, not the function that symbol points to.
Thus, to make the shortest possible change that makes the original code function:
(defmacro reapply-function
[args]
(list 'let ['f (list 'first (list 'quote args))
'result args]
(list '(resolve f) 'result)))
The first rule of macro club is ... don't use macros unless you have to.
In this case,
(defn twice [f]
(fn [& args] (f (apply f args))))
((twice inc) 4)
;6
Or, if you prefer to swallow the function and its arguments at once,
(defn reapply-function [f & args]
(f (apply f args)))
(reapply-function inc 4)
;6
You can find the rules of macro club in Programming Clojure by Halloway & Bedra. Also, the above rather casts a side-light on the question than answer it.
Many times when I try to write some function I get exception. That is normal. In Java you can find place and reason why exception occures but in clojure exception texts just make me crazy. Is there some tips how to read exceptions in clojure and how to find where in code exception happens and why?
For example I will take some code:
(do
(list?)
(list? [])
(list? '(1 2 3))
(list? (defn f [] (do ())))
(list? "a"))
when I call this function in REPL I will get
java.lang.IllegalArgumentException: Wrong number of args (0) passed to: core$list-QMARK- (NO_SOURCE_FILE:46)
which do not help me much to find the problem in second line. In little more complicated code it will give almost no information. (Of course it tells look at list? in some there is wrong number of arguments.) Is it wrong that I try to write code in REPL? How to read exception mesages in REPL? Is there way how to get better information about exceptions in REPL?
You can use clojure.stacktrace: http://richhickey.github.com/clojure/clojure.stacktrace-api.html
Usage:
(use 'clojure.stacktrace)
(/ 1 0)
(e)
Output:
java.lang.ArithmeticException: Divide by zero
at clojure.lang.Numbers.divide (Numbers.java:156)
clojure.lang.Numbers.divide (Numbers.java:3691)
user$eval954.invoke (NO_SOURCE_FILE:1)
clojure.lang.Compiler.eval (Compiler.java:6511)
clojure.lang.Compiler.eval (Compiler.java:6477)
clojure.core$eval.invoke (core.clj:2797)
clojure.main$repl$read_eval_print__6405.invoke (main.clj:245)
clojure.main$repl$fn__6410.invoke (main.clj:266)
nil
Acquire org.clojure/tools.trace.
user=> (use 'clojure.tools.trace)
Let's try a dotrace (changed up the order to make things more interesting):
user=> (dotrace [list?]
#_=> (do
#_=> (list? [])
#_=> (list? '(1 2 3))
#_=> (list?)
#_=> (list? (defn f [] (do ())))
#_=> (list? "a"))
#_=> )
IllegalStateException Can't dynamically bind non-dynamic var: clojure.core/list?
clojure.lang.Var.pushThreadBindings (Var.java:353)
Hmm...
user=> (.setDynamic #'list?)
#'clojure.core/list?
Let's try that again:
user=> (dotrace [list?]
#_=> (do
#_=> (list? [])
#_=> (list? '(1 2 3))
#_=> (list?)
#_=> (list? (defn f [] (do ())))
#_=> (list? "a")))
TRACE t1216: (list? [])
TRACE t1216: => false
TRACE t1217: (list? (1 2 3))
TRACE t1217: => true
TRACE t1218: (list?)
ArityException Wrong number of args (0) passed to: core$list-QMARK-
clojure.lang.AFn.throwArity (AFn.java:437)
Aha! Made it to (list?) before the exception.
Why don't when-let and if-let support multiple bindings by default?
So:
(when-let [a ...
b ...]
(+ a b))
...instead of:
(when-let [a ...
(when-let [b ...
(+ a b)))
I am aware that I can write my own macro or use a monad (as described here: http://inclojurewetrust.blogspot.com/2010/12/when-let-maybe.html).
Because (for if-let, at least) it's not obvious what to do with the "else" cases.
At least, motivated by Better way to nest if-let in clojure I started to write a macro that did this. Given
(if-let* [a ...
b ...]
action
other)
it would generate
(if-let [a ...]
(if-let [b ...]
action
?))
and it wasn't clear to me how to continue (there are two places for "else").
You can say that there should be a single alternative for any failure, or none for when-let, but if any of the tests mutate state then things are still going to get messy.
In short, it's a little more complicated than I expected, and so I guess the current approach avoids having to make a call on what the solution should be.
Another way of saying the same thing: you're assuming if-let should nest like let. A better model might be cond, which isn't a "nested if" but more an "alternative if", and so doesn't fit well with scopes... or, yet another way of saying it: if doesn't handle this case any better.
Here is when-let*:
(defmacro when-let*
"Multiple binding version of when-let"
[bindings & body]
(if (seq bindings)
`(when-let [~(first bindings) ~(second bindings)]
(when-let* ~(vec (drop 2 bindings)) ~#body))
`(do ~#body)))
Usage:
user=> (when-let* [a 1 b 2 c 3]
(println "yeah!")
a)
;;=>yeah!
;;=>1
user=> (when-let* [a 1 b nil c 3]
(println "damn! b is nil")
a)
;;=>nil
Here is if-let*:
(defmacro if-let*
"Multiple binding version of if-let"
([bindings then]
`(if-let* ~bindings ~then nil))
([bindings then else]
(if (seq bindings)
`(if-let [~(first bindings) ~(second bindings)]
(if-let* ~(vec (drop 2 bindings)) ~then ~else)
~else)
then)))
Usage:
user=> (if-let* [a 1
b 2
c (+ a b)]
c
:some-val)
;;=> 3
user=> (if-let* [a 1 b "Damn!" c nil]
a
:some-val)
;;=> :some-val
EDIT: It turned out bindings should not be leaked in the else form.
If you use cats, then there is a mlet function that you might find useful :
(use 'cats.builtin)
(require '[cats.core :as m])
(require '[cats.monad.maybe :as maybe])
(m/mlet [x (maybe/just 42)
y nil]
(m/return (+ x y)))
;; => nil
As you can see, the mlet short-circuits when encountering a nil value.
(from section 6.5.1 nil)
Functions with closures seem to break when used with eval.
(eval {:fn (let [x "foo"] (fn [] "x"))})
;=> {:fn #<user$eval14716$fn__14717 user$eval14716$fn__14717#1ddd735>}
(eval {:fn (let [x "foo"] (fn [] x))})
;=> IllegalArgumentException No matching ctor found for class user$eval14740$fn__14741
; clojure.lang.Reflector.invokeConstructor (Reflector.java:166)
I don't really know enough about Clojure (or closure) to know if this is a bug or something which intentionally isn't allowed - can anyone shed some light on this?
Edit: Just to be clear, I'm talking specifically about the way eval handles function objects. AFAIK eval is actually designed to work with java objects, including functions; the example given on the clojure website - (eval (list + 1 2 3)) - passes a function object into eval.
Cloure's eval does not perfectly support function objects. It's not necessarily even closures that cause the problem.
For example, this did not work in Clojure 1.0.0:
(eval {:fn (fn [x] x)})
But this did:
(eval (fn [x] x))
The first example got fixed. The following also works:
(eval (let [x "foo"] (fn [] x)))
But the following still does not work:
(eval {:fn (let [x "foo"] (fn [] x))})
I can't pin it down to a single line in the compiler, but it's something about how literal objects (clojure.lang.Compiler$ObjExpr I think) get handled by eval in different contexts: e.g. at the "top" of an expression versus inside another data structure.
In general, I think, you cannot rely on being able to eval function objects in Clojure, regardless of whether or not they are closures. It happens to work for some simple examples, mostly to simplify the explanation of things like (eval (list + 1 2)). Macros should always return literal source code as data structures, not compiled functions.
Try quoting your argument to eval:
(eval '{:fn (let [x "foo"] (fn [] x))})
;=> {:fn #<user$eval345$fn__346 user$eval345$fn__346#17b6dd83>}
((:fn *1))
;=> "foo"
This is not a bug. The equivalent of (eval (list + 1 2 3)) with a "closure" is (eval (list fn [] "foo")), not (eval (fn [] "foo")).
And (eval (list fn [] "foo")) => Can't take value of a macro: #'clojure.core/fn, again indicating that you're not supposed to do things like that (and there's no need for it anyway).
Are there non-macro versions of and and or in Clojure?
Update: In this case I don't care about the short circuiting.
or
The function some "Returns the first logical true value of (pred x) for any x in coll, else nil."
So you could use (some identity coll) for or. Note that its behaviour will differ from or when the last value is false: it will return nil where or would return false.
and
If you don't need to know the value of the last form in the coll vector, you can use (every? identity coll) for and. This will differ from the behaviour of the and macro in that it returns true if all of its arguments are truthy. See larsmans' answer if you need the result of the last form.
Let land stand for "logical and", then they're trivial to define:
(defn land
([] true)
([x & xs] (and x (apply land xs))))
Or, slightly closer to the standard and behavior:
(defn land
([] true)
([x] x)
([x & xs] (and x (apply land xs))))
And similarly for or.
This actually came up as a topic on clojure-dev recently. Rich Hickey ultimately concluded they should be added to core for 1.3 as every-pred and any-pred (logged as CLJ-729). I think further discussions there have led them to now be called every-pred (the and variant) and some-fn (the or variant). The final version was just recently committed to master.
If you mean functions: no, and they cannot be. The reason is that function forms always evaluate all their arguments before applying the function to their value. You do not want that here.
Most cases where you want this there is a more idiomatic way to do it, but just an exercise, it is possible to defer evaluation by thunking. Thunk your expressions and give them to logical operators that evaluate the the thunk when needed, using the standard and/or:
(defn &&* [& fns]
(cond (= 1 (count fns)) ((first fns))
:otherwise
(and ((first fns)) (apply &&* (next fns)))))
(defn ||* [& fns]
(cond (= 1 (count fns)) ((first fns))
:otherwise
(or ((first fns)) (apply ||* (next fns)))))
Example use:
(map
(partial apply &&*)
(map (partial map constantly) ;; thunk all of these values
[["yes" "no"]
[false true]
[true "something"]
[true "something" "false"]]))
("no" false "something" "false")
Another Example:
(defmacro thunks
"convert expressions into thunks to prevent advance evaluation"
[& exprs]
(let [fns# (map (fn [e] `(fn [] ~e)) exprs)]
(cons 'vector fns#)))
(apply ||* (thunks (+ 1 2) false (* 1 5)))
3
(apply &&* (thunks (+ 1 2) false (* 1 5)))
false
(apply &&* (thunks (+ 1 2) (* 1 5)))
5