Live data from Hacker News

The Monad Called Free (2014)

blog.sigfpe.com

31–40 of 44 posts

Re: The Monad Called Free (2014)

#31
post #11

Earlier quoted context omitted.

A great way to understand monads is as a "design pattern," because they pop up extremely often in practical programming. Consider functions with a type signature that looks like `A -> T `, such as `A -> Promise ` or `A -> Optional `. If you have some function fetchResponse that returns a Promise , and a function processJSON that takes a Response as an argument, you cannot compose them the usual way, as `processJSON(f…

This explanation doesn't contain any practical examples either. I mean, I do know that Promises are a monad, but that's not obviously helpful. You can write plenty of async code using Promises without knowing that, and it will probably be clearer to the reader. Similarly, options are a monad, but that's unhelpful for writing code that deals with optional values. Why is most writing about functional programming like t…

Because FP is useless so there aren't any practical examples. ...but all kidding aside, I kinda feel like an astronomer talking to an astrologer when it comes to this stuff. Like I'm scanning the night sky with my telescope while he's talking about Mars being 'in the house of Jupiter' or whatever. It's the old Abelson quote about computer science all over again.

Re: The Monad Called Free (2014)

#32
post #27

Earlier quoted context omitted.

Those words need definitions, but they can both be defined using words most people know. Casual attempts at defining Monads often just sweep a pile of confusion around a room for a while, until everything gets hidden behind whatever odd piece of furniture that is familiar to the person generating the definition. They then imagine they have cleared up the confusion, but it is still there.

Most engineers don't have too much trouble understanding things like List , or Promise , or even Optional , which all demonstrate vividly what a monad does (except Promise in JS that auto-flattens). A monad is a generalization of all them. It's a structure that covers values of type T, some "payload" (maybe one, like Promise, maybe many, like List, maybe even none, like List or Optional sometimes). You can ask it to…

Short definitions, followed by simple examples that clearly match the definition, are the best way to be clear.

Unlike how we define most things, definitions of monads often run into:

1. Just a lot of words, often almost stream of consciousness.

2. Use of supporting words used in a technical sense associated with the concept being defined. Completely understood by anyone who already knows the concept. Completely opaque to anyone else. Those words should be defined first, or not used.

3. Incorporating examples into the definition, which creates a kind of inductive menagerie. There are no obvious boundaries of a concept, or clarity shed on what is crucial or what is specific in the examples.

Dictionaries and most people don't define words this way, for good reason. It is a collage, not a definition.

--

I just spent too much time working on this. It is a deceptively difficult problem. I am certainly not critiquing anyone. To be completed later! For myself, if no one else.

Re: The Monad Called Free (2014)

#33
post #19

Earlier quoted context omitted.

Forget programming, everyday business and physics is monadic in function. And if-then statements are functorial. These are very general thought patterns.

> everyday business and physics is monadic in function. So? > And if-then statements are functorial. So? All the "this is hard" stuff around these ideas seems to focus on managing to explain what these things are but I found that to progress at the speed of reading (so, about as easy as anything can be) once it occurred to me to find explanations that used examples in languages I was familiar with, instead of Haskell…

There is no 'so?' Haskell tends towards applicatives and monads because monads and applicatives are the preferences of haskellers. Just like JavaScript people may like dynamic typing, etc. These are design choices.

By modeling various things as monads, you get the various principled monad extensions. Unlike normal programming where leaky abstractions are the expectation, using algebraic structures with principled laws means things just work.

But this has nothing to do with monads in particular. Haskell's choice to do a lot with monoids provides a similar guarantee about things that combine . It's a preference. Nothing like monoids exist in other languages, because people are told they have to think with 'objects' of whatever.

Re: The Monad Called Free (2014)

#34
post #29
post #22

Earlier quoted context omitted.

I mean people need to be familiar with mathematics. In mathematics things form things without having to understand them. For example, The natural numbers form a ring and field over normal addition and multiplication , but you don't need to know ring theory to add numbers.. People need to stop worrying about not understanding things. No one understands everything.

I get your point, but the natural numbers do not form a ring or field.

Sorry you're right. I was tired commenting.

S/naturals/rationals/g for rings

and s/naturals/algebraic numbers/g for fields

Re: The Monad Called Free (2014)

#35
post #22

Earlier quoted context omitted.

I mean people need to be familiar with mathematics. In mathematics things form things without having to understand them. For example, The natural numbers form a ring and field over normal addition and multiplication , but you don't need to know ring theory to add numbers.. People need to stop worrying about not understanding things. No one understands everything.

Now imagine if every single explanation of natural numbers talked about rings and fields. Nobody ever just says "they're the counting numbers starting from one." A few of them might say, "they're the counting numbers starting from one, and they form a ring and field over addition and multiplication." And I might think, I understand the first part, but I'm not sure what the second part is and it sounds important, so m…

Well most people explain monads for no reason. I'm probably one of the rare Haskell developers who never explains them to anyone. It has nothing to do with IO.

If someone is concerned with how to do IO in a pure language then I show them how it actually happens in GHC, which is via the type system enforcing only one instance of RealWorld# is alive at once. There is ABSOLUTELY nothing you need to know about monads to understand IO in Haskell. It's just function composition and careful use of case to force the evaluation of a token of type RealWorld#. Nothing magic about it.. you're just passing the state of the world around.

Re: The Monad Called Free (2014)

#36
post #11

Earlier quoted context omitted.

A great way to understand monads is as a "design pattern," because they pop up extremely often in practical programming. Consider functions with a type signature that looks like `A -> T `, such as `A -> Promise ` or `A -> Optional `. If you have some function fetchResponse that returns a Promise , and a function processJSON that takes a Response as an argument, you cannot compose them the usual way, as `processJSON(f…

This explanation doesn't contain any practical examples either. I mean, I do know that Promises are a monad, but that's not obviously helpful. You can write plenty of async code using Promises without knowing that, and it will probably be clearer to the reader. Similarly, options are a monad, but that's unhelpful for writing code that deals with optional values. Why is most writing about functional programming like t…

The practical example is being able to use the same names and utility functions and such on all of the different monads. That's kind of it.

Other than that it's just nice for communication in the right groups, it's shorthand for a whole bunch of properties that you then don't have to explain.

It also only really works super well in languages where the type system is expressive enough to allow that (or just permissive enough not to stop you I guess), so that mostly comes up in fun "functional" languages where they spent a bunch of time on the type system.

You'll probably understand a bit better if you take some time and learn/use Haskell a bit (if you don't already understand, it kind of sounds like you do tbh). It's a fun and educational language in a bunch of ways IMO. It depends on the kind of person/programmer you are if you'll really care though.

