Live data from Hacker News

Monads are monoids in the category of endofunctors

sambernheim.com

211–220 of 241 posts

Re: Monads are monoids in the category of endofunctors

#211

So we cannot talk about a type being monad without also saying under what function? E.g. Arrays are monads under concatenation. So why people then say easily "Option type is monad" without stating under which operation? I assume Option is monad under "all" and "any" function, but not sure.

A monad is a triple:

1. A type constructor M of a single argument 2. A function of type a -> M a 3. A function of type M (M a) -> M a

subject to the monad laws. So the 'array monad' is the triple (Array, \x -> [x], Array.concat) and the 'option monad' is the triple (Option, \x -> Some(x), \x -> match x | Some(o) => o; None => None). Saying a type 'is' a monad is imprecise but just means it has an associated monad instance. I've never seen a type with multiple monad instances but I don't know if there could be one.

Re: Monads are monoids in the category of endofunctors

#212
post #56

Earlier quoted context omitted.

A Mappable would be anything that has a `map` function. A `Functor` is a something that has a `map` function _AND_ obeys the rule that calling `map` with the identity function produces the same result. ie: say I have some value `f` that is a functor. If I call `map identity f` I should always get back `f`. The mere existence of a `map` function doesn't imply this law holds. The identity function always returns it's a…

What is a good example (not contrived) of a mappable non-functor?

To be honest with you, you're right in that you usually get that one without trying. Most of the things we think of as mappable obey the identity rules.

Re: Monads are monoids in the category of endofunctors

#213

As an outsider- the amount of disagreement in this thread speaks volumes.

I don't know if you mean that you are an outsider to HN or an outsider to functional programming.

