3 ms·
> 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
by EdwardDiego 7y ago
> 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 7y agoPossibly 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 7y agoAh 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.