5 ms·
A Haskell Journey
- plinkplonk 17y agoI don't think this is HN material (I wrote the blog post,I didn't submit it). This post is just me "thinking by writing", trying to clear my head. I thought it was a largely content free post, just some rambling, and yet here it is on HN. Slow news day I imagine! :-P. Here is a summary, to save you time - Haskell needs a book (to complement RWH) focusing on the the Category Theory aspects.
- jrockway 17y agoThe advantage of HN is that you get better comments than you would directly on your blog. Feedback++.
- plinkplonk 17y ago"The advantage of HN is that you get better comments than you would directly on your blog. Feedback++." Oh I agree. HN has the quality of having awesome comments and discussion on very bland subject material. I am just faintly embarassed that something that was just me rambling in prose landed up here.
- jrockway 17y agoWhat we really need is an "Advanced Haskell" book which assumes a knowledge of Level 1 Haskell and then lays out the CT bits in an orderly fashion. Monads for example, are best understood from a Category Theory perspective than through some tortured analogy to Space Stations or Elephants or whatever. I think people are looking for something profound where there really isn't anything as profound as they are imagining. Monads are applicative functors with "join". Applicative functors are functors with "ap". Functors are containers that can apply a function ("fmap") to their contents. Add in some laws, and that's what you have; a few very generic concepts that let similar things have the same API. There is nothing more profound here than any other generic API, except that the "genericness" goes beyond what most programmers are used to. (Most programmers seem to be used to abstractions that take some "noun" and make variants -- map => hashmap, alist, plist, binary tree; list => linked list, vector. Haskell's abstraction is more like an adjective -- a functor isn't really a thing that you can hold in your hand, it's just a property of something. "Monad" is a property of "list" like "red" is a property of "apple".) (Monoids are even more generic -- they are something that has an "identity" and an "append" operation. Lists are monoids, but so are booleans like "Any" and "All", and integers form monoids over addition and multiplication. Monoid is just a word that relates the common properties. The advantage of this abstraction in Haskell is that you can "mconcat" a list of sentences into a paragraph just like you can "mconcat" a list of numbers into their sum.) Monads get a lot of mindshare in Haskell because they are a good way to sequence computations -- you write a single sequencing combinator, and you get convenient syntax sugar and a big collection of utility functions ("liftM", etc.). Write another function, and you can create monads from other monads, which happens to be convenient for writing applications. (Reading the definition of the type "X" in xmonad is enlightening, if only because you realize that the entire program is a bunch of functions that return "X", and that the mechanics of X's "monad" instance make the program work the way it does. xmonad is really the X monad!) Arrows are like monads, except instead of "bind" you have a variety of other combinators; "fanin", "fanout", etc. If your data flow looks like that, Arrows make your code look like your dataflow. (And from a category theoretic perspective, every monad is an arrow. Kind of how every "light red" is "red".) I think RWH provides good coverage of monads, although reading RWH once you "get" monads can be tedious. The good news is, by the time you learn what the word "monad" means, you already know what it does for you. So anyway, just start programming, and you'll see why these abstractions are useful. You'll also see why "easy to understand" abstractions like "foldable" and "traversable" are useful too, even though there is not much category theory there. I guess to summarize: don't let unfamiliar words scare you away from simple concepts! You already know how to program, and programming Haskell is just programming!
- tel 17y agoReading the Typeclassopedia is a great way to hear this all over again slightly different and is therefore highly recommended. Really the biggest step is, as jrockway says, to realize that Monad is (kind of) a verb. "Here's a list and here's how you can monad it."
- plinkplonk 17y ago" think people are looking for something profound where there really isn't anything as profound as they are imagining. " I largely agree with what you (jrockway) say, but differ in some nuances. These "concepts + laws" seem to be best understood as mathematical constructs (like say functions or sets) than analogies. What I think is happening now is people are trying to understand Sets by studying say java.util.HashSet, or even worse, analogies to java.util.HashSet. "Functors are containers that can apply a function ("fmap") to their contents." This is true enough, but it is hard to understand the ((->) r) functor (for example) as a container. You can do it but you have to stretch a bit. I have seen people struggle with understanding IO as a container. Wheras if you know that a functor maps (or transforms) objects (in the cCategory Theory sense, not in the OO sense) to objects ,and morphisms to morphisms, subject to 3 laws, then it makes perfect sense that ((->) r) is a functor. Likewise with IO. The key this is to understand the Hask category where types are objects and functions are morphisms. For a Haskell functor, the type constructor takes care of the "objects to objects" (or type to type) mapping and the first order function (map for lists for example, or compose for the ((-> r) and so on) take care of the morphism to morphism (here, functions to functions) mapping. And it is not even very hairy math. The definition of a category is hardly half a page of prose, much easier to understand than some specs we programmers work off of. @jrockway, do you want to write that book? :-)
- jerf 17y agoI don't know about that. When you get to the "second-level" stuff, it can be weird. Lately, partially as an exercise and partially to get a project of mine going the way I'd like to see it going, I've been working with the State monad directly. Using the state monad is trivial. Understanding how the state monad does its thing is slightly less trivial, but just a matter of following along the definitions and doing the appropriate substitutions. Understanding where the state monad came from and how to modify it to do what I want... now that's starting to get tricky. I blundered around for a bit trying to reconstruct the state monad without actually looking at it and came to the conclusion it wasn't possible. Except that it clearly is possible because it's part of the standard library. The problem was that it had never really occurred to me to put a function in the type signature itself, the way the state monad does, and despite how simple it looks it has really hit home that I don't really grok the thing yet. I do not mean to overmystify; like I said I follow the thing perfectly well, a minimal and perfectly functional definition of the thing is like 10 rather simple lines and there's an upper limit to how confusing 10 lines can get, but... if you can't modify something you certainly don't grok it. Yesterday night I realized I have felt this way before, but it had been such a long time that I hadn't instantly recalled it. I felt this way when I learned about pointers. My first language was Commodore 64 BASIC and I suffered the Dijkstra brain damage, but I got better. I never had the freakout that some people have with pointers, I got the base mechanics right away, no problem. But it took me a while to grok them, and in particular what they are good for. To come to the level of understanding that I might plausibly convince myself that I could come up with the Tortoise and the Hare linked list cycle detection algorithm on my own, or to come to the point where I could comfortably deal with pointers to pointers to functions that take pointers to functions that take an int and return another int without flipping out. Or another example, I fell right into this: http://lukepalmer.wordpress.com/2010/01/24/haskell-antipattern-existential-typeclass/ http://lukepalmer.wordpress.com/2010/01/24/haskell-antipatte... . Same reason, BTW; I wanted to write a "concrete datatype" when what I really needed was functions right there in my datatype. It really strikes me as the same sort of problem you can run into in C or C++ if you try too hard to avoid pointers; you end up with crazy ass designs that end up copying too much stuff around, and are ever stuck with being able to not really quite use the language to its fullest. I'm feeling the way I did when I learned about pointers, and I recognize that I've got a ways to go before I'm comfortable with this sort of thing. (And I get the same results now when I diddle around with the state monad that I remember getting when I tried to modify pointer code before I really understood what I was doing, except the "segfaults" occur at compile time instead of runtime. Progress!) (Perhaps ironically, I'm finding it's making me better with OO, too. Expressing responsibilities in terms of methods that take closures, rather than simply "concrete methods" that take parameters that aren't closures, can be a remarkably powerful way to approach OO interfaces.) I hope this doesn't sound mystical, because I don't really mean it to be. I suspect (and my gut says) that in the end this will not be particularly different than learning about pointers, and few people (though not zero!) consider learning about pointers a mystical experience today. But that learning takes time, and I've only begun. At something like ~12 years of serious experience, finding a paradigm like this that has left me feeling like a total newb again has been a bit of a shocker. I find it invigorating and interesting, but I have slightly greater understanding for those who feel like after that long they should be allowed to avoid stepping into the areas that make them feel like newbs... (And also, for those who may read this who are considering Haskell, let me again emphasize that you can do a lot of work in Haskell without giving thought one to the issues I raise here. Just as you can be a pretty decent programmer without really getting into assembler, and pulling the state monad out of the library and using it is trivial. It's just that if you want to go in really deep and really understand what's going on, there's definitely different stuff going on. It's not just C, C++, or Perl writ different like most languages today.)
- jriddycuz 17y agoI think what we need is "Real World Category Theory" to go along with "Real World Haskell." It's not surprising that it's difficult to get your head around concepts lovingly referred to as "abstract nonsense." The reality is, of course, that CT is really useful because it is so general, but that same generality is what makes it really hard to grok. More experienced Haskell-ers are often very quick to offer encouragement and to tell you that Monads and Monoids and Functors and Arrows/Morphisms aren't that tough, and they're right, but in some ways they are missing the point. The level of abstraction that those concepts exist is so foreign to most people that they have trouble conceiving of it in any meaningful way until they actually go through and learn enough to kinda get it. At least Monads and Monoids have kinda scary names. Basic category theory concepts like arrows and objects have familiar, inviting name, but they refer to something totally different. Worse than being scared by words, these words leave us thinking we know things they don't.