https://wiki.haskell.org/api.php?action=feedcontributions&user=Dominicprior&feedformat=atomHaskellWiki - User contributions [en]2024-03-19T09:23:33ZUser contributionsMediaWiki 1.35.5https://wiki.haskell.org/index.php?title=Tutorials/Programming_Haskell/Introduction&diff=53951Tutorials/Programming Haskell/Introduction2012-09-21T16:19:20Z<p>Dominicprior: Fixed some indentation. Previously, the indentation was preventing the cope snippets from compiling.</p>
<hr />
<div>It's about time we got some job done in [[Haskell]], eh? Now, one of my<br />
favourite programming books as an undergraduate was the Camel Book,<br />
"Programming Perl". It was full of lots of practical examples of Perl<br />
code, written well. (And I'm grateful to Larry Wall, Tom Christiansen<br />
and Randal Schwartz for writing the book that made programming fun).<br />
<br />
So what would it look like if we wrote a Haskell tutorial in this style?<br />
Let's have at it!<br />
<br />
== Getting started ==<br />
<br />
Like some languages Haskell can be both compiled and interpreted. The<br />
most widely used implementation of Haskell currently is GHC, which<br />
provides both an optimising native code compiler, and an interactive<br />
bytecode interpreter. I'll be using [http://haskell.org/ghc GHC] <br />
(or its interactive front end, GHCi, for all code. So grab a copy of GHC<br />
now, from your package system, or the [http://haskell.org/ghc GHC home page].<br />
<br />
Start up GHCi:<br />
<br />
$ ghci<br />
___ ___ _<br />
/ _ \ /\ /\/ __(_)<br />
/ /_\// /_/ / / | | GHC Interactive, version 6.6, for Haskell 98.<br />
/ /_\\/ __ / /___| | http://www.haskell.org/ghc/<br />
\____/\/ /_/\____/|_| Type :? for help.<br />
<br />
Loading package base ... linking ... done.<br />
Prelude><br />
<br />
The interpreter now waits for your code fragments. The "Prelude" prompt<br />
indicates which library modules are in scope, and in this case, only the basic<br />
language module, known as <br />
[http://www.cse.unsw.edu.au/~dons/data/Prelude.html the Prelude].<br />
<br />
Now we can start running Haskell code.<br />
<br />
<haskell><br />
Prelude> "G'day, world!"<br />
"G'day, world!"<br />
<br />
Prelude> putStrLn "G'day, world!"<br />
G'day, world!<br />
</haskell><br />
<br />
You can compile this code to a native binary using GHC, by writing in a source file:<br />
<br />
<haskell><br />
main = putStrLn "G'day, world!"<br />
</haskell><br />
<br />
and then compiling the source to native code. Assuming your file is A.hs:<br />
<br />
$ ghc A.hs<br />
<br />
This produces a new executable, ./a.out (a.out.exe on windows), which you can<br />
run like any other program on your system:<br />
<br />
$ ./a.out<br />
G'day, world!<br />
<br />
==Variables==<br />
<br />
We can name arbitrary fragments of Haskell using variables. Like so:<br />
<br />
<haskell><br />
phrase = "G'day, world!"<br />
main = putStrLn phrase<br />
</haskell><br />
<br />
We don't have to define what type phrase is, as Haskell uses type inference to<br />
infer at compile time the types of all expressions in the program. As "G'day,<br />
world!" is a string, so must phrase be a string. There are a bunch of basic<br />
types of values to play with. Here's a small sample:<br />
<br />
<haskell><br />
answer = 42<br />
pi = 3.141592653589793238462643383279502884197169399375105820974944592<br />
avocados = 6.02e23<br />
pet = "Lambda"<br />
sign = "I love my " ++ pet<br />
coat = "It costs $100"<br />
hence = "whence"<br />
thence = hence<br />
moles = 2.5<br />
x = moles * avocados<br />
c = '#'<br />
pair = (2.5, "lambdas")<br />
list = [5,6,4,3,1]<br />
options = Just "done"<br />
failed = Nothing<br />
void = ()<br />
</haskell><br />
<br />
One important thing to remember is that Haskell's variables, like in most<br />
functional programming languages, are like variables in mathematics, and are<br />
just names for expressions. They're explicitly not [http://www.haskell.org/pipermail/haskell-cafe/2006-December/020147.html mutable boxes], <br />
like in most imperative programming languages. As a result, you never need to<br />
worry about initialising a Haskell variable, nor do you need to worry about the<br />
current value <i>in</i> a variable: it always has the same value, and can<br />
always be replaced with its definition. So the following behaves<br />
just like it would in maths:<br />
<br />
<haskell><br />
answer = 42<br />
another = answer + 1<br />
more = another + answer<br />
main = print more<br />
</haskell><br />
<br />
That is,<br />
<br />
$ ghc A.hs<br />
$ ./a.out<br />
85<br />
<br />
Now, since variables are just names for program fragments, you can evaluate<br />
Haskell on paper by replacing all names with their definition, until you reach<br />
a final value, like so:<br />
<br />
<haskell><br />
main = print more<br />
=><br />
main = print (another + answer)<br />
=><br />
main = print ((answer + 1) + answer)<br />
=><br />
main = print ((answer + 1) + 42)<br />
=><br />
main = print ((42 + 1) + 42)<br />
=><br />
main = print (43 + 42)<br />
=><br />
main = print 85<br />
=><br />
85<br />
</haskell><br />
<br />
Having such a simple system for variables allows for a wide range of<br />
interesting optimisations, and makes understanding what a program is doing at<br />
any point much easier, since you don't have to worry about what state a<br />
variable might currently be in. (Of course, some problems need (threadsafe) <br />
[http://haskell.org/ghc/docs/latest/html/libraries/base/Control-Concurrent-MVar.html mutable boxes], and they're available as a library for when you need that).<br />
<br />
==Collections==<br />
<br />
Often you need to collect a bunch of values together into some kind of<br />
collection. Haskell has many many collection types, but in particular, it has<br />
lists and finite maps, which operate much like arrays and hashes of the<br />
imperative world.<br />
<br />
===Lists===<br />
<br />
A list is just an ordered, um, list of values. They can be nested, and<br />
transformed in all sorts of ways, using functions. Assuming your file, A.hs,<br />
contains:<br />
<br />
<haskell><br />
home = ["couch", "chair", "table", "stove"]<br />
</haskell><br />
<br />
We can play around with this stuff like so:<br />
<br />
<haskell><br />
$ ghci A.hs<br />
<br />
*Main> home<br />
["couch","chair","table","stove"]<br />
<br />
*Main> head home<br />
"couch"<br />
<br />
*Main> tail home<br />
["chair","table","stove"]<br />
<br />
*Main> last home<br />
"stove"<br />
<br />
*Main> home !! 2<br />
"table"<br />
<br />
*Main> reverse home<br />
["stove","table","chair","couch"]<br />
<br />
*Main> map reverse home<br />
["hcuoc","riahc","elbat","evots"]<br />
</haskell><br />
<br />
Loading in the [http://haskell.org/ghc/docs/latest/html/libraries/base/Data-List.html List library] <br />
gives us some more functions to use:<br />
<br />
<haskell><br />
*Main> :m + Data.List<br />
<br />
*Main Data.List> intersperse "#" home<br />
["couch","#","chair","#","table","#","stove"]<br />
<br />
*Main Data.List> concat (intersperse "#" home)<br />
"couch#chair#table#stove"<br />
<br />
*Main Data.List> home \\ ["table","stove"]<br />
["couch","chair"]<br />
</haskell><br />
<br />
===Finite Maps===<br />
<br />
[http://haskell.org/ghc/docs/latest/html/libraries/base/Data-Map.html Finite maps]<br />
(or maps) are the lookup tables of purely functional programming. Whenever<br />
you'd use some kind of hash in an imperative language, you can replace it with<br />
a Map in Haskell.<br />
<br />
Like hashes, maps can be seen as a table of pairs of keys and values. You can<br />
declare a new map:<br />
<br />
<haskell><br />
import Data.Map<br />
<br />
days = fromList<br />
[ ("Sun", "Sunday" )<br />
, ("Mon", "Monday" )<br />
, ("Tue", "Tuesday" )<br />
, ("Wed", "Wednesday" )<br />
, ("Thu", "Thursday" )<br />
, ("Fri", "Friday" )<br />
, ("Sat", "Saturday" ) ]<br />
</haskell><br />
<br />
You can also convert a map to a list, using (well, duh!) toList:<br />
<br />
<haskell><br />
*Main> toList days<br />
[("Fri","Friday"),("Mon","Monday"),("Sat","Saturday")<br />
,("Sun","Sunday"),("Thu","Thursday"),("Tue","Tuesday")<br />
,("Wed","Wednesday")]<br />
</haskell><br />
<br />
Note that they come out unordered, just like in hashes. If you just want the keys of the map:<br />
<br />
<haskell><br />
*Main> keys days<br />
["Fri","Mon","Sat","Sun","Thu","Tue","Wed"]<br />
<br />
*Main> elems days<br />
["Friday","Monday","Saturday","Sunday","Thursday","Tuesday","Wednesday"]<br />
</haskell><br />
<br />
Since maps are a good structure for looking up values, you can search them using<br />
the lookup function. This function returns the element, if found: <br />
<br />
<haskell><br />
*Main> Data.Map.lookup "Tue" days<br />
"Tuesday"<br />
</haskell><br />
<br />
Since the name 'lookup' is also used by a list function of similar purpose in<br />
the Prelude, we use the <i>qualified</i> name here to disambiguate which<br />
'lookup' to use.<br />
<br />
===On failure===<br />
<br />
But what happens if the key is not found? (Feel free to skip this section if<br />
you don't care about errors yet) lookup will then fail, and how it fails<br />
depends on what kind of failure you want. Haskell goes to great lengths to make<br />
programming for failure flexible. For example, to fail with an exception:<br />
<br />
<haskell><br />
*Main> Data.Map.lookup "Thor" days<br />
*** Exception: user error (Data.Map.lookup: Key not found)<br />
</haskell><br />
<br />
Which is the same as failing with an IO error. We can specify this specifically<br />
with a type annotation, to say "fail with an IO error":<br />
<br />
<haskell><br />
*Main> Data.Map.lookup "Thor" days :: IO String<br />
*** Exception: user error (Data.Map.lookup: Key not found)<br />
</haskell><br />
<br />
Often you might instead prefer that some special value is returned on failure:<br />
<br />
<haskell><br />
*Main> Data.Map.lookup "Thors" days :: Maybe String<br />
Nothing<br />
</haskell><br />
<br />
Maybe you'd just like an empty list:<br />
<br />
<haskell><br />
*Main> Data.Map.lookup "Thor" days :: [String]<br />
[]<br />
</haskell><br />
<br />
Finally, you can always provide an explicit default value:<br />
<br />
<haskell><br />
*Main> findWithDefault "Not found" "Thor" days<br />
"Not found"<br />
</haskell><br />
<br />
Failure is entirely under your control! <br />
<br />
==Actions==<br />
<br />
Now, real programs interact with the outside world. They call functions which<br />
do IO, as a side effect, and then return some value. In Haskell, functions with<br />
side effects are often called <i>actions</i>, to distinguish them from normal<br />
Haskell functions (which behave like mathematical functions: they take inputs<br />
and return a result, with no side effects). Programming with side effects is<br />
carefully handled in Haskell, again to control the possibility of errors, and<br />
all functions which have side effects have a special type: the IO type. <br />
<br />
For example, the function to print a string has the following type (and you can<br />
ask the interpreter for the type interactively):<br />
<br />
<haskell><br />
Prelude> :t putStr<br />
putStr :: String -> IO ()<br />
</haskell><br />
<br />
which tells you that this function takes a String as an argument, does some IO<br />
side effect, and returns the null value. It is equivalent to the following C<br />
type:<br />
<br />
void putStr(char *);<br />
<br />
but with a bit of extra information, namely, that the function does some IO.<br />
We would print out some element of our map like so:<br />
<br />
<haskell><br />
main = print ("Tue in long form is " ++ findWithDefault "Not found" "Tue" days)<br />
<br />
*Main> main<br />
"Tue in long form is Tuesday"<br />
</haskell><br />
<br />
==An example==<br />
<br />
One of the classic programming puzzles for introducing real world problems is<br />
the 'class grade' problem. You have a text file containing a list of student<br />
names and their grades, and you'd like to extract various information and<br />
display it. In deference to The Camel Book, we'll follow this lead, and start<br />
with a file "grades", containing something like this:<br />
<br />
Alonzo 70<br />
Simon 94<br />
Henk 79<br />
Eugenio 69<br />
Bob 80<br />
Oleg 77<br />
Philip 73<br />
...<br />
<br />
Student's appear multiple times, with entries for each of their subjects Let's<br />
read this file, populate a map with the data, and print some statistical<br />
information about the results. First thing to do is import some basic libraries:<br />
<br />
<haskell><br />
import Data.Char<br />
import Data.Maybe<br />
import Data.List<br />
import Data.Map hiding (map)<br />
import Text.Printf<br />
</haskell><br />
<br />
And now here's the entire program, to read the grades file, compute all the<br />
averages, and print them:<br />
<br />
<haskell><br />
main = do<br />
src <- readFile "grades"<br />
let pairs = map (split.words) (lines src)<br />
let grades = foldr insert empty pairs<br />
mapM_ (draw grades) (sort (keys grades))<br />
where<br />
insert (s, g) = insertWith (++) s [g]<br />
split [name,mark] = (name, read mark)<br />
<br />
draw g s = printf "%s\t%s\tAverage: %f\n" s (show marks) avg<br />
where<br />
marks = findWithDefault (error "No such student") s g<br />
avg = sum marks / fromIntegral (length marks) :: Double<br />
</haskell><br />
<br />
==Running it==<br />
<br />
How do we run this program? There's lots of ways:<br />
<br />
===Compile it to native code===<br />
<br />
$ ghc -O Grades.hs<br />
<br />
$ ./a.out<br />
Alonzo [70.0,71.0] Average: 70.5<br />
Bob [80.0,88.0] Average: 84.0<br />
Eugenio [69.0,98.0] Average: 83.5<br />
Henk [79.0,81.0] Average: 80.0<br />
Oleg [77.0,68.0] Average: 72.5<br />
Philip [73.0,71.0] Average: 72.0<br />
Simon [94.0,83.0] Average: 88.5<br />
<br />
===Run it in the bytecode interpreter===<br />
<br />
$ runhaskell Grades.hs<br />
Alonzo [70.0,71.0] Average: 70.5<br />
Bob [80.0,88.0] Average: 84.0<br />
Eugenio [69.0,98.0] Average: 83.5<br />
Henk [79.0,81.0] Average: 80.0<br />
Oleg [77.0,68.0] Average: 72.5<br />
Philip [73.0,71.0] Average: 72.0<br />
Simon [94.0,83.0] Average: 88.5<br />
<br />
===Execute it interactively===<br />
<br />
$ ghci Grades.hs<br />
Prelude Main> main<br />
Alonzo [70.0,71.0] Average: 70.5<br />
Bob [80.0,88.0] Average: 84.0<br />
Eugenio [69.0,98.0] Average: 83.5<br />
Henk [79.0,81.0] Average: 80.0<br />
Oleg [77.0,68.0] Average: 72.5<br />
Philip [73.0,71.0] Average: 72.0<br />
Simon [94.0,83.0] Average: 88.5<br />
<br />
===Make the script executable===<br />
<br />
Under unix, you can use the #! convention to make a script executable. Add the<br />
following to the top of the source file:<br />
<br />
#!/usr/bin/env runhaskell<br />
<br />
And then set the script executable:<br />
<br />
$ chmod +x Grades.hs<br />
<br />
$ ./Grades.hs<br />
Alonzo [70.0,71.0] Average: 70.5<br />
Bob [80.0,88.0] Average: 84.0<br />
Eugenio [69.0,98.0] Average: 83.5<br />
Henk [79.0,81.0] Average: 80.0<br />
Oleg [77.0,68.0] Average: 72.5<br />
Philip [73.0,71.0] Average: 72.0<br />
Simon [94.0,83.0] Average: 88.5<br />
<br />
===Next week=== <br />
<br />
More IO!<br />
<br />
[[Category:Tutorials]]</div>Dominicprior