12 ms·
Why learning Haskell makes you a better programmer
- charlieflowers 14y agoThis is a great article. To me, the most powerful example is this: "It forced me to think of every such problem as a chain of the primitive list operations, maps, folds, filters and scans. Now I always think in these terms. I "see" the transformation of a container as a simple sequence of these operations. " That's a higher level of thinking that is faster and yet at the same time more precise than thinking in terms of imperative loops. Haskell has taught him to think more powerfully.
- minikomi 14y agoInteresting. Going through the 4clojure problems gave me a taste of this.. And the ->/->> operators make it even simpler.
- beagle3 14y agoI recommend people study APL / J / K for the same reason: It has the right primitives that make everything expressible as maps, folds, filters, scans, ranges, and stuff like that. Even more so than Haskell. It doesn't have monads and the kind of abstractions that lets you modify control flow semantics. It doesn't even have the facilities to build abstract data types - which makes you work with less-abstract data, and realize that although some abstraction is useful, most of what is practiced today is useless. APL / J / K promote, at the same time, container abstraction, and concrete down-to-the-metal real work.
- tel 14y agoI've always wanted to dive into these guys. Is there a good introduction for fluent Haskell programmers somewhere?
- Someone 14y agoI haven't done anything in Haskell, but I doubt that makes much of a difference. Maybe more inspiration than introduction, but the "Game of Life in APL" video is a must see (http://uk.youtube.com/watch?v=a9xAKttWgP4&fmt=18 http://uk.youtube.com/watch?v=a9xAKttWgP4&fmt=18) The description on YouTube points to a "Game of Life" tutorial at http://tryapl.org http://tryapl.org. I haven't tried it, but it looks nice. For a more academic (as in "read about, don't play with") introduction, I found Iverson's "Notation as a Tool of Thought" (http://awards.acm.org/images/awards/140/articles/9147499.pdf http://awards.acm.org/images/awards/140/articles/9147499.pdf) a good introduction.
- gtani 14y agoI don't know how i feel about APL now, but I have some funny APL stories. One of my friends was the Jimi Hendrix of IBM APL2, he was amazing, unfortunately i can't tell the stories but when he learns haskell he'll be top notch. Another was the Dweezil Zappa of APL, he learned the language as a high school sophomore or junior from Iverson himself in Yorktown Heights.
- pekk 14y agoLearning other languages can broaden your repertoire in a similar way. Why is it more important to decompose problems into map/fold/filter/scan than other sets of operations?
- charlieflowers 14y agoWell, when you really boil it down, these are the fundamental operations. At least, I believe that is true. Someone stronger in CS theory could come along and verify/correct that statement. This paper [1], for example, appears to argue that FOLD (foldr in particular) is universal, meaning that there is only one way to move through a list and resolve it to a single value. In other words, you may have some other algorithm that you think is not a FOLD, but if it moves through a list and resolves it to one value, then your algorithm is FOLD. (I say the paper "appears" to claim that, because in all honesty there's about 10-15% of the paper that I didn't fully grok). I suspect that applies to map/filter/etc. After all, if you look at their definitions, they contain nothing but the essence of the operation (map/filter/fold/etc) itself. So, map/filter/fold/etc are THE operations on lists. [1] http://www.cs.nott.ac.uk/~gmh/fold.pdf http://www.cs.nott.ac.uk/~gmh/fold.pdf
- shaydoc 14y agoI really like your executive summary there, these functional concepts really help with solution composition and abstraction. I really am pleased the penny has dropped with myself too. I am trying daily to encourage my colleagues to spend some time learning these functional fundamentals!
- dschiptsov 14y ago..to think of every such problem as a chain of the primitive list operations, maps, folds, filters and scans. Now I always think in these terms. I "see" the transformation... You mean these functions - http://karma-engineering.com/lab/wiki/Tutorial5 http://karma-engineering.com/lab/wiki/Tutorial5 Why Haskell, then?)
- sixbrx 14y agoType safe pattern matching would be my reason, followed by separation of effects from pure functions, and allowance for creating real data structure types so we don't have to do things like "(define make-tree cons)" as in the last example of your page. We can instead give tree a proper type, give operations over the type proper signatures which help to explain what the operations do, and get verification from the compiler at the same time that we aren't mixing things up. It's hard to beat data Tree a = Node a [Tree a]
- dschiptsov 14y ago(define make-tree cons) - is very natural thing - it is called synonym - just another word for the same thing in a new context. Moreover, this is a better candidate for a proper way.) separation of effects from pure functions - this I cannot understand. Aren't these function pure? And another part of "properness" - each node of a tree is a tree itself. Sometimes less types is more.) Well, I'm old-fashioned, but I think that being able to put any kind of data into a list is the strength, not weakness of a language, and that user-defined ADTs and compound data-structures needs no explicit typing, they are nothing but conventions.
- adimitrov 14y agoThink again. Types provide one important thing: static guarantees. Compile-time type checking can only work with strong, expressive data types, and ADTs provide a very good way of enforcing that. (Together with Haskell's `newtype` keyword, which really is nothing more than a convention, if you will, a different way of expressing a type synonym, but with the static guarantee that when you make up a `newtype Name = Name Text`, you cannot use `Name` wherever you can use `Text`, but you can access the `Text` `Name` is a wrapper for easily. This makes it much easier for your intent to be expressed directly in the code, namely in the data types. Just compare: makePerson :: Name -> Age -> Gender -> Address -> Person makePerson' :: Text -> Int -> Text -> Text -> Person
- dchichkov 14y agoAs long as people don't abuse that power and don't write C++ code, full of statefull functors (intellegent fools they are, the worst kind), I'm happy.
- beagle3 14y agoThis. A lot of Haskell's power comes from it being able to enforce the validity of this style of programming. C++ does not - and when working with C++, you have to reason at both the C++ level like you always do, and at the Haskell level (but without the compiler verifying your work). That's extra burden without extra benefits.
- dchichkov 14y agoIt is just that author in the "Container Operations" says that using functors in C++ is such a good practice. No. While concept is nice, in practice, with C++ it can result in time waste and unreadable/unmodifiable code with no clear benefits. Cleaning up some statefull functors overdesigned by some intellegent fool can be a gruesome job.
- laureny 14y ago> Cleaning up some statefull functors This makes no sense at all. A functor is a type class, it doesn't contain any state.
- wonderzombie 14y agoIn Haskell, it makes no sense, agreed. In C++, it makes sense: http://www.sgi.com/tech/stl/functors.html http://www.sgi.com/tech/stl/functors.html. Functors are function objects, which is to say they're a class/struct with one method, operator(). If this makes you sigh, know that you're not alone.
- laureny 14y ago> full of statefull functors (intellegent fools they are, the worst kind), I'm happy. If you are going to be judgmental and calling people fools, I suggest improving your spelling first, or you'll just appear to be one yourself.
- Posibyte 14y agoI've felt the power of Haskell before, through tidbits I've learned and books like Learn You A Haskell. However, Haskell still presents itself as a giant rock in my mind, somewhat impenetrable and obtuse. It's so different from my normal thinking processes that my brain has an incredibly difficult time adjusting. It's like my thinking process turns to goo after the first few layout steps. Basically, I wish I could understand Haskell so I could harness its power.
- meric 14y agoDon't give up. I kept myself at it for many days, thinking while walking/eating and even before I slept. Eventually it clicks. Probably the most memorable experience I have had as a programmer, so far (still young).
- tikhonj 14y agoThe best way to do that would be to sit down and just write something fun in Haskell. I think the Write Yourself a Scheme in 48 Hours tutorial[1] is a good place to start. While I'm not sure it has the best code style, it's probably the easiest way to get yourself into the Haskell mindset. [1]: http://en.wikibooks.org/wiki/Write_Yourself_a_Scheme_in_48_Hours http://en.wikibooks.org/wiki/Write_Yourself_a_Scheme_in_48_H...
- gtani 14y agoGo on stackoverflow or or haskell beginner list and ask questions! The crew on SO won't instantly close your question as "duplicate" or "shows no effort" theyre really understanding. i had a list of lesser known haskell tutorials/cheats/ search engines here https://news.ycombinator.com/item?id=4927532 https://news.ycombinator.com/item?id=4927532 but i forgot the Stanford course notes http://www.scs.stanford.edu/11au-cs240h/notes/basics-slides.html http://www.scs.stanford.edu/11au-cs240h/notes/basics-slides.... ____________ for all those squiggles like (>>=) use hoogle, index of RWH hardcopy or: http://rigaux.org/language-study/syntax-across-languages-per-language/Haskell.html http://rigaux.org/language-study/syntax-across-languages-per... http://symbolhound.com/?q=haskell+%3C%24%3E http://symbolhound.com/?q=haskell+%3C%24%3E and bookmark these 3 pages http://www.haskell.org/haskellwiki/Keywords http://www.haskell.org/haskellwiki/Keywords http://hackage.haskell.org/packages/archive/base/latest/doc/html/Control-Monad http://hackage.haskell.org/packages/archive/base/latest/doc/... http://hackage.haskell.org/packages/archive/base/latest/doc/html/Control-Applicative#g:4 http://hackage.haskell.org/packages/archive/base/latest/doc/... ______________ also, I think more time reading code, esp type signatures, and less time reading language spec and descriptions of syntax vs. O-O pantheon: https://news.ycombinator.com/item?id=4863667 https://news.ycombinator.com/item?id=4863667
- mitchi 14y agoI'm also fairly interested in Haskell but I think that studying data structures and algorithms extensively will make me a better programmer. Not learning a whole other way to program... That will not work. I am already very comfortable in C++/Java/D and other imperative languages. I can do the same work faster because I can use global state to get easier solutions to problems. If I want pure functions, I'll write a pure function. If I want shorter, more readable code I'll write a macro. Or maybe not.
- ky3 14y agoI'm genuinely curious how you'd prepare for a multicore and cloud computing future. Using global state is like blowing Gabriel's horn -- everything dies! Ok, so not everything, but the upshot is that global state plays very badly in the future of computing.
- mitchi 14y agoI feel very comfortable actually, since I'll program in the same language that the OS uses. I'll feel completely at home. The Haskell language uses the operating system to switch threads and send datagrams. And isn't the OS the biggest user of global state? It's like doing Yoga in a Karate class.
- alayne 14y agoI recommend some other functional language like Scheme or OCaml. All I got from my time with Haskell was frustration. I read RWH and worked through tutorials. I never got to a point where it didn't seem like the simplest tasks weren't painful.
- tomjakubowski 14y agoReal World Haskell isn't a very good introductory text. Some of the early chapters are riddled with errors and confusing, contradictory instructions. Learn You a Haskell For Great Good! is one of the better introductory programming books I've read, and the author writes in a ridiculously fun and easy-to-read style. http://learnyouahaskell.com/ http://learnyouahaskell.com/
- ericssmith 14y ago"ridiculously fun and easy-to-read style" But if you find 'fun' and large amounts of text distracting, then Graham Hutton's 'Programming in Haskell'[1] is a better introduction to what Haskell is about. [1] http://www.cs.nott.ac.uk/~gmh/book.html http://www.cs.nott.ac.uk/~gmh/book.html
- pretoriusB 14y agoI'd also like to see an article about how learning Haskell makes you prone to preaching to other people about how better programmer it made you and why you should learn it too, despite not having much in terms of actual production to show for it...
- charliesome 14y agoYou totally missed the point.
- Morcane 14y agoNo he didn't.
- pretoriusB 14y agoI don't care about the point. I've dabbled in Haskell myself. I just hate the preaching, especially when it's unsubstantiated.
- mbetter 14y agoHe gave concrete examples for all of his points, unlike you.
- pretoriusB 14y agoWait, what? I spoke against a concrete and specific thing, this very article, what more "examples" do you want? Pointing to a particular thing is the very definition of an example. So, yes, this kind of article is very often seen, and we're very familiar with the genre to need more examples. It even admits so itself, as it starts with: "It's OFTEN CLAIMED that learning Haskell will make you a better programmer in other languages.". As for the articles' examples: meh. Using Haskell signatures to sketch public APIs in other languages is somehow better that using any other similar notation? Even if it was so, he never proves it better. Just some handwaving. And his other big point is about thinking in terms of maps, sets, filters, reduces and such. How is that unique to Haskell? Lisps beg to differ...
- zvrba 14y agoHaskell helps you develop your imagination: > I have no idea what the word "boo" is supposed to mean when used as a function name. But I can be almost certain what this "boo" does. It takes a map that has Integer keys and String values, and a second argument that is just a String, and it returns an Integer. So I'm fairly sure that this function does a reverse mapping
- jeremyjh 14y agoActually its just deductive reasoning. Because that example is a pure function, you know there is little else it could be doing. The only question in fact would be whether or not it is doing a transformation on the value before returning it.
- lobster_johnson 14y agoThings it could be doing: * Counts the values that have the second argument as a substring * Returns key that, using some fuzzy string algorithm, matches the second argument the closest * Computes a score from matching the second argument against the map, which tracks document occurrences in a search engine type gizmo I don't think the meaning is obvious at all. If anything it's a lesson in naming stuff: Don't call your functions "boo".
- tel 14y agoThe actual function would be written more like invMap :: forall k v. (Eq v, Eq k) => Map k v -> v -> Set k The parametricity means that while this function may get specialized to have v ~ String and k ~ Int, we can be sure that invMap isn't using that information. Instead, with the typeclass restriction, we know that the only thing the function can be doing internally to the ks and vs is comparing them for equality. Intuitively, invMap can either be computing the Set of ks which hold a particular v or, maybe, the set of ks which don't hold that value and not much else.
- laureny 14y ago> Haskell makes your functions pure by default. You have to change the type signature if you want to do IO The author seems to be conflating IO and side effects. IO is just one of the many ways that side effects can occur. > Purity also means you can't read or write global variables, that's just another type of IO. Er... what? No, not at all. Reading global variables is not side effects, much less IO. Writing global variables is side effects, but has nothing to do with IO. > Functions that do IO are difficult or impossible to test. Absolutely not, IO functions are not harder to test than regular ones. Is your function creating a file? Start your test, assert the file doesn't exist, call your function, assert your file exists. Done. > I now put all my IO in top-level functions that are called directly from main This is great for homework, but in the real life, IO needs to be performed throughout your entire application, and unsafePerformIO is just not an option. You have to use iteratees for this kind of operation, which adds a pretty thick layer of complexity to your code base. > boo :: Map Integer String -> String -> Integer This syntax is clean but it doesn't tell me anything that def boo(map: Map[Integer, String], key: String) : Integer doesn't tell me. > The point is, you can't confidently reason about a function from its signature if IO is involved. Sure, you can: the mention of IO tells me this function operates within the IO monad, so I know a lot of operations and laws that apply to it. Haskell is a fantastic language but this article is doing a very poor job at explaining why. I suspect its author has only recently started using Haskell and would have been better advised to wait a bit before sharing his enthusiasm.
- fusiongyro 14y agoGlobal variables certainly create opportunities for side-effects. The author may be throwing everything into one big pot and labeling it I/O, but they're right that you can't sneak a global variable access into a function without changing the function's type. That's a valid point even if it's poorly worded. > This is great for homework, but in the real life, IO needs to be performed throughout your entire application My experience has been that I could get away with pushing I/O out to the fringes of the app and make 80-90% of the code pure. But everybody lives in a different "real life" and it's not unlikely that the kinds of things I do on the side for fun just don't require it. I fully concur with the rest of your remarks.
- 14y ago
- venomsnake 14y agoRant: I needed an hour to get pointers and an afternoon to grasp what function pointer is in C. 10 minutes to grok recursion, and OOP (some parts of it) came as a just a sane way to do stuff ... I am hitting my head in the monad brick wall close to a month now. Aside from that it is an amazing language that really kicks you to think differently.
- BasDirks 14y agoWant to understand monads? write some.
- dschiptsov 14y agoWhat for?
- deleted 14y ago[deleted]
- necubi 14y agoCopied from a comment of mine from a couple of years ago: "I've recently finished reading Learn You A Haskell For Great Good!, and I think the author's approach to monads works well. I'd read various metaphorical explanations that only served to further cloud the issue, but thanks to LYAH I finally got it. In short, he has you using monads long before you understand them (Maybe and IO in particular), then slowly introduces monads by first explaining functors and applicative functors. In retrospect, the mystique seems crazy. Monads are just not that confusing: they're simply values with added context, along with functions that let you interact with those values without losing the context. It's a shame that this powerful idea is so obscured by its supposed difficulty." --- I'm not going to give you any more metaphors (a monad is not a burrito). Monads are objects (in the category theory sense) that in some sense "contain" another object, and let you work on both the level of the contained object and the box around it. This can be terribly useful (and necessary in a pure language like Haskell). It turns out that monads obey the so-called functor laws (which is a just another scary category theory word for a really simple idea) and thus have the function `map` defined on them. What's really cool is that we can map (transform) the boxed data inside the monad without caring whether there's actually anything inside. Why is this useful? Consider the Maybe monad in Haskell (or Option in Scala). Lets say you want to get data out of a HashMap. You look up a specific key, but there might not be any data there. In most languages, this would be handled by returning a null or throwing an exception. Both solutions break type safety, so we want something better. Using Maybe we don't have to care whether or not the data is present to operate on it: // This is Scala because my Haskell is a bit rusty, but the // ideas are the same val map = new HashMap[String, String]("hello" -> "goodbye") // HashMap$get returns, in this case, an Option[String] val opt1 = map.get("hello") // => Some("hello") val opt2 = map.get("bonjour") // => None // What happens when we process these two options? opt1.map((x) => x.toUpperCase) // => Some("HELLO") opt2.map((x) => x.toUpperCase) // > None // Note that `map` here is the ">>=" operator in Haskell // It's different than a normal map, which takes in something // of type A, a function from (A -> B) and produces something // of type B. ">>=" instead takes in something of type M[A], // a function from (A -> B) and produces something of type // M[B]. The powerful thing about this construction is that // it lets us concatenate processing steps: opt1.map(_.toUpperCase).map(_ + "!!!").map("!!!" + _) //=> Some("!!!HELLO!!!") opt2.map(_.toUpperCase).map(_ + "!!!").map("!!!" + _) //=> Nothing // If we start out with Nothing, we always have Nothing, but // we don't need to check for it at every step. No conditional code checking for nulls, just type-safe processing on possible unreliable inputs. And Maybe/Option is possibly the simplest monad. You're probably more familiar calling map on lists, and it turns out list is also a monad, with the empty list representing the Nothing value. Things get trickier when we move into the work-horse monads in Haskell: IO and State, but the same principles apply. The IO monad lets us process the results of running IO operations without having to care (until we need to) about whether the options have succeeded or not. It also, crucially, allows us to run IO operations in a determined order by chaining the operations together. The details of all this are too long for a HN comment, so I'll just direct you to Learn You a Haskell [0] if you haven't read it, or Real World Haskell [1] if you have. Both have pretty good chapters on monadic thinking which hopefully will get you over the hump. My best advice is: don't be scared by the category theory. Contrary to popular opinion, you don't need a PhD to program in Haskell :). Good luck! [0] http://learnyouahaskell.com/chapters http://learnyouahaskell.com/chapters [1] http://book.realworldhaskell.org/read/ http://book.realworldhaskell.org/read/
- deleted 14y ago[deleted]
- simgidacav 14y agoOT: why did you post the url with the "m=1" parameter in the end?
- millstone 14y agoYou have to change the type signature if you want to do IO... Purity also means you can't read or write global variables, that's just another type of IO. If you've written so much Haskell that you think of global variables (!) as a form of IO, then perhaps you would benefit from writing in C for a while? Beware internalizing abstractions so completely that you confuse them with the reality.
- fmap 14y agoThis came up a few times already and it really isn't as wrong as it sounds. In Haskell all "side effects", which include memory stores, are in the IO monad (with a few exceptions). Writing a global variable is a side effect, because you change something other than the return value of a function. Reading would still break functional extensionality: Calling a function with equal arguments might yield different results in different contexts.
- tel 14y agoBeing "in the IO monad" doesn't actually mean "doing IO". It's a bit of a badly punned name. It's more idiomatic to think of it as the RealWorld monad.
- decasteve 14y agoAnalogous to this I found category theory gave me a better/different view understanding of modern algebra and set theory.
- tel 14y agoMy god yes! Reading through a book or two on category theory is astoundingly useful for improving your deductive chops. I'm not interested in declaring it a new foundation for mathematics, but it's such a useful language!
- wzdd 14y agoI'm sure the author is right about IO, but these sorts of articles always include such trivial examples that they do the opposite of the intended job. The one code snippet is from a dictionary look-up function. Here it is in Haskell: boo :: Map Integer String -> String -> Integer And here it is in C++ as a standalone function: template <class Key, class Value> Key boo (const Map<Key, Value> &, const Value &); or, more idiomatically, as part of a class: template <class Key, class Value> class Map { […] Key boo(Value &val); }; The author's point is that the Haskell code can't do much other than use its parameters, because there is no IO involved, whereas the C++ version could do anything. Well, sure, it could, but let's be reasonable (which is all we can be without reading the code): it's probably gonna map a string to an int. It's just as likely that the Haskell code could do weird stuff (how does it behave if it finds multiple candidates for Value?) as the C++ code, practically speaking. So this argument actually turns out to be an argument for good containers, or, more generally, for good built-in types. The author could make all those assumptions about his function from the type signature because he/she knew what "Map" meant, not because of anything inherent in Haskell's type system. I mean, I grant that Haskell has a great type system, and great built-in types. It's just that this example doesn't make the argument it claims to make. Let's look at some code I'm writing now. Here is strstr: boo :: String -> String -> Int Not too helpful really. Here is a function which searches ComplexDataStructure, which contains file and directory names as well as other data, for partial matches of "string", returning a "goodness" value indicating the closeness of the match for the highest-matching candidate: boo :: ComplexDataStructure -> String -> Int I challenge anyone to work out what the function does from that. Sure, you can make more assumptions about the Haskell version, but, practically speaking, IO cleanliness has little to do with it. What matters here is knowing what the data structures do, and using decent types.
- reycharles 14y agoI think your C++ example corresponds more to the following haskell type. boo :: Map a b -> b -> a since it nowhere mentions int.
- mifrai 14y agoI think the Haskell example should've used this type signature instead. Compared to the OP's example, this has a much stronger implication. There's very few implementations of this type that would make sense, whereas with the other one - it could be 'count' of the number of times 'string' shows up in the map; Or it could be number of letters in the provided string (ignoring the map entirely); Or it could be (key - 1); or any number of other things. Here, you can't just magic into existence or modify an unknown 'a' value.
- dschiptsov 14y agoSure. Instead of learning the power of composition of high-order functions, you will learn that particular composition, with a trendy name, and will start to argue on forums, that this particular function composition is somehow superior to all other compositions and you must do everything using that particular composition, which is called monad..) There is one question, before you press "down-vote" few times - what is the sequence of machine instructions your monad compiles into?
- tmhedberg 14y agoWho is arguing that the monad as a means of composition is superior to all others? Or that Haskell doesn't emphasize the power of composition of high-order functions? Function composition is one of the most important tools in a Haskell programmer's toolbox. Monads are just another one of those. The thing is, most popular languages these days support higher-order functions. It's nothing to write home about. Monads get a lot of attention specifically because they are a fairly unique idea that is not supported out-of-the-box in other languages. People who are advocating (or evangelizing, if that's your perspective) for a particular thing are going to focus primarily on the specific aspects of that thing that make it stand out from the crowd. For Haskell, that means things like monads, algebraic data types, non-strict evaluation, functional purity, etc. There is one question, before you press "down-vote" few times - what is the sequence of machine instructions your monad compiles into? I can't understand what you're getting at here. What difference does it make what code the compiler emits, unless I am trying to aggressively optimize low-level performance? If it does what I intend it to do, and is reasonably fast at doing it, I don't care about the specific instructions. How is that relevant?
- dschiptsov 14y agoThe question was why should one have to learn some sophisticated, over-engineered type system along with unreadable notation, when the result will be plain, sequential machine code anyway? What is the point besides "I'm so clever"?
- mbetter 14y ago
- diminish 14y agoAnd learning Lisp will make you a better Haskell programmer. Read PG's arc tutorial.
- Peaker 14y agoIndeed, extending your repertoire is definitely a good thing.
- dudehuh 14y agoI swear if I see another gray on white web page, I will throw my own S#!7 at you like a 900 pound gorilla in a zoo!