The problem in this thread is not that monads are controversial or poorly understood. The problem is that there is a strong selection effect: the thread is populated by people for whom the topic of monads is relatively new, and who think that this discussion is worth having in this context. People who are "productive" with monads already know exactly what the structure is (it's not complicated) and they're unlikely to want to enter this discussion. They just have better things to do than engage with the misconceptions of "learners" on the Internet.

HN in general suffers from posters who write confidently about things they don't fully understand. This tends to drive genuinely competent people away from the site. It affects politics as well as tech discussions.

The problem isn't monads, it's the denizens of HN.

Re: Monads are monoids in the category of endofunctors

#214

Earlier quoted context omitted.

The categorical definition of a monoid is different than the usual algebraic one (although one can be recovered from the other) and usually treats a monoid as a single object (hence "mono-"). In particular, usually a monoid is defined as a single object category (sometimes a "monoid object" is defined inside a category, but that's a different presentation). The usual multiple objects of an algebraic monoid correspond…

A monoid is a certain kind of object (potentially one of many) in a certain kind of category (called “monoidal” category).

> We can also talk about a monoid object in a larger category in much the same way.

Indeed that is one way of viewing a monoid. However, the most basic categorical definition of a monoid (and the first one usually introduced in a category theory textbook) views it as a category with only one object, where all the "interesting stuff" happens with the morphisms. A monoidal category and monoid objects are a restatement of that idea in a larger category.

Re: Monads are monoids in the category of endofunctors

#215
post #56

Earlier quoted context omitted.

A Mappable would be anything that has a `map` function. A `Functor` is a something that has a `map` function _AND_ obeys the rule that calling `map` with the identity function produces the same result. ie: say I have some value `f` that is a functor. If I call `map identity f` I should always get back `f`. The mere existence of a `map` function doesn't imply this law holds. The identity function always returns it's a…

What is a good example (not contrived) of a mappable non-functor?

Great question! There's a minority of them, but the ones I've run into the wild all usually boil down to having some internal book-keeping that is either triggered by function calls or can affect a function call.

For example, caches whose eviction calculation is triggered by function invocation aren't functors, even if you can map over the elements in the cache (since just calling any function on the cache can cause certain elements to be evicted). That's a bit of a weird one because caches also usually have some time-dependent behavior (which in general makes notions of things like equality a bit hard to pin down and so moots a lot of laws), but you can also have caches with eviction policies that are not based on time strictly, but rather deterministic heuristics that use the number of function calls as an input to the heuristic.

Another example is certain tree-based data structures that use function calls to rebalance themselves in a way where the rebalancing causes publicly observable changes. Again there just calling map by itself can trigger a rebalancing, regardless of whether identity is passed or not.

Another example is stuff like variants of `Iterator` where just iterating over something creates changes or function calls are logged.

FWIW all these cases of a mappable non-functor can be changed into a mappable functor, simply by "suspending" calling the function until you actually ask for a value (so the `map` call does nothing other than just squirrel the function away somewhere). So often the idea of a mappable non-functor isn't useful so much for truly distinguishing a functor from a non-functor, as it is for pointing out how you should implement `map` in a way that "makes sense."

Re: Monads are monoids in the category of endofunctors

#216

Earlier quoted context omitted.

What is a good example (not contrived) of a mappable non-functor?

To be honest with you, you're right in that you usually get that one without trying. Most of the things we think of as mappable obey the identity rules.

I think the confusion is calling anything that doesn't "obey the rules" a map function just because it is named "map".

For example, ruby's hash "map" returns an array instead of hash. That's not really a map, is it?

Making this useless distinction between "things with map function" and "functors" serves no purpose. It should be "things with real/fake map functions".

The name of the function does not matter!

Re: Monads are monoids in the category of endofunctors

#217
post #24

This post is very wrong. Being a monoid in the category of endofunctors means something very different from being a monoid in the normal sense (they're related in a category-theoretic way, but the similarity is at a very abstract level). Simple sequences have a lot of structure which mean they are a lot of different things; it is true that sequences are all of monads, functors, and monoids, but the latter structure i…

> means something very different from being a monoid in the normal sense Yet you didn't say how exactly different. I understand I probably need to know category theory for that, yet nevertheless OP's post was useful, fulfilled some curiosity, but your comment didn't, besides leaving feeling "it's complex", sorry. I'd really appreciate if someone one day wrote as clear explanation of monads as OP's, but at the same ti…

Let me give it a shot. Monads as in category theory are a bit more abstract than this explanation, but for the purposes of programming you can think like this:

A functor has a `map` mechanism, which takes a value A and a function B, and returns something that has the same type as A. A simplistic example of a "mappable" that isn't a functor is `const map = setTimeout`: it technically take a value A (the time) and a function B, but `map(fn1, map(fn2, 1))` is semantically nonsensical even though all the types technically align and the code runs (because the effective "type" of the return value does not use milliseconds as a unit of measurement, and effectively causes undefined behavior).

A monad, in addition to being a functor, must also not have extra funky semantics. A example of a functor that isn't a monad is `Promise`. Imagine we did this:

    TimeSequence = Promise
    TimeSequence.of = TimeSequence.resolve
    TimeSequence.prototype.map = TimeSequence.prototype.then
    t1 = TimeSequence.of(1) // t1 has type TimeSequence
    t2 = t1.map(v => v * 2) // t2 has type TimeSequence
    // BUT
    t3 = t2.map(v => TimeSequence.of(v)) // t3 has type TimeSequence, NOT TimeSequence>
The Array equivalent is:

    SpaceSequence = Array
    a1 = Array.of(1) // a1 has type SpaceSequence
    a2 = a1.map(v => v * 2) // a2 has type SpaceSequence
    a3 = a2.map(v => Array.of(v)) // a3 has type SpaceSequence>
    a4 = a3.flat() // this is a separate operation to get a SpaceSequence out of a3
Here, Array is "monadic" because it has both map and flatMap as separate operations. Promise conflates the two, hence it's not monadic.

(Note: I say Array is "monadic" in quotes, because this is only true if we constrain its uses to patterns that conform to monadic laws. If you do `array.map(parseInt)`, that's valid JS, but as far as monads go, all bets are off!)

Also, note that I renamed a bunch of methods (resolve => of, then => map) and called things `Sequence`s. This is the value of functional orientation as it pertains to programming: when we say we've implemented monadic TimeSequence and SpaceSequence, in practical terms that means that I can write code for one type and reuse it for the other type because both follow the same semantics within one larger, common context. It's a lot easier to say "these implementations are not monadic" than it is to say "Promise-based logic is not compatible with Array-based logic because the method names are all different and the semantics of flattening Promises of Promises don't match the semantics of Arrays of Arrays (or Streams of Streams, etc)".

If this is still too abstract, here's an application: suppose I'm writing a task runner that takes tasks and runs them in parallel (but queues once we reach a parallelization limit). I can serialize this representation in space by turning a array of arrays into JSON. But I can also represent this as a grid of timings (meaning, for example, I can write shared code to iterate over both representations, rather than hard-coding different implementations for each type of grid)

Now, you could make an argument that in isolation and within some artificial constrainst, both Array and Promise are monadic (i.e. Promise> works just fine), but saying that's monadic is basically pedantic wankery, since for practical purposes, you can't arbitrarily write code against a specification to abstractly treat promises and arrays as an implementation of a common abstraction (e.g. a sequence of some sort)

Re: Monads are monoids in the category of endofunctors

#218

So we cannot talk about a type being monad without also saying under what function? E.g. Arrays are monads under concatenation. So why people then say easily "Option type is monad" without stating under which operation? I assume Option is monad under "all" and "any" function, but not sure.

A monad is a triple: 1. A type constructor M of a single argument 2. A function of type a -> M a 3. A function of type M (M a) -> M a subject to the monad laws. So the 'array monad' is the triple (Array, \x -> [x], Array.concat) and the 'option monad' is the triple (Option, \x -> Some(x), \x -> match x | Some(o) => o; None => None). Saying a type 'is' a monad is imprecise but just means it has an associated monad ins…

Wow that's clearer. I wonder if just a list of examples (and counter-examples) would be enough to explain monads (preferrably with configurable syntax for Haskell-deprived folks).

Btw, I think you got a mistake there, in the third part of Option should be "Some(Some(o)) => Some(o), Some(None) => None, None => None", otherwise o can be not an Option at all.

Re: Monads are monoids in the category of endofunctors

#219

So we cannot talk about a type being monad without also saying under what function? E.g. Arrays are monads under concatenation. So why people then say easily "Option type is monad" without stating under which operation? I assume Option is monad under "all" and "any" function, but not sure.

Yes, but its usually obvious which one is meant, so you don't bother saying under what operation. Option is a monad under the same concatenation as Array, just think of them as arrays that always have length 0 or 1.

How to concatenate Option-s? Btw your comment seems to contradict another sibling comment.

Re: Monads are monoids in the category of endofunctors

#220
post #162

Earlier quoted context omitted.

> The precise terminology is vital; it's how we avoid these errors when talking about this very abstract stuff. Given that the OP is very wrong, precisely because of the kind of attitude you've taken here, I hope you'll take this opportunity to reconsider your views. Your behavior is precisely the behavior that someone new to FP would see and say "Hard pass on that", which goes into the greater problem of FP being un…

You don’t have to understand category theory to program in FP languages. But if we want to talk about the mathematical basis, you do need a common vocabulary at least to be able to communicate at all. You could not really explain JIT compilation to someone not knowing anything about computers, could you?

> You could not really explain JIT compilation to someone not knowing anything about computers, could you?

You can't explain the concept of doing something only when you absolutely need to, to someone that doesn't know about computers?

Post reply on HN