First, let's install GHC, the Haskell implementation that currently doesn't really have any competition for serious development. You'll probably want the new-style Windows installer. The installation should be straightforward, but there's a little snag that I find quite annoying that has to be removed once the installation is complete:
Go to the properties of the shortcut for GHCi and add the flag "-fno-monomorphism-restriction" to the command (i.e. there's a text line input box that looks like "c:\whatever\the\path\to\ghc\is\ghci.exe", change the end to "ghci.exe -fno-monomorphism-restriction"). It'll be a while before I can explain the rather technical reason behind the restriction, but since it basically trades programmer convenience for predictability of performance, and we care a lot more about convenience than porformance at this point, we'll turn it off.
Now that you've got a working Haskell implementation, a few things will change. One, you will never have to evaluate an expression by hand again - GHC will do it for you. Two, we won't be using the <> shorthand from lesson 1 anymore - since an expression is equal to its value, if I want to say that some expression evaluates to something, I'll just use the equality operator (==). Three, when I mention operators (such as the equality operator there, or plus or minus, etc.), I will write them in parentheses, since that is (for a reason that we'll learn later) the way it's done by Haskell users.
Start up ghci. Every expression we've used so far will work in it, except that when you define a name in ghci, you need to prefix the line with "let". Here's an example ghci session doing a few of the things we did in lesson 1. The lines beginning with Prelude> are stuff written by me (except for the "Prelude>" itself):
Code: Select all
___ ___ _
/ _ \ /\ /\/ __(_)
/ /_\// /_/ / / | | GHC Interactive, version 6.6, for Haskell 98.
/ /_\\/ __ / /___| | http://www.haskell.org/ghc/
\____/\/ /_/\____/|_| Type :? for help.
Loading package base ... linking ... done.
Prelude> (\x -> x + 6) 5
11
Prelude> let average x y = (x + y) / 2
Prelude> average 2 4 == 3
True
Prelude> let seven = 7
Prelude> let max x y = if x > y then x else y
Prelude> average (max seven 10) 0
5.0
Type, you ask? Every Haskell expression has a type. The type tells Haskell what it is and what it isn't allowed to do with that expression. For instance, you can switch the truth value of a Bool around with the function not: True becomes False and False becomes True. You do not need to figure out the types of your expressions yourself - Haskell will infer them for you.
The most important types for now are:
- Char, the type of single characters of text
- String, aka [Char], the type of lists (sequences) of characters, i.e. pieces of text. In general, a type inside square brackets means "a list of things of this type".
- Integer, the type of whole numbers
- Bool, the type of truth values
Functions have types, too. For instance, the type of not - the function that flips truth values around - is Bool -> Bool. This means "give me a Bool and I will evaluate to a Bool". The type of gcd is slightly more general than what we can deal with so far, but a good approximation is Integer -> Integer -> Integer. This means "give me two Integers and I will evaluate to an Integer". length :: [a] -> Int means "length wants you to give it a list of things so it can evaluate to an int" - the meaning of the "a" (standing for "something") here will be explained later.
Since Haskell knows the type of each expression, and what can be done with each type, it can tell you if you try to do impossible things by mistake. For instance, try flipping around the truth value of a string (a piece of text), though, and this is what you get:
Code: Select all
Prelude> not "hello"
<interactive>:1:4:
Couldn't match expected type `Bool' against inferred type `[Char]'
In the first argument of `not', namely `"hello"'
In the expression: not "hello"
In the definition of `it': it = not "hello"
Another type error that you'll commonly come across involves typeclasses, which will be elaborated on later. Here's an example of an error involving them, though:
Code: Select all
Prelude> not 5
<interactive>:1:4:
No instance for (Num Bool)
arising from the literal `5' at <interactive>:1:4
Possible fix: add an instance declaration for (Num Bool)
In the first argument of `not', namely `5'
In the expression: not 5
In the definition of `it': it = not 5
Exercises:
Ask GHCi about the types of various expressions. Here's a couple of example expressions to try:
"hello"
("hello, " ++)
"hello, " ++ "world"
(++)
1
1 + 2
1 + 2.5
(+)
A lot of the types have features that will look strange for now. This is because of Haskell's polymorphism features which help you get a lot more work done with far fewer functions. They will be handled later.
Play around with the various functions in the Haskell Prelude - the reason why it says "Prelude" on the command line is that you have access to the Prelude functions. Try ones like replicate, length, (++), (:), minimum and maximum to play with lists, for instance. The full list of names defined in the Prelude can be found at http://www.haskell.org/onlinereport/sta ... elude.html or by pressing tab twice at an empty GHCi prompt. Try to use types to help you match up useful expressions - Haskell programmers often say that the type system alone is strong enough that a randomly created expression might be useful if it type-checks.
Try functions like map, foldr and filter, too. They are some of the most commonly used functions in Haskell, but since they're all higher-order functions I'm going to defer explaining them in-depth to the next lesson. Here's a couple of examples of what they do, though:
Code: Select all
map (\x -> x + 1) [1,2,3] == [2,3,4] -- map takes a function and a list as its arguments, and applies the function to each value in the list
filter (\x -> x > 0) [1, -5] == [1] -- filter takes a predicate (a function that evaluates to a truth value) and a list as its arguments and throws out all of the values that don't match the predicate (make it evaluate to True when given as an argument to it)
foldr (+) 0 (map (+ 1) [1,2,3]) == foldr (+) 0 [2,3,4] == 9 -- the full meaning of this line will be explained later...
Lessons will be posted at a slightly slower pace from now on, depending on how well people seem to be taking them in, so as to avoid overloading people and making them think they're falling behind. If at any part you feel I'm going too slowly, you can just go http://haskell.org/haskellwiki/Books_and_tutorials and try out the many tutorials over there.
Thought of the day: Yes, these lessons are rather concise. That's because I work to keep them short: If I were to just let go and write out everything I thought interesting, these lessons would definitely be a lot lengthier - and a lot harder to understand, too, since I would constantly be going off on irrelevant tangents. Besides, Haskell is a concise language, so by symmetry, lessons about it should be concise.