Re: The Monad Called Free (2014)

#37
post #20

Earlier quoted context omitted.

It's also important to note that in Haskell and other functional programming languages, there is no implied order of operations. You need a Monad type in order to express that certain things are supposed to happen after other things. Monads can also express that certain things happen "in between" two operations, which is why we have different kinds of Monads and mathematical axioms of what they're all supposed to do.…

There is an implied order of operations in Haskell. Haskell always reduces to weak head normal form. This implies an ordering. Monads have nothing to do with order (they follow the same ordering as Haskell's normalization guarantees). > JavaScript decided to standardize on a Monad-shaped "thenable" specification for representing asynchronous processes, Its impossible for something to be monad shaped. All asynchronous…

> All asynchronous interfaces form a monad whether you decide to follow the Haskell monad type class or decide to do something els

JS's then is categorically not a monad because it doesn't follow the monad laws.

fn1 : a -> Promise

fn2 : b -> c

fn3 : b -> Promise

With JavaScript, composing fn1 and fn2 with then gives you a -> Promise. So then is isomorphic to map.

With JavaScript, composing fn1 and fn3 with then gives you a -> Promise. So then is isomorphic to flatmap.

Therefore, with JavaScript, map is isomorphic to flatmap. Which obviously violates monad laws.

There's a rather famous Github issue where someone points this out in the issue tracker for `then` development, and one of the devs in charge of then...leaves responses for posterity.

Re: The Monad Called Free (2014)

#38
post #21
post #14

Earlier quoted context omitted.

> You need a Monad type in order to express that certain things are supposed to happen after other things This is the kind of explanation that drives me absolutely batshit crazy because it is fundamentally at odds with: > Do you understand "flatmap"? Good, that's literally all a monad is: a flatmappable. So, I think I understand flatmap, assuming that this is what you mean: https://www.w3schools.com/Jsref/jsref_array…

Because you're not used to abstract algebra. JavaScript arrays form a monad with flatmap as the join operator. There are multiple ways to make a monad with list like structures. And you are correct. Monads have nothing to do with sequencing (I mean any more than any other non commutative operator -- remember x^2 is not the same as 2^x) Haskell handles sequencing by reducing to weak head normal form which is controlle…

Nota bene, then is not a monad because the implementation of then implies map is isomorphic to flatmap. This is because `then` turns the return value of a callback into a flat promise, even if the callback itself returns a promise.

That is to say, then checks the type of the return value and then takes on map or flatmap behavior depending on whether the return value of the callback is a Promise or not.

Re: The Monad Called Free (2014)

#39
post #33

Earlier quoted context omitted.

> everyday business and physics is monadic in function. So? > And if-then statements are functorial. So? All the "this is hard" stuff around these ideas seems to focus on managing to explain what these things are but I found that to progress at the speed of reading (so, about as easy as anything can be) once it occurred to me to find explanations that used examples in languages I was familiar with, instead of Haskell…

There is no 'so?' Haskell tends towards applicatives and monads because monads and applicatives are the preferences of haskellers. Just like JavaScript people may like dynamic typing, etc. These are design choices. By modeling various things as monads, you get the various principled monad extensions. Unlike normal programming where leaky abstractions are the expectation, using algebraic structures with principled law…

> Nothing like monoids exist in other languages

Monoids exist in plenty other languages. Hell, I use them in TypeScript.

Re: The Monad Called Free (2014)

#40
post #7

Earlier quoted context omitted.

Right. I don't know how many times I've been exasperated by how monads are perceived as difficult. Do you understand "flatmap"? Good, that's literally all a monad is: a flatmappable. Technically it's also an applicative functor, but at the end of the day, that gives us a few trivial things: - a constructor (i.e., a way to put something inside your monad, exactly how `[1]` constructs a list out of a natural number) -…

> Do you understand "flatmap"? Good, that's literally all a monad is: a flatmappable. Awesome! Now I understand. > Technically it's also an applicative functor Aaaand you've lost me. This is probably why people think monads are difficult. The explanations keep involving these unfamiliar terms and act like we need to already know them to understand monads. You say it's just a flatmappable, but then it's also this othe…

I'm sorry, I wasn't clear. The "technically" was meant to signal "it doesnt' matter to you, but to pedants here who get off on saying "well ACKshually": I didn't forget that, it's just not relevant :D

If you want a little more elucidation, what you need to know, unless you're aiming to be functional programming god, is that:

- a monad is a FLATMAPPABLE - all monads are also applicative functors, which i will explain last bc it's kind of a twist on MAPPABLE - all applicative functors, and thus all monads, are functors, which are MAPPABLEs - an applicative functor is essentially a mappable for functions that take more than one parameter

I think applicative functors are the hardest to grok because it's not immediately obvious why they're necessary. The type signature is strange, and it's like "why would I ever put a function inside a container??" I wrote a lot of functional code in Kotlin and TypeScript before I finally understood their utility. The effect of this was that a lot of awkward code became much cleaner.

So let's begin with functor (i.e., a mappable):

Container

if you have a function Integer to Text, a functor allows you to convert the Integer to Text using a function called `map`. We do this with arrays all the time in Python, JavaScript, etc. It's a very familiar concept, but we don't call it "functor" in those languages.

BUT, what if you have

Container

and the function you want to map with takes two parameters. A classic example is you want to use the Integer as the first argument of a constructor. Let's say Pair.

So if Pair's constructor is: a -> a -> (a, a), you would first map Container with PairConstructor. Now you have Container (Integer, Integer)>.

To pass in the second Integer to finish constructing the tuple, you use the special property of applicative functors. This is often called "ap" (like "map" without the "m").

---

Now, I would say the ACTUAL most important thing about applicative functors is this:

Imagine if you had a list of words. You want to make an API call for each word. API calls are often modeled with the Async monad (which is also, as mentioned above, definitionally an applicative functor).

But if you mapped [Word] with CallApi, you would end up with [Async ApiResult]. This models "a list of successful and unsuccessful API calls."

But what if you wanted Async [ApiResult] instead? (One might say this is an attempt to model "all api calls successful, but if one api call fails, the whole operation is considered a failure."

This is where applicative functors shine: pulling the applicative functor out of the container and wrapping the whole container. (There's more cool stuff to learn about the nature of this "container" but that'd be for another lesson, much like how you don't learn about primitives and interfaces on the same day in an OOP class.)

Recall that constructing a list of N items would be

a -> a -> a -> ... -> a (n times) -> [a]

That looks an awful lot like one MAP followed by (n-1) APs, based on the discussion above! And that's exactly what it is.

You can map the first api call and then ap the rest, and you end up going over the entire list, getting Async [ApiResult].

Now, there are a lot of ways languages go about solving this kind of "fail if one of the operations fails rather than compile a list of all successes and failures."

But the nice thing about using Functors, Monads, etc. is that you have a bunch of functions that work on these things, and they handle a ton of code so you don't have to.

That collection of Words above? It's a list. Lists are a Traversable, and all Traversable have the following function:

traverse: (a -> Applicative b) -> Traversable a -> Applicative Traversable b

The above, the traversable is a list and applicative functor was apiCall, so your code is as simple as

traverse apiCall listOfWords

No juggling around anything. That's it. You know your result will be "list of successful results, or a failure."

---

There are many more of these "type classes," and the real power comes from not needing to write a lot of code anymore because it's baked into the properties of the various type classes. Have a type that can be mapped to an order able type? Bam, now your type is order able and you never have to write a sort function for your type. Etc.

Post reply on HN