1

I have a map and I want to write a custom function for updating it.

(-> {:a 1 :b 2}
    (fn [x] (update x :a inc)))

This of course is a simple example and could be easily done without the function wrapped around the update, but it shows what I want to do. But this gives me the following error.

Syntax error macroexpanding clojure.core/fn at (core.clj:108:1).
{:a 1, :b 2} - failed: vector? at: [:fn-tail :arity-1 :params] spec: :clojure.core.specs.alpha/param-list
{:a 1, :b 2} - failed: (or (nil? %) (sequential? %)) at: [:fn-tail :arity-n] spec: :clojure.core.specs.alpha/params+body

I don't get why this is not working, since the threading macro should but my map as first parameter in the function, right?

4 Answers 4

5

You can always use macroexpand to see what happened. In your case, macroexpand will return you:

(fn {:a 1, :b 2} [x] (update x :a inc))

obviously this is not a valid function. But if you tweak it this way:

(-> {:a 1 :b 2}
    (#(update % :a inc)))

the expanded form will then become valid:

(#(update % :a inc) {:a 1, :b 2})
Sign up to request clarification or add additional context in comments.

Comments

2

You don't put a function itself to be called, but call the function without the first parameter, For your example it would be:

> (-> {:a 1 :b 2} 
      (update :a inc))

{:a 2, :b 2}

This is easier to see by expanding the macro in each case

> (macroexpand-1 '(-> {:a 1 :b 2} (update :a inc)))

(update {:a 1, :b 2} :a inc)

> (macroexpand-1 '(-> {:a 1 :b 2} (fn [x] (update x :a inc))))

(fn {:a 1, :b 2} [x] (update x :a inc))

1 Comment

I didn't want to use update but something else this was just for demonstration. So the code doesn't really solve the problem. Anyways this showed me where the issue was. I was giving the macro a function, where it cannot insert the parameter. So I need to wrap my function into extra parentheses. Thanks!
1

As @jas and @rmcv pointed out, I was giving the threading macro the function itself, not the call of a function without the argument. So in short terms the solution would be

(-> {:a 1 :b 2}
    ((fn [x] (update x :a inc))))

Comments

1

I don't think any of these solutions are the simplest. I would propose choosing one of the following:


A. Use the normal threading form:

(-> {:a 1, :b 2} 
  (update :a inc))   => {:a 2, :b 2}

Everyone is used to seeing this and can understand it easily. Since you have already rejected this approach, I assume you think the code is clearer by using a named parameter.


B. Use a named function

(defn updater [x] (update x :a inc))

(-> {:a 1, :b 2} 
    updater)         => {:a 2, :b 2} 

(-> {:a 1, :b 2} 
    (updater))       => {:a 2, :b 2}

This is more how the -> form was envisioned to work. I think the 2nd version is the clearest, as it is the most consistent where all function expressions have parentheses (single arg or multi-arg).


C. Consider using the it-> macro from the Tupelo Library:

(it-> {:a 1, :b 2} 
      (update it :a inc))   => {:a 2, :b 2}

Much like the named function, the expression is normal Clojure form without the "invisible" parameter silently inserted into the update expression. The pronoun it serves as the temporary placeholder for the threaded value (an idea copied from Groovy). Simple, explicit, and flexible, since the it can be in the first, last, or any other parameter location:

(it-> 1
      (inc it)                                  ; thread-first or thread-last
      (+ it 3)                                  ; thread-first
      (/ 10 it)                                 ; thread-last
      (str "We need to order " it " items." )   ; middle of 3 arguments
;=> "We need to order 2 items." )

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.