XXX Hi, Redditers -- this was somehow linked directly out of my darcs repository and is not at all complete. Additionally, I decided there are a ton of other Haskell tutorials out there so I sorta abandoned working on this back in June 2006. With that said, it looks like the initial feedback was sorta positive (despite some embarrassing errors) so I guess you might as well read and enjoy, and maybe I'll finish it someday. -- Evan Martin, 13 July 2007 XXX = Haskell for Programmers Many people have written Haskell tutorials, but when I learned Haskell they didn't present it in a way that made sense to me. The author's perspective always leaks through in the exposition. So here's my perspective, and what I expect of you: you hate it when people waste your time by using more words than necessary; you should be comfortable programming (particularly with a high-level language, such as Perl, Python, or Ruby) but maybe not so much with "funky languages"; you are interested in learning new things for their own sake; you are interested in practicality; you learn through examples ('strings look like "foo"') better than abstract statements ('strings are sequences of characters surrounded by double-quotes'). Some brief style tips on getting the most out of this: - The exercises included are intended to be worked through, at least mentally if not at a keyboard. Sometimes they introduce new concepts. - Jargon and new terms are typically //emphasized//. === Why Haskell is Worth Learning You're already here, so there's not a lot of convincing to be done. Instead, here are the reasons I think you //ought// to be here. - Haskell is different than what you're comfortable with. Another way of saying that that is that it's "Seriously Weird", but in a good way. It has changed the way I think about programming. - Haskell code is beautiful, and that makes it enjoyable to both consume and produce. - Some people write Real Programs with it. Notably: Perl6 (pugs) and darcs. I'm not promising much here, but I also think this is less important; if you can get your job done using Perl, then go get your job done. - Some people argue it is the future of programming. See, [[sweeny.pdf]] for a discussion of this by one of the creators of the game Unreal. Even if the language of the future is not Haskell, it's likely some of Haskell's ideas will percolate down to more popular languages much in the way garbage collection did in the last decade. === The two things you have to get over to learn Haskell 1. Haskell is not a just a new syntax for a language you already know. Many languages are just new paint jobs on the same car. Haskell switches out some more fundamental ideas, which has the following important effects: - Haskell uses some terms (such as "class") that are found other languages, but with a meaning that is very different. Once you understand things fully you can sorta squint and understand the name choice, but until then it's better to just pretend that the used the same name for an entirely different thing. - Haskell does not have a lot of features you may be comfortable with and expect to have a new language to have. You will become frustrated if you try to "translate" an existing program or construct into Haskell. So instead of "how do I do [x] in Haskell?" (where [x] is e.g. "read a line of input from the user"), try to think of problems in terms of "how do I achieve [x]?" ("how do I make my program work with user input?"). Another way of saying this is: this will be the best for you if you relax, and try to go with the flow. 2. Haskell has a bunch of features you may have never seen before, and they require some head scratching. In particular, understanding Haskell requires thinking at a //higher level of abstraction// than other programming languages, and requires practice. But hopefully that is part of why you're here: you want to learn something. === Getting Set Up There are multiple implementations of Haskell, but you should use GHC; everyone else does. Debian/Ubuntu users: apt-get install ghc6. Others: {XXX download link}. GHC comes with "ghci", which is a read-eval-print loop that lets you type in expressions interactively and see their results (like Lisps, "irb", or "ipy"). However, for various reasons you can't really write full programs with it. I recommend instead writing code in a text file like you would another language, and then loading it up into ghci to play with it: ghci test.hs You can then hit ctl+d to exit. Below, I show input to ghci by showing the prompt as a ">". == First Steps === First, an Apology Some things (such as printing text) that are simple in other languages are more complicated in Haskell, so please forgive me for skipping "Hello, world". (It's a trivial single line of code, but explaining it is more involved.) Similarly, error messages can be difficult to understand until you get what's going on, so don't worry about them too much yet. === Diving in Fire up a text editor and write some code. (Or copy'n'paste this.) myint = 2 y = myint + z z = 1 -- single-line comments start with a double-hyphen. -- infinity is one greater than... well, infinity. infinity = infinity + 1 mylist = [1,2,3] Try loading this into ghci. All Haskell code is a collection of //definitions//: that first line means "define ##myint## to stand in for 2". This is different than in other languages: in Haskell, a given name (like ##myint##) can have only one meaning, so it's an error to later try to give it a different definition. (In C++-speak this means everything is defined "const", and there's no way to turn it off.) How can a collection of definitions actually do anything useful, especially when the whole point of a program is to transform some input into some output? That's part of the magic of Haskell! And it will be explained. At the ghci prompt, you can type in //expressions// (the right side of an equals sign of a definition) and ghci will print out what the expression evaluates to. Try a few: > 1 + 1 2 > myint 2 A few caveats to note upfront so you don't get confused: - Note that you can't make new definitions here. Only expressions are allowed. (This is why, earlier, I suggested you get into the habit of putting code into a file to test it.) - If you get an error about "##No instance for (Show ...##", what that means is that you typed a valid expression, but there is not a defined way for Haskell to show it. So what about ##y##? Though it's defined in terms of something that comes later, since these are all just definitions it doesn't really matter which one comes first. (Using "later" in the previous sentence was a bit misleading.) In fact, you can even define something in terms of itself, and later we'll use this a lot. Ask ghci for the value of ##infinity## -- infinite loop! (You can hit ctl-C to interrupt it.) This demonstrates a few of the great magics of Haskell: there is no mutation (once a name is bound to a definition, there's no changing it), there is no specific order of evaluation (the order in which things are computed doesn't really matter), and things can be defined in terms of themselves. === Functions Functions are very fundamental to Haskell, and so calling a function gets a very simple syntax -- just a space between the function and its arguments: > sin 1 -- a builtin mathematical function 0.8414709848078965 > length mylist -- list length 3 > div 6 3 -- integer division: divide 6 by 3 2 To nest function calls, you must use parenthesis. ##sin div 6 3## means "call ##sin## with three arguments: ##div##, 6, and 3", while ##sin (div 6 3)## means "call ##sin## on the result of dividing 6 by 3". This syntax is weird at first, but it'll eventually feel natural. To define a function, it's written as below. (Again, since this is a definition, it must go into your test program and then reloaded into ghci.) -- a one-argument function: oneLessThan x = x - 1 -- a two-argument function: add x y = x + y -- a function that takes another function as an argument: applyTwice f x = f (f x) Functions themselves are values. You can give them names, pass them as arguments to other functions, and put them in lists. Here's an example using ##applyTwice##: > applyTwice oneLessThan 5 -- one less than (one less than 5) 3 You can create a function as an expression with the following syntax: > applyTwice (\x -> x + 1) 5 7 The backslash introduces a function (it's simulating a lambda: λ), the bit following it gives the arguments names, and the bit after the arrow is the body of the function. We could have defined "add" equivalently like this: add = \x y -> x + y Note that there is no "return" keyword in Haskell. One way to look at it is that these functions aren't "returning" anything. Instead, when you write "oneLessThan 2", you're just substituting 2 for x in the definition of oneLessThan. Along those lines, instead of using terminology like "call add with two arguments", people often say "//apply// add to two arguments". Any two-argument function can also be used with a special syntax, which puts the function name between the two arguments: > 6 `div` 3 2 > 20 `add` 30 10 This sometimes makes code clearer, and has no special meaning beyond that. In fact, even operators like + are functions, except that they default to working in the "between" mode and they obey the normal mathematical notions of precedence. You can use them as regular functions by surrounding them with parenthesis: > (+) 1 2 3 In Lisp, (nearly) everything was a list. In Smalltalk, (nearly) everything was an object. In Haskell, (nearly) everything is a function. Much like in Lisp and Smalltalk, using a single concept pervasively allows for powerful composition throughout the language. Exercises: 1) Precedence. a) Write an expression to find the the length of the hypotenuse of a right triangle whose sides are 5 and 9. (The exponent operator is ##**##, and the square root function is ##sqrt##.) b) What does "sqrt 25 + 81" mean? 2) Parenthesis. a) Write an expression for the sum of the first three integers, using addition only like a regular function that takes two arguments to its right. Pay attention to getting the parenthesis right! b) Now write it as you would in normal code. Answers: 1) a) sqrt ((5**2) + (9**2)) Actually, the parens around the exponents aren't required, due to operator precedence. But you may as well write them if you're not sure. b) sqrt 25 + 81 = 5 + 81 = 86 The one precedence rule that will bite you for a while is that function calls have higher precedence than all the operators. This is sometimes useful and sometimes annoying. 2) a) (+) 1 ((+) 2 3) It looks a bit like lisp, doesn't it? These languages all have their roots in some similar areas of mathematics... b) 1 + 2 + 3 Since addition is associative, we don't care whether that's (1+2)+3 or 1+(2+3). === Types Haskell is statically typed. Static typing has negative associations for many programmers, but I like to think it's just because they haven't been exposed to a good treatment of it yet. Every well-formed expression in Haskell has a type. You haven't seen any yet because Haskell figures out types out for you, but they're still there. (This is one of the reasons static typing in Haskell is pleasant: you almost never have to write types out, but they are always in the background helpfully verifying your code is correct.) You can ask ghci for to display the type of an expression by using the ":t" command. (This is a ghci-specific command and not part of Haskell.) > :t myint myint :: Integer > :t mylist mylist :: [Integer] The double-colon can be read "is a" or "has type". The square brackets around a list type can be read "a list of". Putting them together, that last line reads: "##mylist## is a list of integers". (A list can only contain one type. Another way of saying that is that all elements in a list must be the same type.) If you want, you can annotate your code with types, using that same syntax. It sometimes helps explain your thinking, and also can help catch errors. Trying to load this code: one_third :: Integer one_third = 1 / 3 will result in an error that looks like this: No instance for (Fractional Integer) arising from use of `/' at [...] because / results in a fractional number, but you've declared that ##one_third## is an Integer. (You can use ##div##, discussed above, for integer division.) === Fancier Types Haskell types often involve //type classes//, which we'll discuss later, but for now you need to at least recognize them when you see them. (First, keep in mind the disclaimer from the beginning: these are very distinct from the classes in object-oriented programming.) > :t 3 3 :: (Num t) => t "Num" here is a type class. Everything before the => sign is a constraint on the type, and every after the => sign is the actual type. This type is saying that 3 can serve as any type "t" as long as "t" is an //instance// of Num (which is short for numeric). For example: > :t (3 :: Integer) (3 :: Integer) :: Integer > :t (3 :: Rational) (3 :: Rational) :: Rational > :t (3 :: String) ... No instance for (Num String) Both integers and rationals are numeric, but strings aren't. === Function types Function types are written with an arrow, with the argument on the left and the result on the right: > :t oneLessThan oneLessThan :: (Num a) => a -> a ##oneLessThan## takes a number and gives you back another number of the same type. If you want, you can read the arrow as "maps", like in the mathematical sense, and to sound like you have a math degree you can say "oneLessThan maps numbers to numbers". Multi-argument function types are written with a sequence of arrows. Let's look at ##add##, our addition function: > add 3 4 7 > :t add add :: (Num a) => a -> a -> a What does that type mean? It'll help if we add some parenthesis to make an equivalent definition that shows how the arrows are grouped: add :: (Num a) => a -> (a -> a) That type says that ##add## takes a number ("a") and produces another function -- the one in the parenthesis at the end. Then, that new function has the same type as ##oneLessThan##: it takes another number and produces the final answer. You can even make these two parts explicit with parenthesis: > (add 3) 4 -- equivalent to the above 7 First we pass 3 to add and get a function back, then we pass 4 to that function and get 7. Logically, this intermediate function must add 3 to its argument. You can //partially apply// add to grab that intermediate function. In your test file, you can now define a function just like ##oneLessThan## but with a simpler definition: oneMoreThan :: Int -> Int oneMoreThan = add 1 If you take a step back at this point to review you'll realize that every function in Haskell takes only a single argument, and that multiple-argument functions are built by chaining these simpler functions together. This is sometimes known //curried functions//. Exercises: 1) Write a function that takes three Ints and sums them. What is its type? 2) Write a function that takes a function and an integer and applies the first to the second. The following code should work: > apply oneLessThan 3 2 Answers: 1) addThree :: Int -> Int -> Int -> Int addThree x y z = x + y + z 2) apply :: (Int -> Int) -> Int -> Int apply f x = f x === Lists, Strings, and Polymorphism Define a few lists in your test program and load it back into ghci: -- strings are double-quoted and allow the normal backslash escapes. strings = ["hello", "world"] -- characters are quoted with single quotes, like in C. chars = ['a', 'b', 'c'] -- ranges are just shorthand for writing all the elements out. ints = [1..3] chars2 = ['a'..'c'] -- same as "chars" You can use ghci to print these lists out if you'd like. chars is a bit of a surprise: > chars "abc" In Haskell, a string is just a list of characters, and double quotes are just a shorthand way of writing the list out. You can confirm this by looking at the types of some of these: > :t "test" "test" :: [Char] > :t strings strings :: [[Char]] strings is a list of lists of characters. This also means that all normal list operations will also work on strings. For example, ##length## gives the ##length## of a list and ##(++)## is the list concatenation function: > length ints 3 > length ['a'..'z'] -- how many letters in the alphabet? 26 > ints ++ ints [1,2,3,1,2,3] > "hello" ++ "there" "hellothere" What is the type of ##length##? > :t length length :: [a] -> Int The "a" here in the type is like the "t" we saw in type classes, but without a constraint. This means that for any type a (sometimes read as "alpha"), ##length## takes a list of a and produces an integer. The length of a list is independent of what the list actually contains. This is called //(parametric) polymorphism//: ##length## works for lists of any type. Exercises: 1) What is the type of [], the empty list? 2) For each of the following, is it a valid list? If so, what is its type? a) [myint, sqrt 2] b) [[myint], []] c) []:[] d) [(+), (-)] 3) For each of the following functions, what is its type? You can check your answers using ":t". a) head (given a list, gives you the first element in the list) b) tail (given a list, gives you everything but the first element in the list) c) concat (given a list of lists, gives you the result of concatenating the lists together) Answers: 1) The empty list can be a list of anything, so it has type [a]. 2) a) No. ##myint## is an Integer, while the result of ##sqrt## is fractional. b) [[Integer]] c) [[a]] d) This is a list of functions, and they all have the same type. So the list has type (Num a) => [a -> a -> a]. 3) a) [a] -> a b) [a] -> [a] c) [[a]] -> [a] = XXX BELOW HERE THINGS GET UGLY AND NEED EDITING === More on lists XXX fit me in elsewhere A Haskell list is similar to a Lisp list or a linked list in other languages. A list is recursively defined as either empty, [], or as a value followed a list. The "followed by" operator is a colon, and its right side must be a list of whatever is on the left side. > 1:[] [1] > 1:2:3:[4,5,6] [1,2,3,4,5,6] Again, using 'a':'b':[], ['a','b'], and "ab" are equivalent, though the last one is easiest to read. ones = 1:ones -- this will be explained below Going back to the test program, you'll see that the definition of ones is a 1 followed by... itself. The list goes on forever! If you tell ghci to print it it'll go on forever until you hit ctl+c. 2) What is the type of (:), the list construction operator? 2) It takes something on the left and a list of somethings on the right, so it has type a -> [a]. 4) mystery x = x : (mystery x) a) What does this function do? b) What is its type? 4) This function is actually a builtin, called "repeat". a) It creates an infinite repeating list of out of any value. b) a -> [a] === More Functions and Pattern Matching Let's define an "isEven" test that returns a Bool (whose two values are True and False -- capitalization here seems weird but there's a good reason for it that will become apparent later). isEven :: Int -> Bool isEven x = if x == 0 then True else not (isEven (x-1)) Here's a better definition: isEven 0 = True isEven 1 = False -- not necessary, but it serves as an example isEven x = not (isEven (x-1)) Note here how we define isEven in multiple lines, by breaking the cases out. This sometimes make code clearer than having a chain of if-then tests, and is quite common in Haskell code. Of course, there's also a "mod" function that could be used to compute this directly: isEven x = (x `mod` 2) == 0 Now let's define our own length function, called "len". In Haskell, as in many functional languages, you use recursion for everything. (This requirement is even stronger in Haskell -- since you can't alter definitions, there would be nothing to do inside the body of a hypothetical "for" loop. There really is no looping construct in the language.) Thinking recursively quickly requires practice at first and then seems really natural once you've got it. For len, you define its type: len :: [a] -> Int a base case: len [] = 0 and the induction step: len (x:xs) = len xs + 1 (If you put those three lines together your test file, you'll see that the equals signs line up. This is just a style thing.) The new thing here is the way we wrote the function arguments, where we gave names to pieces of the input. This is called //pattern matching//: the last line here works for inputs that are lists of at least one element, and they call that element "x" and whatever's left of the rest "xs". Since all lists follow one of the two patterns we've defined, len is now defined for all lists. In fact, if your patterns don't cover all inputs and you run ghc with the -Wall flag, you'll get a warning: Warning: Pattern match(es) are non-exhaustive In the definition of `foo': Patterns not matched: _ : _ Here's a function that counts the number of times a certain element is in a list. Since we test whether our "a"s are equal, we must constain them with the "Eq" type class, which covers all types where equality is meaningful. countFind :: (Eq a) => a -> [a] -> Int countFind target [] = 0 countFind target (x:xs) = if x == target then 1 + (countFind target xs) else countFind target xs (A brief word here on syntax: Haskell is defined in terms of semicolons and curly braces, but if you indent your code properly they are optional. You don't even need to learn the rules for //layout//, as it's called, as long as you follow good style. In general, if you're continuing a line, just indent a bit more and you're fine.) Note that an "if" must always have an "else" branch, as the whole thing (if ... then ... else) must always evaluate to a single value and type. It's a pain to keep passing that "target" name around, since it's always the same value. Here's another way of writing that function: countFind target list = countUp list where countUp [] = 0 countUp (x:xs) = if x == target then 1 + (countUp xs) else countUp xs Note the new keyword "where" at the end of the first line. The where keyword lets you introduce new definitions within an expression. Since target is in scope inside the body of countFind, countUp doesn't take it as a parameter. Also note that we didn't write out a type for countUp. We could, but its type is obvious from its context and the compiler can figure it out anyway. Let's redefine the hypotenuse function using where excessively: hyp a b = sqrt (a2 + b2) where a2 = square a b2 = square b square x = x**2 Here, we've defined both plain values (a2, b2) and a function. Exercises: 1) Write a function that counts how many times the substring "ab" occurs within a string. (Hint: this section was about the pattern matching.) Answers: 1) abCount :: String -> Int abCount [] = 0 abCount ('a':'b':xs) = 1 + abCount xs abCount (x:xs) = abCount xs # vim: set ts=2 sw=2 tw=72 et :