I've seen a pattern of switching between thread first and thread-last macros:
(-> a get-some-collection (->> (map inc)))
Is there an equally terse way of going back from ->> to -> , maybe even for a single term?
All I can think of is something along the lines of (->> a ... (#(-> % some-threading)) more-thread-lasting) which feels ... not cheeky enough.Wrapping in an anonymous function is the most common trick.
Or if you're going to have a mixed pipeline start it out as an ->
Otherwise there are libraries like: https://github.com/xadecimal/fpipes
IMO you should just avoid mixing them altogether and not think in terms of terseness. See also https://stuartsierra.com/2018/07/06/threading-with-style/#orgheadline4 (Although I definitely disagree with that article when it comes to threading through arithmetic.)
π thanks for that!
when it's absolutely necessary (which IMO is rare) I like https://clojuredocs.org/clojure.core/as-%3E>
occasionally, i have need of digging into an object to find a sequence and then performing a transformation on the sequence, so i'll have a thread-first and the final line will be a thread-last. but that's rare and i mostly let-bind the result and have two expressions. if i need to go from thread-last to thread-first, i always use a let-bind.
I'd also point to https://clojure.org/guides/faq#arg_order as a good reason not to mix threading styles: collection/data functions generally work with ->; sequence functions generally work with ->>; so you have two distinct "types" of processing going on and mixing them in one expression should give you pause...
my tame take on the matter is the same as Noah, my hot take is that with transducers and fixed indent formatting I use arrows very little
If you really feel you must do this, start with -> and thread into ->>:
(-> a
(->> (seq-op b)
(another-op c d))
(some-threading) ; thread-first here
(->> (more-thread-lasting)
(and-more-last-stuff x y)))(but, yeah, what everyone else said: don't do this in general π )
@doppiaelle1999 can you expand a little on what you mean by "transducers and fixed indent formatting" please?
(-> blah
(->> (reduce #(...) frob)
(map baz)
(filter bar)
(into {}))
(foo quux))
vs
(foo
(into {} (comp
(map baz)
(filter bar))
(reduce #(...) frob
blah))
quux)
of course arrows are more visually pleasant, but they make it tougher to grab a sub-expression (arguably transducers are the same in some circumstances but they have other benefits), and they lend themselves too easily to tricks as shown above.
RE: fixed indenting, I find that 99.9% of the time I either want:
β’ the first args to be "in the spotlight" on the same line as the call
β’ the last arg to be "in the spotlight" alone on a new line
β’ all args to take up a line each as none is particularly "important" vs the rest
and fixed indentation does the job while sparing horizontal spaceFWIW I'd definitely use let there. There's no reason to avoid names when there's a good name already in the foo's argument.
yeah of course that'd likely be a let but I needed to show a reasonably big expression
I think it can even be a reasonable heuristic. :)
If someone starts thinking how something should be threaded, a let should be used immediately.
Any idea why I sometimes get these messages for every single namespace in my codebase and I have to re-jack in?
I recall there's a cider-nrepl op undef-all which does something similar, in case Calva isn't already making use of it? In emacs C-u cider-load-file nukes any stale aliases and defs
Interesting. Calva doesnβt have undef-all implemented.
I'd give more information but it appears random
Maybe @pez knows.
I would need to know more about the project and code to have a guess. Is there something somewhere that tries to require gymgreet-server.text.core :as text?
Yes, gymgreet.core requires it
It's not common or persistent, just kind of annoying when it happens
The error says that the alias conflict happens in gymgreet-server.ai.core⦠do you have some require :as text in that namespace?
I do - I can tell that each space that throws this links to another space that usually throws the same error when I go to that space, but it is all fixed by jacking in
I think that the error message has the clue for us. There are at least two attempts to define the same alias. But very hard to guess about without seeing the code.
I'm guessing it's some sort of corrupt state, no? If it was the code itself, then running lein run or jacking in and re-evaluating would not fix it.
Iβm curious about how people recover their repl state from a situation like this. Restarting can be pretty disruptive.
I use this to "nuke" a namespace (without actually killing references to the ns): https://github.com/seancorfield/vscode-calva-setup/blob/develop/calva/config.edn#L157-L176
This happens when you change the ns that has the alias and try to re-eval that ns form. You have text aliased to gymgreet-server.text.core and it thinks you're trying to alias it to something else (that's usually the cause).
It's not a "corrupt state". It's the compiler saying you can't alias something to an existing alias already defined in the ns.
(note: I never use the refresh/reload workflows -- because those can mess things up and then you really do have to restart your REPL... my zap ns snippet is localized to just aliases, symbols (vars), and refers in the current ns so it's a lot safer)