This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-06-24
Channels
- # announcements (39)
- # aws (4)
- # babashka (18)
- # beginners (32)
- # biff (10)
- # calva (22)
- # clj-kondo (11)
- # clj-on-windows (2)
- # cljs-dev (20)
- # clojure (31)
- # clojure-europe (130)
- # clojure-norway (21)
- # clojure-spec (24)
- # clojure-uk (17)
- # clojured (2)
- # clojurescript (6)
- # conjure (1)
- # core-async (5)
- # cursive (11)
- # datalevin (1)
- # datomic (9)
- # emacs (24)
- # figwheel-main (42)
- # fulcro (6)
- # gratitude (6)
- # honeysql (8)
- # juxt (16)
- # kaocha (6)
- # lsp (235)
- # malli (2)
- # nbb (7)
- # off-topic (23)
- # other-languages (24)
- # pedestal (7)
- # reitit (6)
- # sci (1)
- # shadow-cljs (21)
- # tools-build (22)
- # tools-deps (57)
- # vim (24)
When I do this:
(fn (... (f2 (f1 some-map))...))
code compiles fine.
I prefer to do this:
(-> some-map
f1
f2
...
fn)
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?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?
@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.
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 https://stackoverflow.com/questions/62948630/clojure-functions-and-thread-macro that could give you some clues 🙂
Your example expands to:
(fn {:a 42} [s] (conj s {:b 99}))
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}
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.
@U05390U2P There will be > 10 nested function calls. Right now all I want to do is use threading macro correctly
What I want to do is this:
(-> some-starting-val
f1
f2
f3
...
fn)
e.g.
(->> "string"
str/upper-case
(assoc {:a 42} :string)
(conj [{:b 99}]))
in preference to this:
(conj [{:b 99}]
(assoc {:a 42} :string (str/upper-case "string")))
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
f1
f2
f3
...
fn))
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))
another-func)
(although that case could use ->>
instead of as->
)
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.
That's what the discussion started with, anonymous functions defined inline being used in thread pipelines.
the tail of an expression, is the last subexpression in it to be evaluted/run before the evaluation of the entire expression is complete
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
https://clojure.atlassian.net/browse/CLJ-31 was maybe my first clojure patch, and started the tradition of my patches breaking things that need to be fixed in subsequent patches https://clojure.atlassian.net/browse/CLJ-667