Fork me on GitHub
Jim Strieter07:06:44

When I do this:

(fn (... (f2 (f1 some-map))...))
code compiles fine. I prefer to do this:
(-> some-map
because it's more readable. But I keep getting this error:
Syntax error (NullPointerException) compiling at (/tmp/form-init7026027282884177930.clj:1:73).
What am I doing wrong?


What are the second ... in your first example?

Jim Strieter08:06:47

second ...'s are just more parentheses


I think you've removed too much detail in simplifying your example. There might be a typo in the second one?

Jim Strieter08:06:09

@U01AKQSDJUX posted response in main thread


It's impossible to know without knowing each of those functions. But you can try using macroexpand on the bottom one to see what the macro expands into so you can see the differences


NullPointerexceptions usually means something was null when it shouldn't be, so maybe that will help.

Jim Strieter08:06:48

When I try this at REPL:

(-> {:a 42} (fn [s] (conj s {:b 99})))
I'm expecting this:
{:a 42 :b 99}
But I get this:
Syntax error macroexpanding clojure.core/fn at (/tmp/form-init10684189129508105122.clj:1:1).
{:a 42} - failed: vector? at: [:fn-tail :arity-1 :params] spec: :clojure.core.specs.alpha/param-list
{:a 42} - failed: (or (nil? %) (sequential? %)) at: [:fn-tail :arity-n] spec: :clojure.core.specs.alpha/params+body


So passing anonymous fns to a threading macro has some different expansion effects than you were expecting, see that could give you some clues 🙂

Martin Půda08:06:08

Your example expands to:

(fn {:a 42} [s] (conj s {:b 99}))

Martin Půda08:06:08

Use one of these (the first one is the best):

(-> {:a 42} 
    (conj {:b 99}))

=> {:a 42, :b 99}

(-> {:a 42}
    ((fn [s] (conj s {:b 99}))))

=> {:a 42, :b 99}

(-> {:a 42}
    (#(conj % {:b 99})))

=> {:a 42, :b 99}

🙌 1
👍 2

As a matter pf style I’d not use a threading macro at all for anything less than 3 levels of parens (i.e. 1 as in this case). So my answer would be:

(conj {:a 42} {:b 99})


or this:

(conj (first [{:a 42}]) {:b 99})
in preference to this:
(-> [{:a 42}] first (conj {:b 99}))


Any thing more than one nested form I’d probably use a threading macro.

Jim Strieter08:06:21

@U05390U2P There will be > 10 nested function calls. Right now all I want to do is use threading macro correctly

Jim Strieter08:06:02

What I want to do is this:

(-> some-starting-val



(->> "string"
     (assoc {:a 42} :string)
     (conj [{:b 99}]))
in preference to this:
(conj [{:b 99}] 
  (assoc {:a 42} :string (str/upper-case "string")))

🙌 1

If all those functions are named (as in defined with defn) and take one argument then your (-> some-map f1 f2 f3) form will work


@U014Z9N3UTS you can do what you show if you don’t use anonymous fn’s in the threading macro. So either define the fns f1, f2, etc. using defn’s OR in a let or letfn that encloses the threaded form:

(let [f1 (fn [x] ...)
      f2 (fn [x] ...)
      f3 (fn [x] ...)
  (-> some-starting-val


You can use macroexpand-1 to show what the macro will expand to

👍 1
Joshua Suskalo16:06:56

Something else to be aware of is if you really need to have the argument be in some place besides the first argument of a function call (which is the main target of using an anonymous function here), you can use as-> For example

(-> {:your :data}
    (some-func-in-arg-1 {:extra :data})
    (as-> a (some-func-in-arg-2 {:more :data} a))


(although that case could use ->> instead of as->)

Jim Strieter04:06:59

The mistake I made is that I passed in functions without wrapping in parentheses. For example, I did this:

(-> x f1 f2 f3)
but I needed to do this:
(-> x (f1) (f2) (f3))
Thank you everybody for helping! 🙂


You shouldn't need to wrap the fns in parens if they only take one argument so I'm a little puzzled. Unless you're defining the fns anonymously in line in the threading macro rather than defn or let. In which case wrapping them in parens would resolve it but it's not idiomatic.

Joshua Suskalo20:06:02

That's what the discussion started with, anonymous functions defined inline being used in thread pipelines.


Hi, why can’t recur run in a try block?


inside a try is not a tail


the tail of an expression, is the last subexpression in it to be evaluted/run before the evaluation of the entire expression is complete

👍 1

a try expression semantically is like pushing an exception handler on to the stack of handlers, then evaluating some expression, then popping the pushed exception handler off

👍 1

so the inner expression isn't a tail

hiredman17:06:46 was maybe my first clojure patch, and started the tradition of my patches breaking things that need to be fixed in subsequent patches

❤️ 1