Learn You a Haskell for Great Good
learnyouahaskell.com
learnyouahaskell.com
To elaborate on why you shouldn't read (at least the online version of) Real World Haskell first: It has a lot of important topics that it covers that LYAH does not cover (like how to use cabal their package manager). But, it's not as good of a tutorial on the language and functional programming. It glosses over very complex topics, goes into a lot of depth on details that are not so important and worst of all, gives you some exercises that you aren't capable of answering yet. I tried learning Haskell three times from that book and gave up because it just killed my confidence.
Then I discovered LYAH. It explains things very simply and at a very good pace. If you want to learn Haskell and/or functional programming, I can't recommend it more. Imagine one of the Head First books without all the corny. The only thing I wish it had was exercises.
I felt so loved as a customer of No Starch Press. They sent me the book, let me download the ebook in three free formats (no DRM), plus I got a bunch of freebie stickers.
The book itself is also quite remarkable to me in that I can read it and understand Haskell without needing a computer. I am used to reading mathematics books, and while I don't think Haskell is a programming language for all mathematicians as is so often advertised, the language does have a certain appeal to it in that you can treat its programs as abstract exercises that you can do independently of a machine. The statelessness of the whole thing doesn't need me typing stuff into a machine to see what a particular line of code. This makes it great for offline reading, away from the computer.
So yeah, get the treeware version. Support your favourite authors. :-)
OK so let's recap, you create a new type with `data`, which is formed of a type constructor that starts with the keyword `data` and a data constructor which doesn't start with `data`. To create aliases you use `type` and `newtype` and to create type classes you use `class`.
Now how do you call the type class of things that can be mapped over? Surely that must be something like `mappable`?
They were PL researchers. Used to ML, Common Lisp, Ada, and, most importantly, Mathematics and Category and Type Theory.
`data` introduces an algebraic data type, so it makes sense to use data as a keyword. `type` and `newtype`, on the other hand, are programmer's conveniences: both carry no runtime cost, they are not actually part of the semantics of the backend language.
`map` is a function that's really just a specialization of `fmap`. Now, my category theory is a bit weak, but if I'm thinking correctly here, `fmap f` is an endofunctor in the category of functors, and so it makes perfect sense to call "mappable" Functor. There's also "Traversable," which works pretty much like a fully-fledged "mappable" with all extras, but also requires a Functor instance.
Keywords, and idiosyncrasies of a syntactic nature are really just superficial. The actual elegance of the language is its type system. (The actual warts of the language are, for the most part, also in the type system; records for example.) The latter criticism about `mappable` is really just due to the fact that you haven't gotten used to the underlying theory (and I don't blame you, it's hard.) But getting used to it can be rewarding. I'm not saying it's going to make you a better programmer (I'm not necessarily of that opinion,) but it's rewarding in its own right.
Oh, and the difference between data constructors, type names, and, later, kind names (with promotion, since 7.4,) can be confusing. I sometimes find it an oversight that they are not distinguished syntactically, I think that would greatly help most newbies, and sometimes even make things easier to read and understand for veterans, too.
Furthermore, mathematics is all about communication. The language of mathematics exists to codify concepts so that they can be talked about concisely. Being able to say 'group' instead of 'set with an associative binary operation with identities' is essential if you want to be able to build on top of that concept without taking an hour to read one theorem.
The handicap in communication isn't on the mathematics end, it's on your end. You seem to expect them to be able to explain structures to you that took years of work to build by using the same language that you use to talk about sports or social events. The reality is that you are not the target audience of their communication, and they are okay with that. You should be too.
The weirdest assertion that you made is that high-level programming languages ought to be as close as possible to human languages. The two categories of languages exist to communicate fundamentally and widely different groups of concepts. Words represent categories of analogous concepts, and the relevant categories in human life are nothing like the relevant categories in programming. In Haskell, 'functor', 'applicative functor', and 'monad' are highly relevant categories. They pop up everywhere and can be leveraged with great benefit. In human life these concepts are far less common, and thus do not merit words in the common vernacular. Were we to use a programming language modeled on English, we would miss the benefit of these abstractions, trading them for categories like 'dog' and 'car' which have very little practical use in typical programming.
>>>Being able to say 'group' instead of 'set with an associative binary operation with identities' is essential <<< OK, but the word "group" should be used for no other meaning...
>>>The reality is that you are not the target audience of their communication, and they are okay with that. You should be too.<<< As you can see pretty much anyone is the audience of some math and its inconsistency and ambiguity. It just varies the level and the amount of it.
>>>The weirdest assertion that you made is that high-level programming languages ought to be as close as possible to human languages. The two categories of languages exist to communicate fundamentally and widely different groups of concepts. Words represent categories of analogous concepts, and the relevant categories in human life are nothing like the relevant categories in programming. In Haskell, 'functor', 'applicative functor', and 'monad' are highly relevant categories. They pop up everywhere and can be leveraged with great benefit.<<<
False. Computers and software are mainly used to emulate some real world stuff (objects, actions etc.) and to help people with real world stuff in a more automated way. They aren't used too much to prove theorems or some other math stuff. And pretty much no one cares about proving the so called "mathematical correctness" of a program - a concept that doesn't even make sense in most cases. Old misconception among FP advocates, even Dijkstra himself admitted that he was kinda wrong about how computer would evolve and what they'd used for. But the associated misconceptions live on. A language close to human language also helps avoiding errors. That's why you won't see functional languages in critical systems, but rather languages like Ada which is probably the closest programming language to human language. The claims of clarity of FP languages are pretty much at odds with the evidence the real world provides.
If you're going to make the claim that functional languages use ambiguous symbols, you're going to need to back that up with some examples. I find it exceedingly hard to believe that there is any ambiguity in the operators of a statically and strongly typed language like Haskell.
Those "professionals who actually know what they are doing" don't seem to exist when it comes to functional languages. The evidence is the very fact there's not a single piece of important commercial software written in such a language. The question is rather: can such specialists exist? Because I'm afraid they can't exist because the functional approach is fundamentally wrong.
Examples of ambiguity in FP? What is the following line supposed to mean and what part of it suggest anything about that:
a b c
How is ~ an intuitive replacement for minus? How is (* 5 5) supposed to be as clear as 5 * 5 ?
ps. dynamic typing and type inference are two awfully bad things and either of them can lead to trouble in large programs
And yes, I am a "real programmer" and don't use Haskell at work myself (for the run-time performance reasons).
I also disagree overall with your assessment of mathematical notation. But it doesn't really matter because Haskell doesn't actually use any math notation. It does use some math terminology, though, and overall mathematicians are absurdly pedantic about terminology. Mathematicians may not be great at telling jokes at parties (though you may be surprised!), but it's their job to make sure they are writing in consistent and unambiguous ways.
Haskell wasn't created for "most people". It's not some replacement for Python or Ruby, it was designed for formality.
I've tinkered with other functional languages (Erlang, Scheme and Clojure), but Haskell is a stranger beast.
Also, ML? I know OCaml a bit, but according to this comparison: http://adam.chlipala.net/mlcomp/ Standard ML has a "ref" type, which in effect allows mutable state. While it lack a bit of a syntactic sugar for this, OCaml's mutable record fields are implemented in terms of refs, so in the end SML isn't "more functional" in this than OCaml. Also, according to this answer (didn't check specs) SML has at least a WHILE loop: http://stackoverflow.com/questions/818324/loops-in-sml-nj which is typical to imperative languages. OCaml makes no attempt to outlaw side-effect (I suppose SML is similar), it allows them explicitly by introducing functions which return () (unit type), which are called purely for their side-effects. There is even List.iter function, which accepts a function executed for each element only for side-effects. And side-effects are not restricted in any way.
On the other hand Erlang has nothing like that - no loops, no mutable state in the language and it's side-effects are restricted to message passing.
While Scheme and Clojure, too, allow mutable state and non-pure functions, the default is immutable and pure for almost everything. In Racket the most used data-structure, a pair (and lists by extensions) is immutable by default and all the functions working on pairs and lists are pure. There is another type, "mutable pair", which is used to construct mutable lists, but I haven't seen it used yet.
Coq is not even turing complete.
Anyway, I much prefer talking about "functional style" than "functional programming"/"functional languages". If you think that one cannot use monads outside of Haskell - you're wrong. The same applies for lazy evaluation, which is available in many languages as extension or special syntactic construct in the language itself.
EDIT: I realized that parent probably thought of AGDA instead of ADA. And so I just installed it to check it out :)
Edited for fun and grammar!
The first is the more common: learning these languages exposes you to other ways of programming, which in turn makes you a better programmer no matter what language you're programming in. This is unquestionably true. Mainstream languages are constantly learning new things from these languages, which means you will be able to apply the techniques you learn from them to your code no matter what language you write in. I have the misfortune[1] of writing a lot of PHP at my day job, but it turns out even PHP has support for filter and map over arrays. If I hadn't learned functional programming, I would never have even looked for them.
The other argument, which is made less often, is that you can actually write great software in these languages. This argument is made less often because we can obviously look at the market and the TIOBE index and see that Haskell is way down at #30 with 0.323% of the market share. However, there are really great programs made in Haskell (xmonad, for one). There are also tons of great programs made in Lisps, especially Clojure of late. So, this second argument is not entirely a dead end either.
Revisiting something I mentioned in the first argument for a moment though, I would actually make a third argument that I rarely see. As I said, other languages are constantly learning new things from these languages. Haskell's community is full of PhDs studying at the intersection of programming and mathematics, and they are discovering the things that will revolutionize programming in the future. Haskell, for example, has been exploring strong static typing with algebraic datatypes[2], Hindley-Milner type inference, and typeclasses to support higher-kinded polymorphism[3]. Those concepts are turning out to be incredibly powerful, and you will inevitably see them in other programming languages in the future. We've seen this before with Lisp (garbage collection, recursion, first-class functions), and there's no reason not to expect the trend to continue.
So, why learn Haskell? It's the easiest way to see what tools you'll have available to you in industry 20 years from now when the other languages catch up.
[1]: http://me.veekun.com/blog/2012/04/09/php-a-fractal-of-bad-de...
It's probably worth mentioning that Jane Street writes all their trading infrastructure these days in OCaml. They're probably one of the biggest industrial users of a functional language.
There's also Tsuru Capital, a trading firm of lesser note than Jane Street, which almost exclusively uses Haskell.
Mailrank's backend was also written in Haskell.
For the same reasons you need any programming language—to write useful software in!
> Currently I am learning Java…and Ruby
These are both good ideas. Java is widely used, reliable, and well tested for developing large applications. Ruby is simply a lot of fun to use, and one of the things you’ll discover while using Ruby is that it places a lot of functional programming idioms in an object-oriented context.
The issue with many popular languages is that they fall into much the same paradigm, namely imperative and either procedural or object-oriented, with some functional features borrowed for working with lists. It’s worthwhile to see that not all languages are like this, and learning a new language is not just about new syntax and APIs.
> but what about Haskell? I am genuinely curious what Haskell can offer.
Haskell will teach you to think about structuring your program in terms of simple data and transformations on that data. Object-oriented programming is about coupling data with behaviour, whereas a big part of learning Haskell is learning how to recognise high-level patterns in data structures and functions separately. In doing so, you can avoid the work of reimplementing common operations yourself.
You begin to say, oh, this is a data structure I can map a function over; I’d better make it an instance of Functor. And hey, I could use this data structure to represent this kind of computation, maybe it should be an Applicative and a Monad so I get some nice syntax for dealing with it. This function has a type signature that looks an awful lot like that other type signature—I wonder if I could implement one in terms of the other? And so on.
The type system and succinct syntax also make it very easy to create new data types, and you learn how this can help avoid bugs—don’t use a Boolean here, use an enumeration with two values; then you can’t mess up. Java discourages this by requiring more syntactic effort to create a new datatype.
A useful and oft-cited example of this is in the context of web frameworks: if you have separate data types for strings and HTML, that one simple thing eliminates a whole class of injection attacks. If you represent your links in the type system, then you can statically ensure that your site does not have any broken links. Simply put, quite a lot of bad things can be caught at compile time if you have a type system expressive enough to talk about them.
> I have yet to come across any notable project written with it.
There are more notable projects out there, but here’s my self-promotion. At my old job[1] I maintained a compiler for ActionScript 3 in Haskell, as well as a build system that coordinated the compiler and other build tools to convert Flash games to iOS and Android applications.
Haskell is excellent at manipulating data structures—if that sounds ridiculously general, that’s because it is. The compiler is very nearly feature complete and, even with a certain amount of technical debt, was an order of magnitude smaller and easier to refactor than it would have been in an imperative language.
So there you go. From a recovering professional Haskeller, why you should learn Haskell. And, for that matter, the answer to “should I learn this language?” is always “yes”; but some languages should be higher on your list than others. ;)
[1]: http://spaceport.io/
Now, once you have your feet wet, it's worth learning. Here's why:
- Pure(mostly) functional language. This is in opposition to Java & Ruby, which while having functional aspects are OO (Object Oriented). - It's okay not to know what a functional language is. It's making functions first-class citizens, such that a function becomes a value like any other. - Separates IO from computation. IO is inherently impure and unreliable, so functions are generally pure and instead wrap IO in containers. Obviously, programs would be less useful (still use in the compiler running!) without IO. - All of these concepts (and more) will change how you think about programming and make you a better programmer in all languages.
Haskell can be found in some backend stacks, the more notable examples I can think of are Mailrank (acquihired by Facebook) and Bump.
Personally, I've only done a limited number of projects with Haskell (a Postgres-backed beer API & IRC bot) but I've grown to respect it as a language and through limited exposure has made me love FP.
Ideally, these components are composable and reusable, both to facilitate reasoning about the code and code reuse. The Haskell Standard Prelude is a fantastic example of this working out well.
Basically, it's based on the idea that the functions which are easiest to reason about are those with one well-defined input and one well-defined output which is completely determined by that input. You can't always have this, but functional languages attempt to get you as close as possible. The simplicity of pipeline-style programming is one of the happy little dividends of this kind of thinking.
Haskell is worth learning because it has an expressive, strong, static type system. Most other languages do not have that property (Java's type system is static, give or take null, and strong, but not expressive, and Ruby's is strong but neither expressive nor static). The Haskell type system lets you have conversations with the compiler about what your program should do.
You can declare a state type, and have the compiler tell you if you switch on the state and forget to handle one of the possible states (or if you add a state later and forget to update all the switches).
You can tell the compiler "I think this function is a generally useful utility function that I plan to extract out of this project and use elsewhere", and it will tell you whether there's some dependency on your project that you missed.
You can specify in a function's type "this function should have access to the database"; then the compiler can tell you if you're trying to access the database somewhere you didn't expect to. (A colleague has just spent a person-year of effort refactoring a Django web app, because all the database calls were happening in the view code, and they needed to abstract them out to scale.)
Of course there are ways to do some of those things in other languages, and of course there are also downsides to the static type system. But those sorts of abilities open up new programming techniques (even in languages with poorer type systems), and have shown me ways programming can be better.
By the way, does Haskell's type system have the "value restriction" that you run into in ML?
Haskell's type system is practically a language itself. It has many powerful features that aid with expression:
* sum, product and generalized algebraic data types (GADTs)
* higher-kinded types
* universally & existentially quantified types
* scoped type variables
* impredicative types
* associated types
* type families
* kind polymorphism (a new feature)
* type holes (also new)
>By the way, does Haskell's type system have the "value restriction" that you run into in ML?I'm not sure what you mean by "value restriction". Haskell, like ML, is not a dependently typed programming language. For that you'll want to use something like Agda. As for why you'd want a language to not have dependent types? Well, having them introduces all kinds of considerations with regards to decidability and type inference.
http://mlton.org/ValueRestriction
It can be annoying to work around this, because there are potentially ML programs that would be type-safe and would otherwise compile if they were not hindered by the value restriction.
I got my answer, though:
Ii is interesting that in ML, the presence of mutable ref cells and parametric polymorphism requires the whole language to be dominated by a "value restriction" [1] to ensure that the type system remains sound, whereas in Haskell, because IORef's can only be created (and used) in the IO monad, no such restriction is necessary.
http://www.haskell.org/pipermail/haskell-cafe/2007-July/0292...
http://learnyousomeerlang.com/
Which is a fantastic book, clearly written as a labor of love.
Erlang doesn't have Haskell's type system and doesn't seem to be of much interest for CS research types, but it's awesome for developing certain kinds of system.
I am curious about Haskell myself, and don't tend to need a lot of excuses to learn a language, but I don't perceive a "sweet spot" for it just yet, as in "I could go build X, and Haskell would be a great fit for it". Given how many smart people use it, I'm sure there are a lot of applications for it, but I also get a feeling that there's not one thing that really stands out.
xmonad, one of the best tiling window managers for Linux, is written in Haskell (and is open source).
...and BSD
Introduction to Haskell lecture slides - http://shuklan.com/haskell/
Real World Haskell - http://book.realworldhaskell.org/
http://www.reddit.com/r/haskell/comments/1af3iw/9_best_free_...
http://www.linuxlinks.com/article/20130316105209238/BestFree...
http://channel9.msdn.com/Series/C9-Lectures-Erik-Meijer-Func...
Take this in p52 for instance:
maximum' :: (Ord a) => [a] -> a
maximum' [] = error "Maximum of empty list!"
maximum' [x] = x
maximum' (x:xs) = max x (maximum xs)
Now, you may think that implies that you could do something like this to print a string one character at a time:
rsoat (x:xs) = show x (rsoat xs)
Buy you can't. If you try that Haskell complains "The function 'show' is applied to two arguments, but its type 'a0 -> String' has only one...."
Fair enough, one might reason - you're just passing max two things when it wants one so we can do something like this:
rsoat [] = []
rsoat [x] = show x
rsoat (x:xs) = rsoat xs
But no, that just gets you the tail of your string because x:xs doesn't match x so it never prints the other bits.
What you may end up doing is something like this:
rsoat [] = []
rsoat [x] = [x]
rsoat (x:xs) = show x ++ (rsoat xs)
But that's still not really doing what you wanted to do in the first place. It's sticking all your characters into a string and then reading that string all at once. And it's not a particularly obvious solution either if you don't know the language.
This may seem a silly example. But what happens when, as is frequently the case, someone wants to retrieve the nth element of a list? (list !! n) Haskell, as learnt through Learn You a Haskell, can easily be expected to drive someone up the wall who starts asking such questions - it's not a whole bunch of fun to play with.
You seem to start off from a wrong premise that the show function prints things. It doesn't - it simply converts whatever value (of type a) you give it to a String (provided a is an instance of the Show typeclass). So seeing that you already have a string, a function which converts things to strings will probably not be very useful.
> rsoat [] = [] > rsoat [x] = show x > rsoat (x:xs) = rsoat xs
> But no, that just gets you the tail of your string because x:xs doesn't match x so it never prints the other bits.
Uh, no. It doesn't give you the tail of the string, it gives you a list containing just the last element of the argument (if the argument's not empty), or the empty list (if the argument is empty) - which you can read directly from your definition.
Furthermore, " x:xs doesn't match x" doesn't make a whole lot of sense - it's not supposed to match x, it's supposed to match a non-empty list. Which it does, and the first element of such a list gets bound to the parameter x - but you do not use the parameter in your right-hand side of the definition, so you effectively discard it.
Note that both pattern matching and the Show typeclass (and the corresponding function) are discussed in the book prior to the minimum example.
No, I know what it does. That's how the book describes it however:
"The most commonly used function that operate on instances of this type class is show, _which prints the given value as a string_"
> Uh, no. It doesn't give you the tail of the string, it gives you a list containing just the last element of the argument (if the argument's not empty), or the empty list (if the argument is empty) - which you can read directly from your definition.
I continually forget that tail means the rest of the thing.
> Furthermore, " x:xs doesn't match x" doesn't make a whole lot of sense - it's not supposed to match x, it's supposed to match a non-empty list. Which it does, and the first element of such a list gets bound to the parameter x - but you do not use the parameter in your right-hand side of the definition, so you effectively discard it.
We're far enough away from what each other are thinking that this just isn't worth talking about as written.
You've already matched your list to x earlier in the program so x:xs doesn't get a chance to run. You're not showing x and then falling through to pass xs to the function again and chopping the first element off of that to show - and so on.
> Note that both pattern matching and the Show typeclass (and the corresponding function) are discussed in the book prior to the minimum example.
This:
http://i.imgur.com/7NKReoL.jpg
is not a discussion of the show type class and corresponding function.
> Members of Show can be presented as strings. All types covered so far except for functions are a part of Show. The most used function that deals with the Show typeclass is show. It takes a value whose type is a member of Show and presents it to us as a string.
This is correct. Whether it qualifies as a proper discussion might be debatable, but since the particular typeclass is simple enough, and seeing that this appears very early in the book, it seems reasonable.
> You've already matched your list to x earlier in the program so x:xs doesn't get a chance to run. You're not showing x and then falling through to pass xs to the function again and chopping the first element off of that to show - and so on.
This is incorrect. Definitions are not really "executed" (or run) in Haskell; you simply replace the left-hand side of the definition with the right-hand side. You use the first definition (in file order) with a matching LHS. So in your example, the third definition for rsoat would be used for all lists with more than one element. So the "falling-through" as meant here is not what you get with, say, a switch statement in C.
I'm not sure how far you got after this point, but the problem is easily solved with a higher-order function:
mapM_ print "blahblahblah"
Or if you don't like each character being quoted: mapM_ (putStrLn . return) "hello one at a time"
Or did you not want the characters on their own lines? In that case, you can do this: mapM_ putChar "horse raddish"
But why bother doing that when you can simply do this?: putStr "egg freckles"
>But what happens when, as is frequently the case, someone wants to retrieve the nth element of a list?If you are frequently wanting to do this, you are probably doing something wrong. Haskell's lists are intended to be used as control structures, not indexed data structures. For those, you'll want an Array, Vector, IntMap etc.
I don't think that's really the books fault as much as it is Haskells. I also think it's why you hear ALOT of people swearing Haskell is actually very easy, because everything up to chapter 9 is not very complicated at all.
I'm sure there are some very tallented people where this isn't true, but I get the impression that the people who are saying it's easy haven't gotten to the point where you can actually do things with Haskell, or developed a program with Haskell, or inter-operate with other libraries using Haskell, or tested their metal against the Parsing Combinators in Haskell (let alone making your own set), or ect. ect. ect.
Chapters 1-8 aren't complicated in the slightest.
Chapter 9 is when it starts.
Haskell isn't easy.