by Bayle Shanks
some code samples are from __Yet Another Haskell Tutorial__ which is by Hal Daume III
This (currently unfinished) tutorial aims to be short yet to cover topics through basic monads.
Much of the bulk of the text consists of short code examples and interpreter traces.
The intended reader has programming experience, but only in imperative languages, and wants a quick introductory tour of Haskell. The tutorial is not intended to be difficult, however in the interest of brevity many things are demonstrated without much explanation.
It does not aim to be comprehensive, so after reading it, interested parties may want to read a longer tutorial to get the details that are skipped here. Hopefully, after reading this tutorial, you will be able to breeze through a longer one.
Copyright 2008 Bayle Shanks. You may copy this document under the terms of either:
whichever you prefer (but not in any other circumstances without prior permission).
(ignore this if you are reading the PDF). I'm using EasyLatex? to compile the wiki source of this page into a PDF. So don't mind the occasional LaTeX? commands. For example:
\usepackage{times} \usepackage[margin=3.7cm]{geometry}
\newpage
As of this writing, the most popular implementation of Haskell seems to be ghc, the Glasgow Haskell Compiler. The GHC project also produces ghci, an interpreter. In this tutorial, we'll use ghci (although of course most of the things that we'll talk about are general features of the language that will be in any implementation). So go install ghci now.
When you start ghci, you get an intro banner and then a prompt:
Prelude>
To quit, type Cntl-D.
Start ghci again. You can enter Haskell directly in the interpreter, for example:
Prelude> print "Hello World" "Hello World" Prelude>
If you want to put this in a file, here's how. Each file is a module. The module name should be the same as the file name, except that the file name should have an .hs at the end and the module name should not.
So, create a file called Test.hs and put this into it:
module Test where helloWorldFunction = print "Hello world"
To load a module in ghci, you say ":l modulename". So, to load module Test and then evaluate helloWorldFunction, you do:
Prelude> :l Test [1 of 1] Compiling Test ( Test.hs, interpreted ) Ok, modules loaded: Test. *Test> helloWorldFunction "Hello world" *Test>
Note that prompt changes from "Prelude" to "*Test". If you later change the sourcecode file and want to reload it, you can use :r, which reloads the current module.
In the previous example, the reason that there are quotes around "Hello world" is that the "print" command prints a representation of a value that is useful for debugging. If you just have a string that you want to send to the console directly, you use putStrLn. For example:
Prelude> print "Hello world" "Hello world" Prelude> putStrLn "Hello world" Hello world Prelude>
If you want to compile a standalone executable, you need to make a Main module. Put the following into Main.hs:
module Main where main = putStrLn "Hello world"
Now, at your operating system's command line, use ghc to compile:
$ ghc --make Main.hs -o main $ ./main Hello world
\newpage
In addition,
In Haskell, every expression has a type2. Various functions and operators have various restrictions on what sort of types they can operate on. If you try to use incompatible types, you'll get a compile time error.
If you are curious about the type of some expression in Haskell, you can use ghci's :t command to find out. For example:
Prelude> :t 'c' 'c' :: Char Prelude> :t "a string" "a string" :: [Char]
Later on we'll talk about how to read the notation for expressing the types of things in Haskell. Until then, don't worry about it.
At this point you might be worried that you'll spend a lot of time declaring the types of things, so I should mention that Haskell doesn't usually require you to declare the types of your variables and functions. Haskell has a powerful system of type inference that guesses the types of almost everything automatically.
A disadvantage of using a powerful type inference system is that it makes type error messages harder to interpret. For example, let's say that you have three expressions, call them A,B,C. Let's say that you give expression A the wrong type. Let's say that you construct expression B out of expression A, and then in a very different part of the program, you refer to expression B in expression C. Because you made a mistake with expression A, Haskell might infer the wrong type for expressions B and C. Perhaps the error will only surface when it gets to expression C. In this case, the error message will refer only to expression C, and you'll have to figure out that the root of the problem was really with expression A.
To define a function in a source code file, write something like:
functionName argument1 argument2 = expression
For example:
plusFive a = a+5
Inside ghci, you have to put the word "let" at the beginning of the function definition:
Prelude> let plusFive a = a+5 Prelude> plusFive(10) 15
Functions you define are prefix by default. We'll talk about how to define infix functions later.
To call a prefix function, just write the function, and then a space, and then the argument. If there are multiple arguments, just separate them by spaces. For example:
Prelude> let addition a b = a+b Prelude> addition 2 3 5
In Haskell, if you want to execute a sequence of instructions, you can't just put them one after another, unless they are within a do. For example, try putting this incorrect code into Test.hs:
module Test where
test = print "First line."
print "Second line."
It won't compile in ghci (don't worry about trying to understand this error message3):
Prelude> :l Test
[1 of 1] Compiling Test ( Test.hs, interpreted )
Test.hs:2:7:
Couldn't match expected type `(a -> IO ()) -> [Char] -> t'
against inferred type `IO ()'
In the expression: print "First line." print "Second line."
In the definition of `test':
test = print "First line." print "Second line."
Failed, modules loaded: none.
The problem is that in Haskell, a function is just __a single expression__. So in a sense, a whole Haskell function is analogous to just a single line of code in other languages.
To execute a sequence of instructions, you have to wrap them in a do block. Example in ghci:
Prelude> do {putStrLn "First line."; putStrLn "Second line."}
First line.
Second line.
Example as a source code file (to be placed in Test.hs):
module Test where
test = do {putStrLn "First line."; putStrLn "Second line."}
A complete do block is itself a single expression that returns a single value (which is why you can have a whole do block inside a function, even though functions must be single expressions).
Under the hood, do blocks are actually syntactic sugar for something pretty complicated involving monads. We'll talk more about the deeper meaning of do blocks later. For now, one more thing to note is that a do block that contains I/O has a return value of type 'IO something' (where the 'something' varies).
You don't have mutable variables in Haskell.
Put the following incorrect code into Test.hs:
module Test where x = 1 x = x+1
Now try loading it in ghci:
Prelude> :l Test
[1 of 1] Compiling Test ( Test.hs, interpreted )
Test.hs:3:0:
Multiple declarations of `Test.x'
Declared at: Test.hs:2:0
Test.hs:3:0
Failed, modules loaded: none.
To accomplish the same effect, you have to have a different name for the so-called variable at each stage. Put this into Test.hs:
module Test where x = 1 x2 = x+1
Now it works:
Prelude> :l Test [1 of 1] Compiling Test ( Test.hs, interpreted ) Ok, modules loaded: Test. *Test> x2 2
You can see that what you are really doing here is not declaring "variables", but defining functions, just like the "addition" function in the example above. There are still variables in Haskell, in the form of function arguments; but these are just like the variables found in mathematics -- their value doesn't change over the course of the computation.
However, for me, the word "variable" in the context of a programming language is so bound up the idea of mutable variables that I like to tell myself this: in Haskell, there are no "variables", because nothing "varies"; there are just functions, each with a fixed4 definition.
I'll say that again. In the above code, x and x2 do not correspond to memory locations which you can read and write. x and x2 are names of functions that you have defined.
Now, you're thinking, how are we going to program without mutable variables? Well, as far as I know (todo), there are 3 ways in which variables are used in typical imperative programming languages:
I'll treat each case in turn.
In the first case, these variables don't really have to be mutable -- you could always5 give the variable a new name on each line of code that assigns to it6.
An example of the first case, in Python:
import time
toOutput = "The time is "
toOutput = toOutput + time.strftime('%H:%M')
toOutput = toOutput + " right now"
print toOutput
In this situation, toOutput can be replaced by three different variables:
import time
toOutput1 = "The time is "
toOutput2 = toOutput + time.strftime('%H:%M')
toOutput3 = toOutput + " right now"
print toOutput3
The second case is function arguments. Function arguments aren't usually considered "mutable variables", but they do take on different values each time the function is called. Well, we do have function arguments in Haskell, so there's no problem here.
The third case are things that change as you progress through loop iterations. Well, that's easily taken care of. In Haskell, you're not allowed to loop. Feel better?
That's a half-truth. In Haskell, you replace loop constructs with recursion. Each iteration is replaced with a new call to the function. So, you can still go through the same section of code over and over again while incrementing the equivalent of a loop counter -- it's just that the loop counter is a function argument, rather than a local variable, and that each iteration is a separate function call. I'll give an example later.
By the way, you can put some nonalphanumeric characters into your function names. Some people like to use the apostrophe (for example, "x'" -- read "x prime") to indicate to the reader that a bunch of functions should be considered to form sequence. Example:
module Test where x = 1 x' = x+1
To reassign a name within a do block, use something like "let {x=1}". Example:
Prelude> do {let {s="Line 1"}; putStrLn s; let {s="Line 2"}; putStrLn s;}
Line 1
Line 2
However, this doesn't mean that we have mutable variables. The following code enters an infinite loop when Haskell tries to evaluate "x=x+1":
Prelude> do {let {x=1}; print x; let {x=x+1}; print x;}
You can also reassign things within the ghci interpreter using let. You might think of the things that you enter in the interpreter as being within an implicit do block7. Example:
Prelude> let x = 3 Prelude> x 3 Prelude> let x = 4 Prelude> x 4 Prelude> let f a b = a+b Prelude> f 2 3 5 Prelude> let f a b = a*b Prelude> f 2 3 6
You can either enclose blocks in curly brackets and delimit lines with semicolons, or you can use whitespace. If you use whitespace, just indent things that you would have put into curly brackets.
In the ghci interpreter, you cannot use whitespace, you must use only "{}" and ";". You can only use whitespace in source code files.
Here is an example using "{}" and ";":
module Test where
test = do {putStrLn "First line."; putStrLn "Second line."}
And here is the equivalent using whitespace:
module Test where test = do putStrLn "First line." putStrLn "Second line."
The rules for interpretation of whitespace in Haskell are sometimes called the rules of "layout".
Generally for the rest of this tutorial when I present code samples, I'll use the whitespace form, as if it was sitting inside a source code file. Sometimes, however, when I want to show you what happens when you evaluate something, I'll show you a ghci trace and put in some commands in the form that uses {} and ;.
Examples:
x = 3 -- this is a single line comment
{- this is a
multiline
comment -}
To import a module in a source code file, use the import statement. For example, to import module Random:
import Random
To import a module within the ghci interpreter, use the :m command. For example, to import module Random:
Prelude> :m Random Prelude Random>
You can set breakpoints within do blocks by using the ghci along with the breakpoint command, which is in the GHC.Base module:
module Test where
import GHC.Base
main = do let a = 3
let b = a*2
breakpoint $ do
print a+b
If you are using ghci 6.8.1 or later, you can use the more powerful debugging facilities included in ghci. See http://haskell.org/ghc/docs/6.8.1/html/users_guide/ghci-debugger.html for details.
True and False are the result of boolean expressions.
Another special value in Haskell is the "unit" value, written:
()
This is sometimes used to mean something like what is called "null" in other languages (in Haskell, null is used for something else; it's a boolean function that tells you if a list is empty).
By the way, sometimes you run into trouble because you expect the negation function ('-') to bind very tightly to numbers, but it doesn't. Just use parenthesis. Example:
Prelude> abs 3
3
Prelude> abs -3
<interactive>:1:5:
No instance for (Num (a -> a))
arising from the literal `3' at <interactive>:1:5
Possible fix: add an instance declaration for (Num (a -> a))
In the second argument of `(-)', namely `3'
In the expression: abs - 3
In the definition of `it': it = abs - 3
Prelude> abs (-3)
3
When we tried to do abs -3, what happened was that it was parsed as (abs -) 3. Oh well.
Example8