----- isHidden: false menupriority: 1 kind: article created_at: 2012-02-08T15:17:53+02:00 title: Haskell the Hard Way subtitle: Haskell will blow your mind author_name: Yann Esposito author_uri: yannesposito.com tags: - Haskell - programming ----- <%= blogimage("main.png","Title image") %> begindiv(intro) <%= tldr %> enddiv
> runhaskell filename.lhs >> > You should see a link just below the line. enddiv
main = print "Hello World!"
~ runhaskell ./hello.hs
Hello World!
You could also download the literate haskell source.
You should see a link just above the introduction title.
Download this file as `00_hello_world.lhs` and:
~ runhaskell 00_hello_world.lhs
Hello World!
main = do
print "What is your name?"
name <- getLine
print ("Hello " ++ name ++ "!")
# Python
print "What is your name?"
name = raw_input()
print "Hello %s!" % name
# Ruby
puts "What is your name?"
name = gets.chomp
puts "Hello #{name}!"
// In C
#include
int main (int argc, char **argv) {
char name[666]; // <- An Evil Number!
// What if my name is more than 665 character long?
printf("What is your name?\n");
scanf("%s", name);
printf("Hello %s!\n", name);
return 0;
}
The structure is the same, but there are some syntax differences.
A major part of this tutorial will explain why.
In Haskell, there is a `main` function.
In Haskell every object has a type.
The type of `main` is `IO ()`.
This means, `main` will cause side effects.
`IO` is a ... .
Wait! No! I won't say it now!
I am afraid to terrify you.
You might run away crying.
For now, I won't talk about what `IO` really is.
Just remember, Haskell can look a lot like other imperative languages.
int f(int x, int y) {
return x*x + y*y;
}
In javascript:
function f(x,y) {
return x*x + y*y;
}
in Python:
def f(x,y) =
return x*x + y*y;
in Ruby:
def f(x,y)
x*x + y*y
end
In Scheme:
(define (f x y)
(+ (* x x) (* y y)))
Finaly, the Haskell way is:
f x y = x*x + y*y
Very clean. No parenthesis, no `def`.
Don't forget, Haskell is mainly built on function and types.
It is thus very easy to define functions and types.
The syntax was particularly well thought for these objects.
The common usage is to declare the type of your function.
This is not mandatory.
The compiler is smart enough to discover it for you.
f :: Int -> Int -> Int
f x y = x*x + y*y
Let's play a little.
f :: Int -> Int -> Int
f x y = x*x + y*y
main = print (f 2 3)
f :: Int -> Int -> Int
f x y = x*x + y*y
main = print (f 2.3 4.2)
f x y = x*x + y*y
main = print (f 2.3 4.2)
% ghci GHCi, version 7.0.3: http://www.haskell.org/ghc/ :? for help Loading package ghc-prim ... linking ... done. Loading package integer-gmp ... linking ... done. Loading package base ... linking ... done. Prelude> :load 22_very_basic.lhs [1 of 1] Compiling Main ( 22_very_basic.lhs, interpreted ) Ok, modules loaded: Main. *Main> :type f f :: Num a => a -> a -> aHey? What is this strange type? ~~~ Num a => a -> a -> a ~~~ First, `a` is a type variable. It means, that the first and the second argument will have the same type. And furthermore, the result will also be of the same type. The type variable `a` could take many different type value. For example `Int`, `Integer`, `Float`... So instead of having a forced type like in `C` with declaring the function for `int`, `long`, `float`, `double`, etc... We declare only one function like in a dynamic typed language. Generally, without the type class constraint, `a` can be any type. For example a `String`, an `Int`, but also more complex types, like `Trees`, other functions, etc... But here with have a `Num a => `. `Num` is a typeclass. It contains only type which behave like numbers. In fact, `Num` is class containing types who implement a specific list of functions, and in particular `(+)` and `(*)`. Typeclass is a very powerful language construction. We can do some incredibly powerful construction with this. More on this later. Finally, `Num a => a -> a -> a` means: Let `a` be a type belonging to the `Num` typeclass. This is a function from type `a` to (`a -> a`). Yes, strange, in Haskell no function have two argument. Instead all function have only one argument. In fact `f 3 4` is equivalent to `(f 3) 4`. Note `f 3` is a function: ~~~ f :: Num a :: a -> a -> a g :: Num a :: a -> a g = f 3 g y ⇔ 3*3 + y*y ~~~ Another notation exists for function. The lambda notation permit us to create function without assigning them a name. We call them anonymous function. We could have written: ~~~ g = \y -> 3*3 + y*y ~~~ If you are not used to functional programming your brain should start to heat up. It is time to make some real application.
f :: Num a => a -> a -> a
f x y = x*x + y*y
main = print (f 3 2.4)
f :: Num a => a -> a -> a
f x y = x*x + y*y
x :: Int
x = 3
y :: Float
y = 2.4
main = print (f x y)
square :: Num a => a -> a
square x = x^2
square' x = (^) x 2
square'' x = (^2) x
square''' = (^2)
abs x :: Num a => a -> a
abs = if x >= 0 then x else -x
abs' x
| x >= 0 = x
| otherwise = -x
function evenSum(list) {
var result = 0
for (i=0; i< length(list) ; i++) {
result += list[i];
}
return result;
}
But, in Haskell we don't have variables, nor for or while loop.
This is why we will use recursion[^0120101].
Here is a `C` version of the recursive function.
Note, for simplicity, I assume the int list should end with the first `null` value (`0`):
[^0120101]: Don't worry if you comme from imperative programming. Generally Haskell handles recursion efficiently.
int evenSum(int *list) {
return accumSum(0,list);
}
// In C I should have declared this
// function before evenSum, but
// I find it easier this way
int accumSum(int n, int *list) {
if (list == nil) { // if the list is empty
return n;
} else {
x = list[0]; // let x be the first element of the list
xs = list+1; // let xs be the list without its head
if ( 0 == (x%2) ) { // if x is even
return accumSum(n+x, xs);
} else {
return accumSum(n, xs);
}
}
}
Keep this code in mind. We will translate it in Haskell.
But before, I need to introduce three simple but useful function we will use:
even :: Integral a => a -> Bool
head :: [a] -> a
tail :: [a] -> [a]
-- Version 1
evenSum :: [Integer] -> Integer
evenSum l = accumSum 0 l
accumSum n l = if l == []
then n
else let x = head l
xs = tail l
in if even x
then accumSum (n+x) xs
else accumSum n xs
% ghci GHCi, version 7.0.3: http://www.haskell.org/ghc/ :? for help Loading package ghc-prim ... linking ... done. Loading package integer-gmp ... linking ... done. Loading package base ... linking ... done. Prelude> :l 11_Functions.lhs [1 of 1] Compiling Main ( 11_Functions.lhs, interpreted ) Ok, modules loaded: Main. *Main> evenSum [1..5] 6Here is an example of execution[^2]: [^2]: I know I cheat. But I will talk about non-strict later. ~~~ *Main> evenSum [1..5] accumSum 0 [1,2,3,4,5] 1 is odd accumSum 0 [2,3,4,5] 2 is even accumSum 2 [3,4,5] 3 is odd accumSum 2 [4,5] 4 is even accumSum 6 [5] 5 is odd accumSum 6 [] l == [] 6 ~~~ Comming from an imperative language all should seems right. In reality many things can be improved. First, we can generalize the type.
evenSum :: Integral a => [a] -> a
-- Version 2
evenSum :: Integral a => [a] -> a
evenSum l = accumSum 0 l
where accumSum n l =
if l == []
then n
else let x = head l
xs = tail l
in if even x
then accumSum (n+x) xs
else accumSum n xs
-- Version 3
evenSum l = accumSum 0 l
where
accumSum n [] = n
accumSum n (x:xs) =
if even x
then accumSum (n+x) xs
else accumSum n xs
foo [] =
foo l =
foo l = let x = head l
xs = tail l
in if even x
then foo (n+x) xs
else foo n xs
foo (x:xs) = if even x
then foo (n+x) xs
else foo n xs
-- Version 4
evenSum :: Integral a => [a] -> a
evenSum = accumSum 0
where
accumSum n [] = n
accumSum n (x:xs) =
if even x
then accumSum (n+x) xs
else accumSum n xs
import Data.List
filter :: (a -> Bool) -> [a] -> [a]
map :: (a -> b) -> [a] -> [b]
foldl' :: (a -> b -> a) -> a -> [b] -> a
-- Version 5
evenSum l = mysum 0 (filter even l)
where
mysum n [] = n
mysum n (x:xs) = mysum xs (n+x)
filter even [1..10] ⇔ [2,4,6,8,10]
myfunc list = foo initialValue list
foo accumulated [] = accumulated
foo tmpValue (x:xs) = foo (bar tmpValue x) xs
Which can be replaced by:
myfunc list = foldl bar initialValue list
-- Version 6
import Data.List
evenSum l = foldl' mysum 0 (filter even l)
where mysum acc value = acc + value
foldl f z [] = z
foldl f z (x:xs) = foldl f (f z x) xs
-- Version 7
evenSum l = foldl' (\x y -> x+y) (filter even l)
(\x y -> x+y) ⇔ (+)
-- Version 8
import Data.List
evenSum :: Integral a => [a] -> a
evenSum l = foldl' (+) 0 (filter even l)
(f . g . h) x ⇔ f ( g (h x))
-- Version 9
import Data.List
evenSum :: Integral a => [a] -> a
evenSum = (foldl' (+) 0) . (filter even)
-- Version 10
import Data.List
evenSum :: Integral a => [a] -> a
evenSum = sum . (filter even)
squareEvenSum = sum . (filter even) . (map (^2))
square x = x * x
square 2
4
square 2.1
4.41
:m Data.Complex
(2 :+ 1) * (2 :+ 1)
3.0 :+ 4.0
int int_square(int x) { return x*x; }
float fl_square(float x) {return x*x; }
complex complex_square (complex z) {
complex tmp;
tmp.real = z.real * z.real - z.img * z.img;
tmp.img = 2 * z.img * z.real;
}
For each type, you need to write a new function.
The only way to work around this problem is to use some meta-programming trick.
For example using the pre-processor.
In C++ there is a better way, the C++ templates:
class Number {
T value;
square() {
value = value*value;
}
}
Number i;
i.square;
Number f;
f.square;
class Complex {
int real;
int img;
Complex operator<*>(Complex z) {
Complex result;
result.real = real*z.real - img*z.img;
result.img = img*z.real + real*z.img;
return res;
}
}
Number z;
z.square
Even with C++ templates you are forced to write a line for each type.
To be fair, there is also a definition of the multiplication of Complex in Haskell.
But it takes only one line.
Somewhere in the source of the module `Data.Complex`:
instance Num (Complex a) where
...
(x:+y) * (x':+y') = (x*x'-y*y') :+ (x*y'+y*x')
...
type Name = String
type Color = String
showInfos :: Name -> Color -> String
showInfos name color = "Name: " ++ name
++ ", Color: " ++ color
name :: Name
name = "Robin"
color :: Color
color = "Blue"
main = putStrLn $ showInfos name color
putStrLn $ showInfos color name
In fact you can replace Name, Color and String everywhere.
The compiler will treat them as completely identical.
Another method is to create your own types using the keyword `data`.
data Name = NameConstr String
data Color = ColorConstr String
showInfos :: Name -> Color -> String
showInfos (NameConstr name) (ColorConstr color) =
"Name: " ++ name ++ ", Color: " ++ color
name = NameConstr "Robin"
color = ColorConstr "Blue"
main = putStrLn $ showInfos name color
NameConstr :: String -> Name
ColorConstr :: String -> Color
The syntax of `data` is generally:
data TypeName = ConstructorName [types]
| ConstructorName2 [types]
| ...
Generally the usage is to use the same name for the
DataTypeName and DataTypeConstructor.
Example:
data Complex = Num a => Complex a a
Also you can use the record syntax:
data DataTypeName = DataConstrctor {
field1 :: [type of field1]
, field2 :: [type of field2]
...
, fieldn :: [type of fieldn] }
And many accessor are made for you.
Furthermore you can use another order when setting values.
Example:
data Complex = Num a => Complex { real :: a, img :: a}
c = Complex 1.0 2.0
z = Complex { real = 3, img = 4 }
real c ⇒ 1.0
img z ⇒ 4
infixr 5 :::
data List a = Nil | a ::: (List a)
deriving (Show,Read,Eq,Ord)
convertList [] = Nil
convertList (x:xs) = x ::: convertList xs
main = do
print (0 ::: 1 ::: Nil)
print (convertList [0,1])
import Data.List
data BinTree a = Empty
| Node a (BinTree a) (BinTree a)
deriving (Show)
treeInsert :: (Ord a) => BinTree a -> a -> BinTree a
treeInsert Empty x = Node x Empty Empty
treeInsert (Node y left right) x
| x == y = (Node y left right)
| x < y = (Node y (treeInsert left x) right)
| otherwise = (Node y left (treeInsert right x))
main = print $ foldl' treeInsert Empty [7,2,4,8]
import Data.List
data BinTree a = Empty
| Node a (BinTree a) (BinTree a)
deriving (Eq,Ord)
instance Show (BinTree a) where
show t = ... -- You declare your function here
Here is my version on how to show a binary tree.
Don't worry about the apparent complexity.
I made a lot of improvement in order to display even strange objects.
-- declare BinTree a to be an instance of Show
instance (Show a) => Show (BinTree a) where
-- will start by a '<' before the root
-- and put a : a begining of line
show t = "< " ++ replace '\n' "\n: " (treeshow "" t)
where
treeshow pref Empty = ""
treeshow pref (Node x Empty Empty) =
(pshow pref x)
treeshow pref (Node x left Empty) =
(pshow pref x) ++ "\n" ++
(showSon pref "`--" " " left)
treeshow pref (Node x Empty right) =
(pshow pref x) ++ "\n" ++
(showSon pref "`--" " " right)
treeshow pref (Node x left right) =
(pshow pref x) ++ "\n" ++
(showSon pref "|--" "| " left) ++ "\n" ++
(showSon pref "`--" " " right)
-- show a tree using some prefixes to make it nice
showSon pref before next t =
pref ++ before ++ treeshow (pref ++ next) t
-- pshow replace "\n" by "\n"++pref
pshow pref x = replace '\n' ("\n"++pref) (show x)
-- replace on char by another string
replace c new string =
concatMap (change c new) string
where
change c new x
| x == c = new
| otherwise = x:[] -- "x"
treeInsert :: (Ord a) => BinTree a -> a -> BinTree a
treeInsert Empty x = Node x Empty Empty
treeInsert (Node y left right) x
| x == y = (Node y left right)
| x < y = (Node y (treeInsert left x) right)
| otherwise = (Node y left (treeInsert right x))
treeFromList list = foldl' treeInsert Empty list
main = do
putStrLn "Int binary tree:"
print $ treeFromList [7,2,4,8,1,3,6,21,12,23]
putStrLn "\nString binary tree:"
print $ treeFromList ["foo","bar","baz","gor","yog"]
putStrLn "\nBinary tree of Char binary trees:"
print ( treeFromList
(map treeFromList ["baz","zara","bar"]))
putStrLn "\nTree of Binary trees of Char binary trees:"
print $ treeFromList
(map treeFromList
[ map treeFromList ["Ia!","Ia!"]
, map treeFromList ["cthul","hu"]
, map treeFromList ["Fhtagn!"] ])
toList :: String -> [Integer]
toList input = read ("[" ++ input ++ "]")
main = do
putStrLn "Enter a list of numbers (separated by comma):"
input <- getLine
print $ sum (toList input)
import Data.Maybe
maybeRead :: Read a => String -> Maybe a
maybeRead s = case reads s of
[(x,"")] -> Just x
_ -> Nothing
data Maybe a = Nothing | Just a
This is a nice way to tell there was an error while trying to create/compute
a value.
The `maybeRead` function is a great example of this.
This is a function similar to the function `read`[^1],
but if something goes wrong the returned value is `Nothing`.
If the value is right, it returns `Just
getListFromString :: String -> Maybe [Integer]
getListFromString str = maybeRead $ "[" ++ str ++ "]"
main = IO ()
main = do
putStrLn "Enter a list of numbers (separated by comma):"
input <- getLine
let maybeList = getListFromString input in
case maybeList of
Just l -> print (sum l)
Nothing -> error "Bad format. Good Bye."
import Data.Maybe
maybeRead :: Read a => String -> Maybe a
maybeRead s = case reads s of
[(x,"")] -> Just x
_ -> Nothing
getListFromString :: String -> Maybe [Integer]
getListFromString str = maybeRead $ "[" ++ str ++ "]"
askUser :: IO [Integer]
askUser = do
putStrLn "Enter a list of numbers (separated by comma):"
input <- getLine
let maybeList = getListFromString input in
case maybeList of
Just l -> return l
Nothing -> askUser
main :: IO ()
main = do
list <- askUser
print $ sum list
askUser :: IO [Integer]
askUser = do
putStrLn "Enter a list of numbers (separated by comma):"
input <- getLine
let maybeList = getListFromString input in
case maybeList of
Just l -> return l
Nothing -> askUser
main :: IO ()
main = do
list <- askUser
print $ sum list
askUser :: IO [Integer]
askUser = do
putStrLn "Enter a list of numbers:"
input <- getLine
let maybeList = getListFromString input in
case maybeList of
Just l -> return l
Nothing -> askUser
askUser w0 =
let (_,w1) = putStrLn "Enter a list of numbers:"
(input,w2) = getLine w1
(l,w3) = case getListFromString input of
Just l -> (l,w2)
Nothing -> askUser w2
in
(l,w3)