
Threading Macros Guide - tosh
https://clojure.org/guides/threading_macros
======
roenxi
Threading macros make it very obvious how nested functions are competitive
with imperative styles. They both make the problems more obvious and the
solutions to those problems more intuitive.

After using them for a few hours, suddenly monads become obvious. The values
passing through the thread need a level of boxing; and the threading macro can
respond sensibly when errors occur.

Continuing the logical pattern from -> to some-> there is a fertile field of
possibility for threading macros to provide the error handling structures that
get lost moving from imperative to functional programming. Probably possible
to be too clever though.

~~~
EdwardDiego
> After using them for a few hours, suddenly monads become obvious

They do? I mean, I love the pipe operator from F# and the thread-first macro
in Clojure, but I'm missing the part where suddenly it's monads.

~~~
roenxi
Possibly I'm about to reveal a horrible misunderstanding of monads :p.

So I might naturally write code:

    
    
      f = open("some/file.path")
    
      if f == ERROR_FILE_NOT_FOUND:
        print("ERROR_FILE_NOT_FOUND")
        return -1
    
      f.write("yes")
    

vs with a threading macro

    
    
      (-> "some/file.path"
          (open)
          (write "yes"))
    

Obviously I've lost the test in the middle and the thread macro code will
crash hard if something goes wrong. I need to fix it. Now I could re-write
(open) so that it handles errors internally. We don't really want that because
probably how we handle errors is context dependent. However, we have a
function that is basically the context: (->)! So instead of embedding the
error handling by re-writing (open) we embed it into (->) by rewriting it as
(-$>).

So now we have (-$>) that checks every step of the thread and handles
ERROR_FILE_NOT_FOUND appropriately. And returns the -1 we were looking for.
Neat.

However, now we are in territory where we would like to start using an object
that is either in a "successful" state or a "failed" state, and that is quite
primitive so that (-$>) knows about it. Ie, we want (-$>) to appear in a
library that doesn't know anything about our specific use case, and
generalises beyond ERROR_FILE_NOT_FOUND. We are about to stumble into
something very similar to Haskell's Maybe monad.

So we created a new problem - can't check state in the middle of a nested
function call - and the solution is to use a threading macro that by-the-way
does error checking and thinks in monads.

~~~
EdwardDiego
Ah yep, that makes sense. Thanks for the detailed explanation :)

Actually, you just reminded me of my recent experience of delving into Go -I
wrote a simple script to read in n YAML files from a directory and merge them
into one, we're using the Prometheus JMX exporter which only accepts one YAML
file, but using several smaller, more focused configs kept the complexity
down.

I used Go to produce a standalone binary so that everything needed could be
brought in from a tiny Docker image as a mixin. Go's default of building
standalone binaries is what tempted me, it was far easier to use Go in a
multi-stage build to create a static binary than it was to try to provide the
same using Python etc.

But yeah, opening a directory, reading its contents, and then consuming all
the files within it required five "if err != nil" blocks. But all I really
needed was for an error in stage N of the chained IO operations to propagate
through the pipeline so that the result of using the pipeline was Some(x) or
None.

------
nathell
Also relevant, Stuart Sierra's opinionated guide to threading macros:

[https://stuartsierra.com/2018/07/06/threading-with-
style](https://stuartsierra.com/2018/07/06/threading-with-style)

[https://stuartsierra.com/2018/07/15/clojure-donts-thread-
as](https://stuartsierra.com/2018/07/15/clojure-donts-thread-as)

------
snorremd
I really enjoy the Failjure library [1] to make threads that short-circuits on
failures. This is pretty close to what monadic error handling looks like in
strongly typed functional languages.

[1]
[https://github.com/adambard/failjure](https://github.com/adambard/failjure)

------
IggleSniggle
Having no experience with clojure, this reminded me of pipeable RxJS syntax,
as well as more generally the sometimes seen use of _ to denote an unused
“threaded value” in more functional styles of js (although _ commonly comes as
the final value in js in combination with unary functions, or will sometimes
be named but unused to assist in establishing context for readability).

------
knubie
Also check out the Swiss arrows project which builds on the idea a bit.
[https://github.com/rplevy/swiss-arrows](https://github.com/rplevy/swiss-
arrows)

~~~
dominotw
not sure why i would use that vs this

[https://clojuredocs.org/clojure.core/as-%3E](https://clojuredocs.org/clojure.core/as-%3E)

------
dominotw
Prevalent use of these macros in clojure code is admission to the following
facts,

1\. Lisp code is hard to read.

2\. Lisp based languages do not have "small syntax".

~~~
EdwardDiego
Nothing to do with Lisp code, because otherwise F# wouldn't have a pipe
operator.

    
    
      f x |> Seq.map g |> Seq.filter h |> Seq.groupBy (fun x -> x.Name)
    

Is just more explicit and obvious than

    
    
      Seq.groupBy (fun x -> x.Name) (Seq.filter h (Seq.map g (f x)))

~~~
dominotw
I don't know much about F# but do you know if language has to be "expanded" to
support that pipe operator?

