35 ms·
Functors and Monads for People Who Have Read Too Many “Tutorials”
- nicolasrusso 5y agoI know this is just for Haskell but someone needs to start explaining category theory at a level 10 year olds can understand or something. Have struggled with confusion in the same way there. So far all I got from this is that functors are functions?
- dan-robertson 5y agoI think category theory is just the wrong way to look at it. A category is quite an abstract thing and it takes a reasonable mathematical background just to have some good examples to work with (I don’t really think type system categories are good to work with.) I prefer to just write down the type and stare at it and think about what sort of functions could have that type. Later you can look at the rules. And this mostly means one needs to have written a little (eg) Haskell so that the one can read simple types. The signature of fmap looks like: fmap :: (a -> b) -> functor a -> functor b It should easy to guess what sort of types and functions could satisfy that. The rule restricts the possibilities more: fmap (\x -> f (g x)) a == fmap f (fmap g a)
- chobytes 5y agoI got a bit obsessed with this stuff (CT, HoTT, haskell, etc) and ended up studying math in college because of it. My advice is not to bother with CT. Its out of context, very abstract math, and you wont find any compelling examples or applications at any accessible level. I dont want to discourage you from learning math, but I think this isnt a good place spend too much thought at first.
- youssefabdelm 5y agoMy interest in category theory is for a completely different reason. I too have had to struggle to understand it, but I don't plan on using it in an applied or programming sense. I've heard a bunch of people say that category theory is almost like a 'bird's eye view of mathematics'. That many of its concepts unify many mathematical concepts under one umbrella, and reveal the similarities and connections between mathematical subdisciplines. The main reason this interests me is that I'd like to learn math much differently than its taught. To me, to learn the same concept under different subdisciplines with different names, is O(N) learning. However, if someone put together a category theory book where they list a concept, and then they do this: "Now, any mathematician can (easily) see that every major area of mathematics is a category. - In Set Theory the arrows are functions - in Topology they are continuous functions - in Group Theory homomorphisms - in Linear Algebra they are linear transformations - in Differentiable Geometry they’re smooth maps, and so on… But what’s important is that we shifted from focusing on the objects to focusing on the functions, the ways in which we transform the objects. This is basically the category-theoretical perspective: it’s the functions that matter." (Taken from this blogpost: https://catsinthejungle.wordpress.com/2008/11/15/category-theory-for-non-mathematicians/ https://catsinthejungle.wordpress.com/2008/11/15/category-th... ) Do I understand any of that? No. But what attracts me to this format is it (MAYBE...) approximates O(1) learning in math, so long as it can also point out, for every concept in every field, how they differ/relate to every other concept. So what this does is it helps teach me the 'unifiers' of mathematics, and helps me see connections between fields which can be useful for creativity. E.g. I realize I'm doing X algorithm here from linear algebra and that is O(N), whereas if I just shift fields, and use Y algorithm from Group theory I might make it O(log n) because it differs in this way or whatever it is. Besides that I see it as a potentially efficient way of learning a lot of math.
- chobytes 5y agoMath is at its heart about intuition; formalism is a just tool. One uses the formalism but one has to see past it. Roughly its the difference between knowing that a group is a set with a certain binary function, and knowing that a group is a particular formalization of symmetry. While a shortcut to understanding sounds nice, I don't think you'll find it by focusing on abstract formalism.
- youssefabdelm 5y agoTo me this would be another way of developing intuition, via analogy across disciplines. E.g. Volk's Metapatterns draws patterns (but perhaps less rigorous and more loose) across discplines giving one intuition about many things. In this case, seeing the connections across the disciplines isn't necessarily something I see or am pursuing because I'm searching for a shortcut (although I did mention O(1)), I just see that as a side benefit. What appeals to me more is developing the intuition you mention via the analogies. I feel like if the idea is restated in different ways (not necessarily just terms, but perhaps visualizations/re-conceptualizations) a more holistic/intuitive/fuzzy idea emerges rather than anything rigorous necessarily, but you know a lot more math than me so maybe I'm saying nonsense.
- chobytes 5y agoTo be honest if you're sufficiently motivated and you find it interesting then go with it. Better to be motivated about an idiosyncratic method than ambivalent about the whole thing.
- chombier 5y agoA functor is two things: - a parametrized type, like for instance List: you can have lists of integers, floats, functions, or any other type - a way of mapping (in the sense of List.map) between instances of the parametrized type, given regular functions. This mapping should behave nicely with respect to regular function composition. If your parametrized type is F, then the mapping operation turns ("lifts") regular functions A -> B into functions (F A) -> (F B) in a reasonable way: "if you give me a function then I can transform any list using it". Functors can be used to "decorate" values: you can model exceptions (some value or an error), promises (some value not yet computed), state (some value + side effects) and many other kinds of effects using functors. So in general an effectful computation will have type A -> (F B) for some functor F modeling the effect. And now you have a problem: how to compose effectful computations in order to build larger programs out of smaller ones? You cannot simply compose A -> (F B) with B -> (F C) since the types don't match. What you need is a way of transforming an effectful computation B -> (F C) into a lifted function (F B) -> (F C) so that you can compose A -> (F B) with (F B) -> (F C) in order to get A -> (F C). That device should satisfy some properties to make effectful composition work the way you expect (in particular there should exist identity effectful computations), and is called a monad. A monad is a functor + some extra operations (bind/return) satisfying properties that will make effectful computations (using this functor) composable. The type of bind is generally written (F A) -> (A -> F B) -> (F B) but really it is the same as (A -> F B) -> (F A -> F B) above (takes an effectful computation, gives back a lifted function).
- bruce343434 5y agoClearer and more succinct than TFA, thank you
- rocqua 5y ago> What you need is a way of transforming an effectful computation B -> (F C) into a lifted function (F B) -> (F C) so that you can compose A -> (F B) with (F B) -> (F C) in order to get A -> (F C). isn't this just applicative? What is needed to turn applicative into monad?
- datatrashfire 5y agoThis was better than the tutorial, thank you.
- adamnemecek 5y agoThe idea of "adjointness" is present like everywhere in math. I wrote up something on it https://github.com/adamnemecek/adjoint https://github.com/adamnemecek/adjoint.
- baryphonic 5y agoThe functor description is quite good. If the author is reading this, I might make a suggestion: connect the hash map example to the function example. Mathematically, a function is typically defined as literally a (potentially infinitely-long) map of unique keys (domain) to values (codomain). In programming, the two are not identical, especially in mutable languages, but the hash map example would be like memoizing function composition for a set of inputs specified as keys in the hash map.
- void_mint 5y agoI have enjoyed Jerf's content for years. I really enjoy this content as well. Jerf if you read this, excellent stuff, thank you.
- cole-k 5y agoI agree wholeheartedly that Functors and Monads should be thought of as interfaces. And that much of what complicates them is separating the implementation details from the interface details. However, I can't help but feel that the author and I have some common flaws. The first being verbosity. I often notice the same thing in my own writing: I will see a well-written paragraph and think "this is good stuff" and then wonder "but what purpose does it serve here?" Stuff like dedicating 5 paragraphs to discussing whether Functor should be a noun (I get the point, but that's a lot of words to make it), or a paragraph with caveats about how Haskell does name resolution. The second being that we both seem to have fallen victim to the monad tutorial fallacy (https://byorgey.wordpress.com/2009/01/12/abstraction-intuition-and-the-monad-tutorial-fallacy/ https://byorgey.wordpress.com/2009/01/12/abstraction-intuiti...). But I'm not 100% sure about this one. I don't think it's wrong to call monads interfaces in the same way that it's wrong to call them burritos. However, our framework of thinking does seem to align with what that author refers to as fallacious. Still was a good enough read that I felt the need to comment partway through :)
- Dylan16807 5y ago> the monad tutorial fallacy The paragraph starting with "I am aware of the notorious effect" directly addresses this, do you disagree with it? Also calling it an interface isn't an analogy.
- cole-k 5y ago> The paragraph starting with "I am aware of the notorious effect" directly addresses this, do you disagree with it? I suppose my answer should be "yes," but let me try to explain my convoluted reasoning: Understanding monads simply in terms of interfaces is what I think is the silver bullet to comprehending them. It's what helped me "get" them. So they are my "burrito." > Also calling it an interface isn't an analogy. I agree, I apologize if I came across this way.
- jerf 5y agoThe verbosity is a direct effect of the several discussions I've had over the years on this topic. It has actually had a number of words removal and paragraph revival passes done on it.
- lambdasquirrel 5y agoAfter years of seeing people try to understand abstractions like a functor from the definition first, I’ve started to think it’s the wrong way to do it. This post helps on some of these points, like explaining that e.g. functors are not always containers. I’d love to see someone try taking it further: just hashing out lots and lots of substantially different examples, showing how they are similar to each other, and progressing into higher level transformations, before finally showing the definition of a functor, and how it maps to each of those cases. Forget monads. Can we just start with functors and see how far we get with that?
- nerdponx 5y ago> I’d love to see someone try taking it further: just hashing out lots and lots of substantially different examples, showing how they are similar to each other, and progressing into higher level transformations, before finally showing the definition of a functor, and how it maps to each of those cases. Forget monads. Can we just start with functors and see how far we get with that? I think this is how a lot of people learn math and programming anyway. Build theory out of intuition.
- EForEndeavour 5y agoSadly, in my experience, math is often taught theory first, followed by almost trivial or an insufficient number of examples, with more helpful examples left as exercises for the reader with no solution provided. Post-secondary math classes were actually amazing to cultivate the habit of persisting through long periods of feeling like a complete idiot. But I have to wonder what the same content would have been like had it been example-driven. It reminds me of learning a language through exposure to endless examples and internalizing rules without being explicitly taught them, versus deliberately memorizing the grammar rules of a foreign language and then applying the rules to specific examples.
- dietr1ch 5y agoI really like when they present examples and counterexamples to help you realize the subtleties of the definition. Often when presented with a definition it's easy to get fixed with examples that are too specific and then to conflate the definition with something a bit stronger.
- valenterry 5y agoThe article doesn't mention lawfulness until the very end. But lawfulness is a crucial part of the definition and usage. Because, a lot of the convenience and ergonomics that we can derive using these abstractions comes from being able to rely on the laws. Without them, they are just not really useful. And when looking at the examples, I can totally see that someone reads > That's all Monad is. It is an implementation of some "method" that conforms to that interface specification. and then makes "Set" or "HashMap" implement the interface unlawful and bad things happen when they are least expected.
- Osiris 5y agoWhat do you mean by lawful? This is the first time I've ever heard someone use that term in this context.
- dllthomas 5y agoMonad and Functor make some additional promises, beyond what can be expressed in the types, that some users rely on. In the same way that we expect addition of numbers to obey the commutative law and associative law. (Except, of course, that floats don't...)
- valenterry 5y agoFloats are a great example actually. Implementing "unlawful monads" would be equal to giving someone a float and telling them "you can use this like a rational number from math". And in fact it might work for the longest time - until it doesn't (think rounding-errors and similar) and then the confusion will be big. With monads it's the same, but potentially even worse, because it's rather easy to test float addition behavior, but for certain monads that can be much harder.
- marcosdumay 5y agoHaskell has a library called monad-validate that I think is a great example for both what the monad laws are important for, and how it's important that one understand their reasons, instead of just following advice literally on every possible situation. Just as yo want to break addition commutativity so you can approximate real numbers, there are times when you want something to behave like a monad, but not follow its laws.
- Osiris 5y agoLet me see if I can sum this up... Functor = map Monad = flatMap Did I understand the article correctly? edit: this is an honest question. I way I read his explanation of Functor is that it's a function that implements map (list as input -> list as output). His explanation of Monad said that its an input of List, where each item returns it's own List, then the function returns a flat List of all the results.
- BoiledCabbage 5y agoI have no idea why this is downvoted. For all the functor tutorials, it really is nothing more than the interface and rules that define map. And Monad is essentially flatmap. That's was so absurd about all of this.
- Dylan16807 5y agoIt's not "essentially flatmap" for more complicated data structures or data sources unless your definition of "flatten" is "do any arbitrary thing".
- bryan0 5y agoMy definition of flatten would be: m m a -> m a Definitely not arbitrary but maybe not useful for many monads (??)
- Dylan16807 5y agoThat's just a signature though, and the implementation could do so many things for complex and/or strange data structures. So in a sense yes it's "just" this function that could do anything following the signature. But now you're in the same place you already were, looking at a simple type signature with massive hidden implications, and if you treat it like a list you'll miss those implications. "flatmap generalized to a vast field of dissimilar types" is much more complicated than just "flatmap"
- wyager 5y ago
- avalys 5y agoI feel like these tutorials always fall apart when they start introducing Haskell syntax and say "That's Monad, simple as that!" class Functor f where flip fmap :: f a -> (a -> b) -> f b class Monad m where (>>=) :: m a -> (a -> m b) -> m b Sorry, that's not doing much for me! I am now going to look for a tutorial on Monads that's written in, say, Python, so at least I don't have to learn an abstract new language alongside the abstract new concept.
- staticassertion 5y agoI found this useful as a Rust dev: https://varkor.github.io/blog/2019/03/28/idiomatic-monads-in-rust.html https://varkor.github.io/blog/2019/03/28/idiomatic-monads-in... I agree with you entirely that the Haskell syntax is probably one of the biggest issues with monads being approachable.
- wyager 5y agoThis blog post is maybe 10% about monads and 90% dealing with why you can’t have them in rust (no HKTs). I hardly think that’s more approachable…
- staticassertion 5y agoIt was for me.
- aszen 5y agoIs that true though, you can't define a generic monadic interface yes but you can implement the same interface for your data types. That makes it less useful as the post says but still pretty powerful in some cases
- the_af 5y agoWhat's difficult about Haskell syntax? Or do you mean "it's an issue because it doesn't look like C, Java or Python?".
- juliangamble 5y agoThis post brings me back to this idea for industrial programmers: https://www.simplehaskell.org/ https://www.simplehaskell.org/
- Arnavion 5y ago>If They're So Wonderful Why Aren't They In My Favorite Language? While monads as a general interface might not be in your favorite language, individual implementations of monads might very well be. JS Promise, C# Task and Rust Future are all asynchrony monads - their bind is .then / .ContinueWith / .and_then respectively (with some caveats; the first two overload their bind to also work as fmap).
- Kwantuum 5y agoIt's not just "some" caveats: then auto-unwrapping means that you cannot use then as fmap reliably. In JS there is no such thing as Promise<Promise<Number>>. You cannot await/then the outer promise so that you can do something with the inner promise. In practice it's not too much of a problem, but it does mean that if you use some FP library, you cannot trivially wrap a promise to make it a monadic type that will work with the library's monad utilities.
- dasyatidprime 5y agoSo, just out of curiosity: I know in ECMAScript this is probably mainly for the purpose of giving you a convenient way to lift either (α → β) or (α → Promise β) into (Promise α → Promise β) without static typing or having two "then" functions, by forcing β and Promise β to be disjoint. It also allows looser composition underneath, since you can choose which kind of return to provide at runtime without having to re-wrap the bare case manually. But are there any other reasons for Promise of Promise to be squashed away like that?
- Arnavion 5y agoApart from simplicity, it was designed to be used with "thenables". If the value that a Promise resolved with had a `then()` member, the value would be treated like a first-party Promise. So even if there was a separate fmap-then and bind-then, the fmap-then would have to deal with thenables and act like bind-then anyway. There's a bunch of history in [1], particularly [2] and [3], if you care to read them. [1]: https://github.com/promises-aplus/promises-spec/issues https://github.com/promises-aplus/promises-spec/issues [2]: https://github.com/promises-aplus/promises-spec/issues/75 https://github.com/promises-aplus/promises-spec/issues/75 [3]: https://github.com/promises-aplus/promises-spec/issues/94 https://github.com/promises-aplus/promises-spec/issues/94
- skybrian 5y agoThis part could be more simply explained: > The problem is that a "do" block in the list monad chains together several of these flatMap calls. It is very easy for the transformation function to result in the computation growing exponentially as each one expands one item from before into one or more items. You have to have something else in the problem that prevents that from happening. You may see a lot of cute little algorithms like solving the n-queens problem, but in practice, this is not something you see used a lot in Haskell. The mathematical transform is cute, but hard to control because it's a bit too simple. In particular, this has no ability to "notice" that it is revisiting possibilities it visited before. That would take more machinery. I think he's saying that it's doing a depth-first search without any caching. I'm wondering if Haskell has some other nice way of specifying depth-first searches that performs better?
- themk 5y agoQuite often what you want is LogicT which gives you more control and can significantly improve performance over List.
- mLuby 5y ago> For their own good reasons, mathematicians use "functor" as a concrete noun. Haskell copied this, hence how often you hear about "a functor". > However, interfaces are adjectives that describe a data structure, which is why in many languages they are often named with -able suffixes (Iterable, Serializable, etc.) Programmers are better off thinking of it as Functable or Fmapable or something like that. Fmapable makes much more sense to me. Naming is hard.
- revskill 5y agoActually, just learn the construct `Promise.then().catch()` and u already know how to use Monad in your code. The `then` is the `bind` of Monad. What's complicated here ? Want `do` syntax ? a <- method b c <- method a is equivalent to a = await method(b) c = await method(a) Again. Use javascript to learn Monad in real world is a better way to learn.
- Dylan16807 5y agoThat doesn't tell me a single thing about what 'monad' means. It's like having someone run hello world in java and declaring they know what 'static' means.
- wyager 5y agoThat’s an example of a specific monad, not the definition of the general concept of a monad. Also, JS promises are not really monads. They lack denotational equality, so they can’t be said to obey the monad laws.
- zzo38computer 5y agoI don't really like the description of the IO type so well. Rather, I can say that the type "IO x" is the type of I/O operations that produce a result of type x. The functor/monad functions that can be used include: - "return x" produces a I/O operation that does nothing and produces the result x. - "x <$> y" or "fmap x y" produces a I/O operation that does the same thing as y, but the result value is the function x applied to the result of y. - "x >>= y" produce a I/O operation that when performed, performs x, and then passes the result to the function y, which returns the I/O operation to be performed next. - "join x" takes the I/O operation x which has the result which is itself a I/O operation; the new one first does x and then the I/O operation which is the result of x. A I/O operation might have different results when performed multiple times, even if it is the same I/O operation. Also, I can say that generators in JavaScript seem to form two different monads actually (as far as I can tell), as described by the following table: yield x yield*x return x return yield*x Also, functors and monads are more than the interface, but also the mathematical properties they have (such as "fmap id = id" etc).
- thewakalix 5y agoA response to the common proposal of “we should call functors Mappables instead”: Names Do Not Transmit Meaning, https://www.parsonsmatt.org/2019/08/30/why_functor_doesnt_matter.html https://www.parsonsmatt.org/2019/08/30/why_functor_doesnt_ma...
- epicureanideal 5y agoThis is a similar argument to not naming code directories based on anything reasonable, and naming them after types of fruit instead, because only by reading the code in each directory can you really understand it, and it always changes anyway. I think it’s better to at least try to make a name that transmits partial meaning or a general gist of the idea.
- wyager 5y ago> I think it’s better to at least try to make a name that transmits partial meaning or a general gist of the idea. If we didn’t use formal terms for math concepts, mathematicians would waste all their time arguing about the colloquial definitions of the colloquial words they tried to map onto abstract concepts. It’s better to have a clean break and force people to learn the specific formal meaning rather than try to limp along on a busted half-intuition.
- acomar 5y agothe terms actually do come from an intuitive place if you look at the underlying math. it's called a functor because it does something function-like - mapping arrows in the domain category to arrows in the codomain. in programming, we think of this as a higher-order function that lifts a function on a category (of types) to a function on a different category (of types). it's only when you try to elide that explanation that the name appears to come from nowhere. similarly, monads bear a strong resemblance to monoids - in fact, it's the same relationship as functions and functors and it's quite precise. these intuitions only become available to you with the math, though, and programmers have a strange aversion to mathematics for people who do it day in and day out.
- malwarebytess 5y agoThis is precisely how my Programming Languages CS Professor explained it. Probably contributed to Haskell being my Go To language. This dialectic style adopted by the article for explanation is effective in exploring confused understandings. (Bit socratic) Not all writing needs to be technical writing like in a manual -- narrative explanations can be useful too.
- ggm 5y agoThanks. Well written. Readable. This sentence from it sums up many of my conversations about FP It probably also doesn't help that we have a function literally called >>=, which is hard to pronounce. It's pronounced "bind", which is only slightly more helpful in that that still fails to evoke any useful imagery
- siraben 5y agoAs someone who studies both CS and pure math, it's quite interesting when I see posts that take a very operational reading of functors and monads, instead of its shorter and more precise categorical definition (as a mapping from one category to another that acts homomorphically over morphisms). That's not necessarily wrong, but it's an important thing to keep in mind when learning category theory through a programming context (e.g. Category Theory for Programmers by Milewski). When I tried to learn category theory in this way, past a certain point the abstractions felt too unnatural and arbitrary (e.g. lack of examples for adjunctions and free objects), whereas a good mathematical treatment of category theory would provide plenty of examples across different areas of mathematics such as algebra, topology and logic. I strongly agree with valenterry's comment[1] here, that post gives is an approximate definition of functors, even omitting the functor law. In particular, it is explaining what an endofunctor is (in the context of FP, you mostly deal with one category anyway). Furthermore, even in Haskell, there is no such thing as the category of Haskell types[0] (because of things like divergence and partiality). For the curious, I've recommended Program Design By Calculation[2] to a number of people to show how category theory can be used as a powerful tool to reason about programs in an FP context while not compromising on the mathematics (the target audience is undergrad CS majors). EDIT: In particular, I have a bit of issue with this sentence after stating the signature of fmap, since it does not reference lawfulness. > This is the full definition. There is no additional hidden machinery that Haskell is somehow automatically invoking when you "use a Functor". [0] http://math.andrej.com/2016/08/06/hask-is-not-a-category/ http://math.andrej.com/2016/08/06/hask-is-not-a-category/ [1] https://news.ycombinator.com/item?id=27638565 https://news.ycombinator.com/item?id=27638565 [2] https://www4.di.uminho.pt/~jno/ps/pdbc.pdf https://www4.di.uminho.pt/~jno/ps/pdbc.pdf
- drdeca 5y agoDo you happen to know of any programming language which has explicit support for multiple categories?
- hiker 5y agoLean[0]'s mathlib has quite nice formalisation of category theory[1] with plenty examples of concrete categories even sheaves and toposes. [0] https://leanprover.github.io/programming_in_lean/#01_Introduction.html https://leanprover.github.io/programming_in_lean/#01_Introdu... [1] https://github.com/leanprover-community/mathlib/blob/master/src/category_theory/category/default.lean#L92 https://github.com/leanprover-community/mathlib/blob/master/...
- bane 5y agoHaha, I read this originally as "Functors are Monads from the class of People who Have Read Too Many 'Tutorials'"
- AnimalMuppet 5y agoThanks, jerf! This is really good stuff. One question on functor: Does the thing returned have to be "the same shape" as the original? That is, if I have a list, and I fmap on it, do I always get a list back? Or am I only guaranteed to get some kind of functor back? If I am guaranteed to get a list back, am I guaranteed that it has the same number of elements? To use your initial language, is it fair to say that a functor takes a source of a, and a function that converts a to b, and returns a source of b of the same "shape" as the original source of a? If it's true, that makes the function example clearer to me. Of course it has to return a function, because the shape of the input was "function". (And, did the article answer this and I just missed it?)
- yakshaving_jgt 5y agoThe answer to your first question is yes. You can see that in the types. fmap :: (a -> b) -> f a -> f b The `a` becomes a `b` (which could be a different type but not necessarily), but the `f` is the same `f`.
- chobytes 5y agoTo be honest I feel like the functor concept is too abstract and subtle to the point where it's probably not very useful in the context of programming. Worse... their usage in programming languages is very unlike how homomorphisms are typically used in their usual context, so appealing to the math seems like its just confusing learners without payoff. To give an analogy... it feels like trying to explain the chain rule by explaining pullbacks between tangent spaces. Not technically wrong, but now they're confused and asking questions whose answers wont help them.
- siraben 5y agoOf course, the chain rule explained via differential topology is simply functoriality! IME, category theory was much more valuable in differential topology in helping solidify why the structures and their compositional properties make sense. You get to work with several, honest-to-god categories and functors between them, especially when tangent bundles come into the mix. Now go to programming, there's really only one category at play, and I'm not sure how worthwhile it is to apply a (more contrived) version of CT that only has endofunctors and being cartesian closed.
- chobytes 5y agoHaha, I feel like thats how I started to get CT concepts too. Topology and geometry really feel like the "natural" context for those ideas. For programming... I feel like they're basically trying to do logic in a roundabout way. I suspect that (finite) model theory might be more useful for such applications if they really want theory.
- the_af 5y ago> it's probably not very useful in the context of programming This puzzles me. It turns out it's very useful in the context of programming in practice (i.e. Haskell programmers use it and find it useful), so what do you mean? I don't know CT so I wouldn't know how well it maps (pun intended) with the Math concept, but regardless, whatever abstraction is there in Haskell that programmers call "functors" is tremendously useful.
- DanAtC 5y agoArticles like this and the subsequent comments are just a long-running, inside joke of who can out-pretentious one another, right?
- loopz 5y agoWether you know functor laws, or you misapply functors, you have to talk about it to be properly applicative ;) The community is very much focused on learning, by teaching the little one knows.
- jtwaleson 5y agoI strongly dislike Haskell's focus on theory. Just show me examples on how to get things done and then the concepts will become clear. The Haskell and mathematics communities too often act like ivory towers that humans have to become enlightened to understand, and that simply is not how my brain is wired. I'm not sure of there have been any studies on the usability score of languages, but I think that Haskell would come out very, very poorly. There's a reason why python is so popular.
- sfvisser 5y agoPython is probably easier to learn for most beginners, but that doesn’t mean it’s easier to write programs in. Context matters when talking about usability. I’ve used functors and monads to solve tons of issues and have literally no clue about the theory behind them.
- jtwaleson 5y agoAgreed. Haskell solves real problems and it's quite a good language once you learn it, it's the cultural focus on theory rather than humans that I don't like.
- mmis1000 5y agoBut the tutorials simply ignores the real world use case. Lots of concepts are actually useful for solve problems (even in other language). You need to give the reader some real world example. A concept dangles in brain without connects to other things won't live too long. You brain is likely to `optimize it out` because it is unused.
- lallysingh 5y agoI thought "Real World Haskell"did just that very well.
- mchaver 5y ago> it's the cultural focus on theory rather than humans that I don't like. That's completely fine. Haskell is not a single community. There are two big camps that often interact with each other, the academic and the industrial. Keep in mind the origin of Haskell is academic and it's original purpose is to test and implement ideas from Programming Language Theory. That is still there and will continue. GHC optimizes for letting people experiment with language extensions. Industrial interest didn't start to grow until somewhere around 2008-2012. It is a small community and will likely remain so. I am a big Haskell user myself. There are many theoretical things I don't understand or need to touch, but I appreciate their contributions to the language and the community. I do agree that Haskell has lots of room to improve on tutorials for non-academics. Maybe I will get inspired to write somethings. It's completely fine if you don't want to use it. However, you may find Haskell users' enthusiasm for the language insufferable, hehe. I do hope anyone who chooses to interact with the Haskell community finds us welcoming.
- proc0 5y agoI suspect this is still not satisfying for people wanting to really know what a Monad is. Perhaps these simplified explanations can get away with explaining Functor (more so the "mechanics" of it) but it starts breaking down for Monad. I don't think there is a shortcut... these are highly abstract concepts. Abstraction is about exposing the most essential parts of something... Category Theory is perhaps the mathematics of abstract composition, exposing the most essential parts of how to combine (in the abstract). Monad and Functor arise from CT, and thus, are very abstract concepts, built from the axioms of an already abstract subject matter. This is why it's hard to "simplify" them any further. The formal definition of a Monad is itself the simplest way to expose it. The fastest way to grapple with it is through the axioms of CT, precisely because CT is arriving at this concept by abstraction; by only thinking about the most essential parts of it. That is probably not a satisfying explanation (to tell someone you need to learn axioms and laws before understanding this concept), but the alternative is to simplify and/or abstract something that is already as simple and as abstract as can be, albeit with esoteric names, and thus inevitably complicating it and shrouding the concept instead. i think the reason so many Monad tutorials aren't as satisfying is because they attempt to explain both a mathematical, abstract concept and also how it applies to programming, at the same time (without explicit separation). The latter is practical, the former is very abstract, and the bridge between them is very complex and usually left out, leaving the reader scratching their heads how and why you go from one to the other. It's worth mentioning Bartosz Milewski here (links below), as he has bridged this gap for many, but as mentioned, there is no shortcut. https://bartoszmilewski.com/ https://bartoszmilewski.com/ https://www.youtube.com/channel/UC8BtBl8PNgd3vWKtm2yJ7aA https://www.youtube.com/channel/UC8BtBl8PNgd3vWKtm2yJ7aA
- siraben 5y agoThere was a particular moment I remember from Milewski's monad video, where likened monad tutorials to a situation where you arrive on a remote island and the islanders ask you to explain what a function is. One's first instinct might be to give a bunch of examples, for instance, you can have a function that takes a list of fishermen and order them by the number of fishes they caught, or a function to turn barley into beer, etc. Upon hearing this, the islanders think that functions must be some magical panacea, and so you backtrack on that and try to talk about functions as being like animals, they take input and produce output (good luck with function composition). The situation is like that with monads. They are far removed from the world of programming (the fact that there were connections at all to effects was remarkable!), but as such, you have to learn the ambient theory in which monads reside to really grok it.
- smitty1e 5y agoBrevity: class Monad m where (>>=) :: m a -> (a -> m b) -> m b Now, I am comfortable with upper case for type and lower case for instance. But m is an instance of a function, not a variable. But m, a, and b all look like variables. You are already hammering me with the brevity here. Maybe put the functions in a serif font, and variables in a sans-serif? Or use different colors? Other languages seem to be more helpful with the visual cues. Solving problems in SQL is a blast, so it seems like Haskell should be an easy lift, but the brevity of the syntax has sent me packing for years.
- dragonwriter 5y ago> Now, I am comfortable with upper case for type and lower case for instance. Monad is a typeclass, not a type. m is a variable, whose value is a type constructor. > But m is an instance of a function, not a variable. On the type level, m is a variable representing a type constructor; a concrete value it might have is List. It is an instance of a typeclass, not a function. > But m, a, and b all look like variables. m, a, and b are all type-level variables; the value of m is a type constructor (which is essentially a type-level function), while the values of a and b are types.
- smitty1e 5y agoMy point is that keeping all that straight at the single letter level is brutal. If it were a physical building, Haskell would be brutalist.
- bmn__ 5y ago> m, a, and b are all type-level variables; the value of m is a type constructor (which is essentially a type-level function), while the values of a and b are types. Let's try to apply that knowledge, and use my tribe's creed that "similar things should look similar, and different things should look different": -- α, β types -- m type constructor (>>=) :: m α -> (α -> m β) -> m β -- a, b types -- 𝘮 type constructor (>>=) :: 𝘮 a -> (a -> 𝘮 b) -> 𝘮 b I think that's much better. GP's complaint was correct, writing different concepts with the same notation is confusing.
- baby 5y agoWhat I find annoying in OCaml and functors is that it makes code really hard to follow. You’re wondering how that function is defined or what exactly is that type? Now you have to try to jump through many functors, their implementation and their instantiations. If IDEs could facilitate that process it’d be fine, but they can’t atm.
- TheFreim 5y ago> Title is literally true. This may not be the best place to learn about these concepts for the first time, because I'm going to focus on knocking down the misconceptions about them. I've recently been interested in learning more math to apply to programming. Does anyone here on HN know where I can find quality education online (free) I can use through the rest of the summer? I only got to geometry as my highest maths in high school.
- WastingMyTime89 5y agoI think the reason so many people have trouble understanding functors and monads is because they are not complex constructions but look useless when you see them. There is nothing inherently interesting about functors and monads. Haskell, as a research project, discovered that you could isolate IO in a lazy language inside a type using a monadic construction to chain them which allows you to track side effects. That was interesting, the rest less so. Now the Haskell community somewhat fetishized these abstractions for reasons which are their own. They must be apparent to the community at large. They are not to me.
- hu3 5y ago> Haskell, as a research project, discovered that you could isolate IO in a lazy language inside a type using a monadic construction to chain them which allows you to track side effects. Sounds like LINQ in C#. I wonder if there's any correlation.
- loopz 5y agoLINQ is being worked on by the same people developing/designing Haskell. These methods are slowly being injected. LINQ is monadic and has a bind function that generalize beyond IEnumerable.
- zem 5y agothe reason for "fetishizing abstractions" is that you can write libraries that work with the generic structure of those abstractions, and are therefore automatically useful for all the specific cases. the design of ruby's "Enumerable" library in the standard lib is a good example, it's a huge collection of methods that works for anything that supplies an 'each' iterator.
- WastingMyTime89 5y agoI was specifically talking about these abstractions as monads and functors not abstraction in general. Enumerable is a lot less abstract than both monads and functors. I personally don't think Haskell stand at the right level of abstraction but that's a personal opinion.
- k__ 5y agoThe monads video by "Fun Fun Function" finally explained functors and monads for me in an understandable way.
- rocqua 5y agoI loved this. It helped me get monad better, and improved my understanding of functor. What I am still unclear on is the difference between applicative and monad. Both, to my mind, have this concept of combining "wrapped" values. Currently my intuition for the two is exactly the same. So my intuition is probably wrong. I would love a similar explanation to this post on applicative vs monad.
- Twisol 5y agoA "functor" lets you take a unary operation on simple values, `a -> b`, and apply it to a value which structures some `a`s within a larger context (`f a -> f b`). A classic example is (to use some Java syntax) `List<A>`; it is not difficult to write a method with Java signature `<A, B> List<B> map(Function<A, B> f, List<A> list)`. Functors let you chain together unary (non-branching, straight line) processing pipelines. If your processing pipeline works over any functor, then it can be applied to any structure that acts as a source of values, and it will leave that structure unchanged around the processed data. An "applicative" is a functor that also lets you take two values, one structuring some `a`s and one structuring some `b`s, and cross them together to produce a result that structures some tuples `(a, b)`. If I have two lists, I can cross them together to get a list of all pairs of elements between the two lists (like a CROSS JOIN in SQL). Applicatives let you merge two separate processing pipelines together, combining data from both sides together into a single stream. If you can combine two pipelines into one, you can combine any number of them; this is what makes them strictly more powerful than general functors, which cannot support multiple parallel data sources. A "monad" is a functor that also lets you take a value with two layers of nested structure, `f (f a)`, and meld/flatten/unify those two layers into a single layer of the same kind (`f a`). If I have a list of lists, I can unwrap the inner list structures, lifting all of their elements to the top level. Or, more viscerally (for me), if I have a binary tree whose leaves contain binary trees, I can graft the inner tree at each leaf onto the outer tree, producing a deeper single tree. Monads let you accumulate some inner structure produced by a stage of your pipeline into the outer structure your values came from. In turn, this reveals the values under the inner structure to downstream pipeline elements.
- the__alchemist 5y agoAbstractions (Functors, Monads, and otherwise) beg the question of "What value is this providing", in context of a specific problem. If the cognitive complexity added by an abstraction is worth the value it provides, I'll use it. If not, I won't. Example: I do mostly embedded prgramming, in Rust. My focus is on building working devices. I use abstractions and build APIs that are useful for solving specific problems I have now, or anticipate in the future. Eg an init function for an ADC that does the register writes described in the reference manual, so I can write `let adc = Adc::new()` instead of a series of reg writes. It's common in embedded rust programming to make heavy use of generics, traits etc, and other things that leverage the type system to make it so that as many mistakes are caught at compile time as possible. This sounds great in theory, but if a given abstraction makes type signatures a mess, doesn't allow me to configure a pin correctly due to an edge case, and provides no advantage if I QC I'm using the right pin, I won't use it. I see these Haskell examples as using abstractions by default, without questioning whether it's appropriate for a given use.
- datatrashfire 5y agoLook another nebulous post on functional programming.
- vanusa 5y agoIf it can't be explained in a tutorial or two - is it a good idea to use in (most) production environments? (I know, I know - your environment is a special case).