[HN Gopher] Monads for Clojure Programmers
       ___________________________________________________________________
        
       Monads for Clojure Programmers
        
       Author : Terretta
       Score  : 53 points
       Date   : 2021-10-04 10:32 UTC (1 days ago)
        
 (HTM) web link (cuddly-octo-palm-tree.com)
 (TXT) w3m dump (cuddly-octo-palm-tree.com)
        
       | adamgordonbell wrote:
       | A monad, as thought of in programming, is really something that
       | requires higher kinded types.
       | 
       | The reason they are hard to explain in a language without HKT is
       | the same reason why List[T] would be hard to explain in a
       | language without generics.
       | 
       | edit: The article is impressive. It's just it feels like a
       | different thing to me to than what monads are in haskell. I guess
       | it would have to be.
        
         | mping wrote:
         | You can have monads in clojure (eg:
         | http://funcool.github.io/cats/latest/#monad) but the problem I
         | see is that they are infectious and kinda force you to use them
         | everywhere. In other languages, it's more palatable because the
         | compiler checks for types, and normally there is some sugar to
         | make it easier on the eyes. Besides, I think monads are a bit
         | off the clojure way, even if they happen to exist somehow (eg
         | promesa and manifold libraries).
        
           | goostavos wrote:
           | And _without_ the compiler checking the types, they 're
           | nightmarish to use. It falls to pure human sweat to track if
           | a variable holds a `thing` or a `Monad Thing`. In my humble
           | experience, it leads to lots of tedious banging around in the
           | REPL to track down where you made a mistake. This is
           | especially painful while refactoring.
           | 
           | I've given up on the monads in dynamic languages outside of
           | really strict confined situations (validation for instance).
           | I think it's ultimately a bit silly to do something like
           | `StateT` in Clojure rather than just using an `atom` or
           | whatever. What you lose in referential transparency and
           | purity, you more than make up for in sanity over the long
           | haul.
           | 
           | The scales would tip if Clojure had stronger typing options,
           | but for now, using the idiomatic stuff is the way to go.
           | Although, spec could probably be used in a pinch, but it's
           | kind've hot garbage compared to compiler checked types
           | (imho!).
        
         | elwell wrote:
         | I love your podcast.
        
           | adamgordonbell wrote:
           | Thanks!
        
         | [deleted]
        
         | platz wrote:
         | It's not that monads "require" higher kinded types, it's that ,
         | like an interface, monads only start really "buying" their
         | "cost" when there are _many_ different things that can be
         | monads.
         | 
         | Then the common abstraction of the monad can be used to
         | interact with these things that all share this common property,
         | in a unified way.
         | 
         | Finally, higher kinded types is what makes the idea of _many_
         | different things that can be monads manageable for the
         | programmer, because if the underlying thing that the monad
         | represents keeps changing into different forms, it's very easy
         | to loose track of what structure or concept it currently
         | represents without the types to keep track of that for you.
        
           | adamgordonbell wrote:
           | yeah, that's better put.
           | 
           | The usefulness of it, to me, is how the bind/flatmap can be
           | generic over type constructors, but type constructors aren't
           | a clojure concept. It's a static typing thing.
           | 
           | To have an interface for monads, you need to have HKT don't
           | you? To declare the type of bind?
           | 
           | It could be useful otherwise. It would just seem like a
           | slightly different thing.
        
             | fiddlerwoaroof wrote:
             | Someone once told me "continuations are isomorphic to
             | monads" and I eventually realized that the best way to do
             | something monad-like in JS was to write functions that take
             | a callback as their last argument and then higher order
             | functions that manage threading the callbacks around,
             | occasionally transforming it to slot in impure behaviors.
             | 
             | I really disagree with the notion "HKT are necessary for
             | monads", though: they're necessary to statically check that
             | your monadic code is correct, but asside from things that
             | rely on return-type inference, you can just implement those
             | type-checks as runtime checks (or leave them out entirely):
             | if the architecture of side-effects in your app is arranged
             | around monadic principles, you can write all the same
             | generic transformations (and, in fact, designing your
             | dynamically typed code this way can get you a good fraction
             | of the correctness benefits of static typechecking)
        
               | fiddlerwoaroof wrote:
               | One example here. CLOS-style multimethods are a
               | reasonably good runtime analog of the way typeclasses
               | work: typeclasses use type inference to thread a
               | dictionary of functions around, multimethods do roughly
               | the same thing with dynamic type-checks. This means
               | multimethods have overhead typeclasses don't have, but it
               | also means that multimethods don't have the "closed-
               | world" assumption and can be extended dynamically.
        
             | jerf wrote:
             | You can also theoretically go the completely opposite
             | direction and use dynamic typing. You have the usual
             | tradeoffs related to that, plus maybe a couple new
             | wrinkles, but it can work.
        
           | jerf wrote:
           | I think there's a lot of people confusing the bricks of
           | functional programming with what you can build with them.
           | 
           | The interesting thing about 'monads' isn't bind and return.
           | The interesting thing is the Control.Monad functions that
           | build on them, and by making something that conforms to the
           | monadic interface, you get to use them: https://hackage.haske
           | ll.org/package/base-4.15.0.0/docs/Contr...
           | 
           | Just like the special thing about iterators isn't that you
           | can get the "next" thing. That's not impressive on its own.
           | The magic is in the tools you get that can generically use
           | and manipulate iterators if they conform to a particular
           | interface.
        
             | lalaithion wrote:
             | Example of a time when the Control.Monad functions were
             | useful to me:
             | https://lalaithion.xyz/posts/practical_monad.html
        
             | endgame wrote:
             | Exactly this. The power of an abstraction is roughly the
             | product of two metrics:
             | 
             | 1. How many things "fit" this abstraction?
             | 
             | 2. What does this abstraction let me say?
             | 
             | The abstraction "Monad" has many inhabitants, and the
             | Control.Monad module shows that it lets you say many things
             | -> it is a very useful abstraction. The abstraction
             | "Applicative" has many more inhabitants, but lets you say
             | fewer things -> it is still a useful abstraction.
        
         | AzzieElbab wrote:
         | agreed but still impressed by authors effort
        
           | tessierashpool wrote:
           | yep. as the OP said: _There is a reason why most monad
           | tutorials use Haskell: without syntactic support, monads are
           | pretty clunky to use._
           | 
           | (You could probaby go further and say "without semantic
           | support.")
        
       | didibus wrote:
       | I think Monad make more sense when you understand the context and
       | its additional constraints as to why you'd want to use Monads.
       | 
       | Imagine that you want to write code that is strictly pure, so
       | that when evaluated, it doesn't do any side effect, it only
       | returns a pure description of the computation it did.
       | 
       | Here's a simple example:                   func announce-good-
       | catch(fish)           if(fish.size > 5)             print "We
       | found a big one!           else             print "That one won't
       | even feed a cat, try again!"
       | 
       | How can you make this function pure? It technically prints
       | something, yet you want to run it and not have it print, but
       | instead return you something that describes what branch it took
       | and what effect it would have done had it been impure.
       | 
       | You can do this:                   func announce-good-catch(fish)
       | if(fish.size > 5)             return [:print "We found a big
       | one!]           else             return [:print "That one won't
       | even feed a cat, try again!"]
       | 
       | Now the function is pure, it doesn't print anything, it evaluates
       | the branch to take based on input and return to you a description
       | of what it should do.
       | 
       | The problem is programming that way is hard, let's add another
       | function:                   func get-fish(fish-id)
       | return new Fish(db.query("select * from fish where id = " + fish-
       | id)
       | 
       | Again this isn't pure, so let's make it pure:
       | func get-fish(fish-id)           return [:new Fish [:query [:get
       | :db] ["select * from fish where id = " + fish-id]]])
       | 
       | Already this is getting pretty complicated, getting the DB is
       | impure so return a description of it, querying the DB is impure,
       | so return a description of it, Fish can't be created if we don't
       | execute the side-effect, so take note that you want to create a
       | Fish after those two side-effects were to actually happen.
       | 
       | Now combine get-fish and announce-good-catch(fish)?
       | announce-good-catch((get-fish 10))
       | 
       | Well this works in the impure version, but this doesn't work for
       | our pure versions because now the input to announce-good-catch
       | isn't an actual Fish but instead it's this:
       | [:new Fish          [:query           [:get :db]
       | ["select * from fish where id = 10"]]]
       | 
       | And so it's not going to work, you can't compose those pure
       | functions as you'd normally would.
       | 
       | Here is where Monads become really handy, they put all this
       | behind an abstraction that lets you with added syntax sugar feel
       | like your code is written in the impure style, yet under the hood
       | it will convert things to a description of all side-effects and
       | whatever pure behavior is pending the result of those. And it'll
       | let you compose those descriptions together so that from the
       | programmer point of view the code you write looks and feels
       | almost the same as the impure version.
       | 
       | This is the problem Monad solves.
       | 
       | Now once you have that description, eventually you want it to
       | actually run for real so that side-effects and all other
       | described pending behavior gets executed. But what people
       | realized here is you can kind of abuse this a bit, where you
       | could choose to interpret the description in new ways, or
       | manipulate the description prior to executing it. Basically
       | whatever executes the description for real is in charge of how to
       | do that and it could choose to change some of the assumptions
       | like the evaluation order or the thread on which thing execute,
       | etc.
       | 
       | Thus once you have Monads, you can also use them to manipulate
       | evaluation order and semantics.
       | 
       | Now if you don't care about constraining yourself to pure
       | functions, all this can seem like overkill, and it kind of is.
       | 
       | Obviously, people will tell you it's a good idea to make
       | everything pure, because you can more easily test things.
       | 
       | And people will tell you, even if you don't care about the
       | purity, how Monads let you manipulate evaluation order and
       | semantics of the code can be cool and allow for some neat
       | features, so maybe that's something you'd find useful, such as
       | adding tracing throughout automatically, or automatically
       | parallelizing things that don't depend on each other, etc.
       | 
       | This is where some people say that Macros can often be used to
       | similar effect. Because macros, a bit like Monads, operate on the
       | description, macros on the code's AST itself, Monads on the
       | captured Monadic composition. So a macro can also rewrite things
       | to change their evaluation order or semantics.
       | 
       | I wouldn't call myself an expert on all this yet, more of an
       | enthusiast, but that's how I would contextualize it.
        
         | Koshkin wrote:
         | I think the List monad is easier to comprehend.
        
           | didibus wrote:
           | The List Monad is the same, if a function called with the
           | same input can return one of many results, each time it is
           | called it could return a different output. That means this
           | function isn't pure, because a pure function maps the same
           | input to the same output even when called repeatedly.
           | 
           | The difference here is instead of returning a description of
           | the impure things that should be done, you can make the
           | function pure by returning the list of all possible options.
           | 
           | That means instead of getting a different output non-
           | deterministically for the same input each time the function
           | is called, you now get the same output everytime, which is
           | the list of all possible outputs for that input.
           | 
           | So the context is the same, you want to constrain yourself to
           | pure functions for some reason, so how do you make this pure?
           | func favorite-color(person-name)           if(person-
           | name.startsWith("A")             return randOf("blue",
           | "green")           else             return randOf("yellow",
           | "black")
           | 
           | The trick is to have randOf return a list of all options such
           | as                   randOf("blue", "green")         =>
           | ["blue", "green"]
           | 
           | Now you have the same composition problem as before:
           | func generate-favorite-color-string(favorite-color)
           | return "My favorite color is: " + favorite-color("Abel")
           | generate-favorite-color-string(favorite-color("Abel"))
           | 
           | But this won't work again, so you need a Monad once more to
           | help you with composing these pure versions of your impure
           | functions so that:                   bind(favorite-
           | color("Abel"),              generate-favorite-color-string)
           | 
           | Which says: compose generate-favorite-color-string with
           | favorite-color using Monads
           | 
           | And now what the Monad composition will do is it will
           | automatically run generate-favorite-color-string for each
           | element in the list returned by favorite-color and itself
           | will return a list of all those possible results:
           | ["My favorite color is: blue",          "My favorite color
           | is: green"]
           | 
           | So the composition is also pure now.
        
       | tfwwtn wrote:
       | related: https://lispcast.com/are-monads-practical/
        
       | Koshkin wrote:
       | What is described looks to me like an interpreter for a Forth-
       | like DSL in which the notion of the monad should find its
       | "natural" expression. Still not sure if it can indeed be viewed
       | as a practically useful extension of the base language
       | (Clojure)...
        
         | sesm wrote:
         | I'm not an expert, but my understanding is:
         | 
         | 1. You are writing a DSL that works with wrapped values.
         | 
         | 2. In this DSL you can use arbitrary pure functions from your
         | base language.
         | 
         | 3. The value proposition is: those pure functions should not be
         | aware of the fact that they are working with wrapped values,
         | they are written as if they are computing regular non-wrapped
         | values. Bind takes care of this.
         | 
         | 4. Indeed the simplest way to implement this is some kind of
         | interpreter. But you don't have to re-implement the entire
         | language inside this interpreter, because of #3
        
       | [deleted]
        
       | sova wrote:
       | Can someone give me a practical example of when one would want to
       | use a monad? Typically I don't want side-effects to all live in
       | one thing. It's cute but "we want to change the world," don't we?
        
         | anchpop wrote:
         | Two common examples from the Ocaml world are promises through
         | the LWT library and options.
         | 
         | If you just wanted to open a file, maybe you would write
         | something like this:
         | run(create_file("filename"))
         | 
         | Here `create_file("filename")` is a pure value, no side
         | effects. It's a value that represents the concept of creating
         | `"filename"` (and maybe returning a handle). When passed to
         | `run`, that value is inspected and only then the actual action
         | it corresponds to, creating a file, is executed.
         | 
         | What if you want to do something with the file? That looks like
         | this:                   let create_then_delete =
         | and_then(open_file("filename"), (handle) ->
         | delete_file(handle))         run(create_then_delete)
         | 
         | `create_then_delete` represents the concept of opening a file
         | and giving it to `(handle) -> delete_file(handle)`. If you want
         | to actually do that, you pass `create_then_delete` to `run`.
         | 
         | Functions with a type signature similar to `and_then` turn out
         | to be pretty common, so it's nice to have a way to talk about
         | them. That's basically what monads are for. In OCaml, all
         | functions that have a similar type signature can use the new
         | "let-monadic syntax", which basically lets you rewrite
         | and_then(open_file("filename"), (handle) ->
         | delete_file(handle))
         | 
         | to                   let\* handle = open_file("filename");
         | delete_file(handle)
         | 
         | Which is a lot more readable if you ask me. Any type that has a
         | function like `and_then` works with this syntax, and those
         | types called monads.
        
         | massung wrote:
         | I believe that most descriptions and blogs about monads
         | completely miss the point.
         | 
         | Monads are a tool. And yes, they can be used for things like
         | having side effects in pure code or allowing for the chaining
         | of functions, but those are just tangential benefits.
         | Fundamentally, monads are just a way to categorize data, and
         | the downstream results of that data. In the end, this gives us
         | the ability to codify what we - as programmers - already know,
         | and therefore depend on.
         | 
         | Consider the following mad-lib:
         | 
         | > Any value computed using the result of ____ must also - by
         | definition - be the result of ____.
         | 
         | Now, let's plug in a simple example with "asynchronous
         | function":
         | 
         | > Any value computed using the result of an asynchronous
         | function must also - by definition - be the result of an
         | asynchronous function.
         | 
         | If I call a function call `httpGet` that returns
         | `Future<Response>` and then I take the response and parse the
         | HTML into a DOM object, I know - as a programmer - that the DOM
         | object is of type `Future<DOM>`. I know this whether or not the
         | programming language I'm using is dynamically typed, supports
         | higher-kinded types, or not.
         | 
         | Whether or not you prefer to live in Haskell land, Go land,
         | Clojure land, or Flatland, monads are just a tool for you - the
         | programmer - to categorize your data and codify (at compile
         | time and/or runtime) what you already know.
        
         | endgame wrote:
         | Monads are not "for side effects". Monads are a programming
         | pattern to facilitate code reuse. Any time you have a type
         | constructor M (like List<_>, say) that supports the following
         | operations:
         | 
         | 1. pure :: T -> M<T>, and
         | 
         | 2. Any one of the following three (you can derive any of these
         | from any other)
         | 
         | join :: M <M<T>> -> M<T>
         | 
         | bind :: (M<A>, A -> M<B>) -> M<B> -- sometimes called "andThen"
         | 
         | pipe :: (A -> M<B>, B -> M<C>) -> (A -> M<C>)
         | 
         | If they interact with each other in a "sensible" way (the
         | "monad laws"), you have a Monad.
         | 
         | ---
         | 
         | What things fit this abstraction? I think the most familiar one
         | to mainstream programmers would be Promises. If you have a
         | Promise<Promise<A>>, you can join them together to get a
         | Promise<A>. If you have a Promise<A> that you need to do some
         | further work on, using the bind operation with a function (a ->
         | Promise<B>) lets you "bind a name" to the result of the first
         | promise and return a new Promise<B>.
         | 
         | If you'd like to be explicit about null-checks, then the
         | Option<A> type lets you build up a computation without writing
         | `if (foo != null)` everywhere. A variant, Result<E, A>, lets
         | you stop on the first error. Tracking explicit error
         | information in this way has been invaluable when taming a large
         | ruby codebase - see the dry-monads project.
         | 
         | Any time you want to describe interactions with a system, this
         | abstraction gives you something like the "command design
         | pattern" on steroids. I have used Transaction<A> types to
         | representing database transactions, ensuring that arbitrary IO
         | doesn't happen inside a transaction (you can't unsend an email
         | if the transaction rolls back). A function of type
         | ((Connection, Transaction<A>) -> IO<Result<E,A>>) executes the
         | DB operations inside a DB transaction and either returns the
         | transaction result or an error.
         | 
         | None of this is "we want to change the world lol". I want to be
         | able to notice the common elements between several type
         | constructors, and be able to work on them using a common
         | vocabulary, instead of reinventing wheels.
        
       | JoelMcCracken wrote:
       | IMO, the best articles I know of on monads, which take two very
       | different approaches:
       | 
       | - http://www.jerf.org/iri/post/2958
       | 
       | - https://philipnilsson.github.io/Badness10k/escaping-hell-wit...
        
       | sesm wrote:
       | See also https://github.com/clojure/algo.monads - part of Clojure
       | standard library which has definitions of common monads and links
       | to tutorials
        
       ___________________________________________________________________
       (page generated 2021-10-05 23:01 UTC)