-
Couldn't load subscription status.
- Fork 2
Haskell
Haskell is a purely functional statically typed lazy programming language.
- functions have no side effects
- functions are referential transparent (same result for calls with the same parameters)
- Haskell has type inference
- you can’t set a variable to one value and then set it to something else later on
- Haskell won’t execute functions until it needs to show you a result
- Haskell doesn’t have a concept of pointers, just values
:l <source-file-without-.hs-suffix> -- load a source code
:r -- reload the file
:t <expression> -- type of the expression
:k <type> -- kind of the type
:m + <module>,<module>,... -- import modules
ghc --make <source-file-without-.hs-suffix> -- compile a program
doubleMe x = x + x
doubleMe 4 -- 8
doubleUs x y = x * 2 + y * 2
doubleUs 4 9 -- 26
doubleUs 2.3 34.2 -- 73.0Every function in Haskell officially takes only one parameter. All the functions that accepted multiple parameters are curried functions.
-- :t max
max :: Ord a => a -> a -> a -- same as `a -> (a -> a)`
-- These two calls are equivalent:
max 4 5
(max 4) 5
max4 = max 4
max4 5
5divideByTen :: (Floating a) => a -> a
divideByTen = (/10)
divideByTen 200 -- 20.0
(/10) 200 -- 20.0isUpperAlphanum :: Char -> Bool
isUpperAlphanum = (`elem` ['A'..'Z'])
isUpperAlphanum 'A' -- True
isUpperAlphanum 'a' -- FalseGenerally, a function like foo a = bar b a, can be rewrited as foo = bar b because of currying:
sum' :: (Num a) => [a] -> a
sum' xs = foldl (+) 0 xs
sum' :: (Num a) => [a] -> a
sum' = foldl (+) 0applyTwice :: (a -> a) -> a -> a
applyTwice f x = f (f x)
applyTwice (+2) 1 -- 5Lambdas are anonymous functions that we use when we need a function only once.
\<parameters> -> <body>
map (\x -> x + 3) [1,2,3] -- [4,5,6]
-- is same as `map (+3) [1,6,3,2]`
map (\(a,b) -> a + b) [(1,2),(3,5)] -- [3,8]-- These two functions are equivalent:
addThree :: Int -> Int -> Int -> Int
addThree x y z = x + y + z.
addThree :: Int -> Int -> Int -> Int
addThree' = \x -> \y -> \z -> x + y + z- When ommiting parentheses, everything to the right of the arrow
->belongs to the lambda.
flip' :: (a -> b -> c) -> b -> a -> c
flip' f = \x y -> f y x($) :: (a -> b) -> a -> b
f $ x = f x
Function application with a space is left-associative (so f a b c is the same as ((f a) b) c), while function application with $ is right-associative.
-
$function has the lowest precedence:
sum (map sqrt [1..130])
sqrt (3 + 4 + 9)
-- could be rewitten as:
sum $ map sqrt [1..130]
sqrt $ 3 + 4 + 9-
$function is right-associative:f $ g $ xis equivalent tof $ (g $ x):
sum (filter (> 10) (map (*2) [2..10]))
-- could be rewitten as:
sum $ filter (> 10) $ map (*2) [2..10]-
$lets us treat function application like just another function:
map ($ 3) [(4+), (10*), (^2), sqrt] -- [7.0,30.0,9.0,1.7320508075688772](.) :: (b -> c) -> (a -> b) -> a -> c
f . g = \x -> f (g x)
(+1) . (*100) $ 4 -- 401map (\x -> negate (abs x)) [1,2,-3] -- [-1,-2,3]
map (\xs -> negate (sum (tail xs))) [[1..5],[3..6]] -- [-14,-15]
-- is equivalent to:
map (negate . abs) [1,2,-3]
map (negate . sum . tail) [[1..5],[3..6]]-- these lines are equivalent:
sum (replicate 3 (max 1 2)) -- can be understood as `(sum . replicate 3) max 1 2`
sum . replicate 3 $ max 1 2Because of currying we can ommit the parameter in the function definition:
fn x = ceiling (negate (tan (cos (max 50 x)))) -- definition with the parameter
fn = ceiling . negate . tan . cos . max 50 -- same function in point-free styleplusThreeTimesTwo :: Num c => c -> c
plusThreeTimesTwo = (*) 2 . (+) 3
plusThreeTimesTwo 2 -- 10- the
elseclause is mandatory
if x > 100 then x else x * 2Lists in Haskell are homogeneous data structure.
- strings are just lists of chars
-
[1,2,3]is just syntactic sugar for1:2:3:[]
[] -- empty list
0:[1,2] -- [0,1,2]
[1,2] ++ [3,4,5] -- [1,2,3,4,5]
'A':" SMALL CAT" -- "A SMALL CAT"
"Steve Buscemi" !! 6 -- B
[1,2,3] !! 0 -- 1
[1,2,3] !! 1 -- 2
[1,2,3] !! 3 -- *** Exception: index too largeb = [[1,2,3,4],[5,3,3,3],[1,2,2,3,4],[1,2,3]]
b ++ [[1,1,1,1]] -- [[1,2,3,4],[5,3,3,3],[1,2,2,3,4],[1,2,3],[1,1,1,1]]
[6,6,6]:b -- [[6,6,6],[1,2,3,4],[5,3,3,3],[1,2,2,3,4],[1,2,3]]
b !! 2 -- [1,2,2,3,4][3,2,1] > [2,10,100] -- True
[3,4,2] > [2,4] -- True
[3,4,2] == [3,4,2] -- True -- [head][--------tail----------]
-- [----------init--------][last]
head [5,4,3,2,1] -- 5
tail [5,4,3,2,1] -- [4,3,2,1]
last [5,4,3,2,1] -- 1
init [5,4,3,2,1] -- [5,4,3,2]length [1,2,3] -- 3
null [1,2,3] -- False
null [] -- True
reverse [1,2,3] -- [3,2,1]
take 2 [1,2,3] -- [1,2]
take 0 [1,2,3] -- []
take 9 [1,2,3] -- [1,2,3]
drop 2 [1,2,3] -- [3]
drop 0 [1,2,3] -- [1,2,3]
drop 9 [1,2,3] -- []
maximum [1,2,3] -- 3
minimum [1,2,3] -- 1
sum [1,2,3,4] -- 10
product [1,2,3,4] -- 24
elem 2 [1,2,3] -- True
elem 2 [3,4,5] -- False[1..10] -- [1,2,3,4,5,6,7,8,9,10]
['a'..'z'] -- "abcdefghijklmnopqrstuvwxyz"
[2, 4..10] -- [2,4,6,8,10]
take 5 [13, 26..] -- [13,26,39,52,65]
take 10 (cycle [1,2,3]) -- [1,2,3,1,2,3,1,2,3,1]
take 12 (cycle "LOL ") -- "LOL LOL LOL "
take 10 (repeat 5) -- [5,5,5,5,5,5,5,5,5,5]
replicate 3 10 -- [10,10,10]
[0.1, 0.3 .. 1] -- [0.1,0.3,0.5,0.7,0.8999999999999999,1.0999999999999999][x*2 | x <- [1..10]] -- [2,4,6,8,10,12,14,16,18,20]
[x*2 | x <- [1..10], x*2 >= 12] -- [12,14,16,18,20]
boomBangs xs = [ if x < 10 then "BOOM!" else "BANG!" | x <- xs, odd x]
boomBangs [7..13] -- ["BOOM!","BOOM!","BANG!","BANG!"]
[ x | x <- [10..20], x /= 13, x /= 15, x /= 19] -- [10,11,12,14,16,17,18,20]
[x+y | x <- [10,100], y <- [1,2,3]] -- [11,12,13,101,102,103]
length' xs = sum [1 | _ <- xs] -- underscore `_` is a temporary variable we don't care aboutmap (+3) [1,2,3] -- [4,5,6]
map (++ "!") ["Bang", "Pow"] -- ["Bang!","Pow!"]
map (replicate 3) [3..5] -- [[3,3,3],[4,4,4],[5,5,5]]
map (map (^2)) [[1,2],[3,4,5]] -- [[1,4],[9,16,25]] listOfFncs = map (*) [0..] -- we will get back a list of functions
(listOfFncs !! 4) 5
-- Getting the element with the index 4 returns a function equivalent to (4*).
-- Then we just apply 5 to that function, which is the same as (4*) 5.filter (>3) [5,3,2,4] -- [5,4]
filter (==3) [1,2,3,4,5] -- [3]
filter (`elem` ['a'..'z']) "u LaUgH aT mE" -- "uagam"
filter (<15) (filter even [1..10]) -- [2,4,6,8,10]quicksort :: (Ord a) => [a] -> [a]
quicksort [] = []
quicksort (x:xs) =
let smallerOrEqual = filter (<= x) xs
larger = filter (> x) xs
in quicksort smallerOrEqual ++ [x] ++ quicksort larger- Folds allow you to reduce a data structure (like a list) to a single value.
- A fold takes a binary function (one that takes two parameters, such as + or div), a starting value (often called the accumulator ), and a list to fold up.
- A right fold over the list [1,2,3], is essentially doing this:
f 1 (f 2 (f 3 acc))
sum' :: (Num a) => [a] -> a
sum' xs = foldl (\acc x -> acc + x) 0 xs
-- alternatively `sum' = foldl (+) 0`
sum' [1,2,3] -- 6map' :: (a -> b) -> [a] -> [b]
map' f xs = foldr (\x acc -> f x : acc) [] xs
-- is much cheaper than the fold-left alternative:
map' f xs = foldl (\acc x -> acc ++ [f x]) [] xselem' :: (Eq a) => a -> [a] -> Bool
elem' y ys = foldr (\x acc -> if x == y then True else acc) False ys
reverse' :: [a] -> [a]
reverse' = foldl (\acc x -> x : acc) []
product' :: (Num a) => [a] -> a
product' = foldl (*) 1
filter' :: (a -> Bool) -> [a] -> [a]
filter' p = foldr (\x acc -> if p x then x : acc else acc) []
last' :: [a] -> a
last' = foldl1 (\_ x -> x)- work much like
foldlandfoldr, except assume the first (resp. last) element to be the starting accumulator.
maximum' :: (Ord a) => [a] -> a
maximum' = foldl1 maxThe scanl and scanr functions are like folds, except they report all the intermediate accumulator states in the form of a list.
scanl (+) 0 [1,2,3] -- [0,1,3,6]
scanr (+) 0 [1,2,3] -- [6,5,3,0]
scanl (flip (:)) [] [1,2,3] -- [[],[1],[2,1],[3,2,1]]Tuples are used to store several heterogeneous elements as a single value.
(1, 3) -- (1,3)
(3, 'a', "hello") -- (3,'a',"hello")
(1, 50.4, "hello", 'b') -- (1,50.4,"hello",'b')
[(1,2),(8,11,5),(4,5)] -- error: Couldn't match expected type `(a, b)`fst (8, 11) -- 8
snd (8, 11) -- 11
zip [1,2,3] [5,5,5] -- [(1,5),(2,5),(3,5)]
zip [1,2,3,4,5] ["a","b"] -- [(1,"a"),(2,"b")]
zip ["a","b","c"] [1..] -- [("a",1),("b",2),("c",3)]phoneBook = [("betty","555-2938"), ("bonnie","452-2928"), ("wendy","939-8282")]
--
findKey :: (Eq k) => k -> [(k, v)] -> v
findKey key xs = snd . head . filter (\(k, v) -> key == k) $ xs
findKey "wendy" phoneBook -- "939-8282"
findKey "diana" phoneBook -- "*** Exception: head: empty list
--
findKey :: (Eq k) => k -> [(k, v)] -> Maybe v
findKey key xs = foldr (\(k, v) acc -> if key == k then Just v else acc) Nothing xs
findKey "wendy" phoneBook -- Just "939-8282"
findKey "diana" phoneBook -- Nothing
### Pattern Matching
Pattern matching is used to specify patterns to which some data should conform and to deconstruct the data according to those patterns.
```haskell
lucky :: Int -> String
lucky 7 = "LUCKY NUMBER SEVEN!"
lucky x = "Sorry, you're out of luck, pal!"
lucky 1 -- "Sorry, you're out of luck, pal!"
lucky 7 -- "LUCKY NUMBER SEVEN!"
factorial :: Int -> Int
factorial 0 = 1
factorial n = n * factorial (n - 1)
factorial 8 -- 40320
addVectors (x1, y1) (x2, y2) = (x1 + x2, y1 + y2)
addVectors (1,2) (10,20) -- (11,22)
first (x, _, _) = x
first (1,2,3) -- 1As-patterns allow you to break up an item according to a pattern, while still keeping a reference to the entire original item.
firstLetter "" = "Empty string, whoops!"
firstLetter myParam@(x:xs) = "The first letter of " ++ myParam ++ " is " ++ [x]
firstLetter "Dracula" -- "The first letter of Dracula is D"initials :: String -> String -> String
initials firstname lastname = [f] ++ ". " ++ [l] ++ "."
where (f:_) = firstname
(l:_) = lastnamecylinder :: Double -> Double -> Double
cylinder r h =
let sideArea = 2 * pi * r * h
topArea = pi * r ^ 2
in sideArea + 2 * topArea- can be used anywhere in the code:
4 * (let a = 9 in a + 1) + 2 -- 42
[let square x = x * x in (square 5, square 3, square 2)] -- [(25,9,4)]- can be separated with semicolons:
(let a = 2; b = 5 in a*b, let foo="Hey "; bar = "there!" in foo ++ bar) -- (10,"Hey there!")max' a b
| a <= b = b
| otherwise = a
max' 2 5 -- 5
weightTell :: Int -> String
weightTell weight
| weight <= 50 = "You're underweight, you emo, you!"
| weight <= 75 = "You're supposedly normal. Pffft, I bet you're ugly!"
| weight <= 95 = "You're fat! Lose some weight, fatty!"
| otherwise = "You're a whale, congratulations!"
weightTell 60 -- "You're supposedly normal. Pffft, I bet you're ugly!"
bmiTell :: Double -> Double -> String
bmiTell weight height
| bmi <= 18.5 = "You're underweight, you emo, you!"
| bmi <= 25.0 = "You're supposedly normal. Pffft, I bet you're ugly!"
| bmi <= 30.0 = "You're fat! Lose some weight, fatty!"
| otherwise = "You're a whale, congratulations!"
where bmi = weight / height ^ 2case expression of pattern -> result
pattern -> result
...
head' :: [a] -> a
head' xs = case xs of [] -> error "No head for empty lists!"
(x:_) -> xPattern matching on function parameters can be done only when defining functions, but case expressions can be used anywhere:
describeList :: [a] -> String
describeList ls = "The list is " ++ case ls of [] -> "empty."
[x] -> "a singleton list."
xs -> "a longer list."Because pattern matching in function definitions is the same as using case expressions, we could have also defined the describeList function like this:
describeList ls = "The list is " ++ what ls
where what [] = "empty."
what [x] = "a singleton list."
what xs = "a longer list."maximum' :: (Ord a) => [a] -> a
maximum' [] = error "maximum of empty list!"
maximum' [x] = x
maximum' (x:xs) = max x (maximum' xs)reverse' :: [a] -> [a]
reverse' [] = []
reverse' (x:xs) = reverse' xs ++ [x]replicate' :: Int -> a -> [a]
replicate' n x
| n <= 0 = []
| otherwise = x : replicate' (n-1) xtake' :: (Num i, Ord i) => i -> [a] -> [a]
take' n _
| n <= 0 = []
take' _ [] = []
take' n (x:xs) = x : take' (n-1) xsquicksort :: (Ord a) => [a] -> [a]
quicksort [] = []
quicksort (x:xs) =
let smallerOrEqual = [a | a <- xs, a <= x]
larger = [a | a <- xs, a > x]
in quicksort smallerOrEqual ++ [x] ++ quicksort largerquicksort "the quick brown fox jumps over the lazy dog"
" abcdeeefghhijklmnoooopqrrsttuuvwxyz"data Bool = False | Truedata Point = Point Float Float deriving (Show)
data Shape = Circle Point Float | Rectangle Point Point deriving (Show)
area :: Shape -> Float
area (Circle _ r) = pi * r ^ 2
area (Rectangle (Point x1 y1) (Point x2 y2)) = (abs $ x2 - x1) * (abs $ y2 - y1)
area $ Circle (Point 0 0) 10 -- 314.15927
area $ Rectangle (Point 0 0) (Point 100 100) -- 10000.0baseRect :: Float -> Float -> Shape
baseRect width height = Rectangle (Point 0 0) (Point width height)data Person = Person {
firstName :: String,
lastName :: String,
age :: Int} deriving (Show)
guy = Person "Charles" "Bukowski" 74 -- alternatively:
guy = Person{ age=74, lastName="Bukowski", firstName="Charles" } -- any order
firstName guy -- "Charles" // the function is generated automatically
describePerson :: Person -> String
describePerson (Person {firstName = first, lastName = last, age = age}) =
"Person " ++ first ++ " " ++ last ++ " is " ++ show age
describePerson guy -- "Person Charles Bukowski is 74"data Maybe a = Just a | Nothing -- the `a` here is the type parameterDepending on what we want this data type to hold when it's not Nothing, this type constructor can end up producing a type of Maybe Int, Maybe Person, and so on. No value can have a type of just Maybe, because that’s not a type—it's a type constructor.
Just "abc" -- Just "abc"
:t Just "abc" -- Just "abc" :: Maybe [Char]
Just 84 -- Just 84
:t Just 84 -- Just 84 :: (Num a) => Maybe a
Just 1 :: Maybe Double -- Just 1.0
:t Nothing -- Nothing :: Maybe aWe say that a type is concrete if it doesn’t take any type parameters at all (like Int or Bool), or if it takes type parameters and they’re all filled up (like Maybe Char). If you have some value, its type is always a concrete type.
data IntMaybe = INothing | IJust IntLike functions, type constructors can be partially applied.
Classes in Haskell are defining a behavior. First we create our type and then we make it an instance of a class depending what behavior we need.
data Day = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday
deriving (Eq, Ord, Show, Read, Bounded, Enum)
Wednesday -- Wednesday
show Wednesday -- "Wednesday"
read "Wednesday" :: Day -- Wednesday
Saturday == Sunday -- False
Saturday > Friday -- True
Monday `compare` Wednesday -- LT
minBound :: Day -- Monday
maxBound :: Day -- Sunday
succ Monday -- Tuesday
pred Saturday -- Friday
[Thursday .. Saturday] -- [Thursday,Friday,Saturday]type String = [Char]type PhoneNumber = String
type Name = String
type PhoneBook = [(Name, PhoneNumber)]type AssocList k v = [(k, v)]data Either a b = Left a | Right b
deriving (Eq, Ord, Read, Show)
Right 20 -- Right 20
:t Left 'a' -- Left 'a' :: Either Char bdata Tree a = EmptyTree | Node a (Tree a) (Tree a) deriving (Show)
singleton :: a -> Tree a
singleton x = Node x EmptyTree EmptyTree
treeInsert :: (Ord a) => a -> Tree a -> Tree a
treeInsert x EmptyTree = singleton x
treeInsert x (Node a left right)
| x == a = Node x left right
| x < a = Node a (treeInsert x left) right
| x > a = Node a left (treeInsert x right)
treeElem :: (Ord a) => a -> Tree a -> Bool
treeElem x EmptyTree = False
treeElem x (Node a left right)
| x == a = True
| x < a = treeElem x left
| x > a = treeElem x right
numsTree = foldr treeInsert EmptyTree [1,5,4,2]
numsTree
Node 2
(Node 1 EmptyTree EmptyTree)
(Node 4 EmptyTree
(Node 5 EmptyTree EmptyTree)
)
treeElem 1 numsTree -- True
treeElem 8 numsTree -- False-- define a new type class:
class Eq a where
(==) :: a -> a -> Bool
(/=) :: a -> a -> Bool
x == y = not (x /= y)
x /= y = not (x == y)
-- create an instance of that class:
data TrafficLight = Red | Yellow | Green
instance Eq TrafficLight where
Red == Red = True
Green == Green = True
Yellow == Yellow = True
_ == _ = False
Red == Red -- True
Red == Yellow -- False
Red -- error: No instance for (Show TrafficLight)
instance Show TrafficLight where
show Red = "Red light"
show Yellow = "Yellow light"
show Green = "Green light"
Red -- Red lightBecause == was defined in terms of /= and vice versa in the class declaration, we needed to overwrite only one of them in the instance declaration.
class (Eq a) => Num a where
...This is just like writing class Num a where, but we state that our type a must be an instance of Eq.
That’s all there is to subclassing — it’s just a class constraint on a class declaration!
For example Maybe is not a concrete type, it's a type constructor that takes one parameter and then produces a concrete type (so you can't have a function of the type a -> Maybe, but it's okay to have a -> Maybe a or Maybe Int -> Maybe String).
-- instance Eq Maybe where -- doesn't compile
-- make all types of the form `Maybe something` an instance of `Eq`:
instance (Eq m) => Eq (Maybe m) where
Just x == Just y = x == y
Nothing == Nothing = True
_ == _ = FalseWe use == on the contents of the Maybe, so we have the assurance that what the Maybe contains can be used with Eq - that's why we needed to add the class constraint.
-- (==) :: Maybe -> Maybe -> Bool -- doesn’t make much sense
(==) :: (Eq m) => Maybe m -> Maybe m -> Bool -- okayA kind is more or less the type of a type.
-
*indicates that the type is a concrete type - concrete type is a type that doesn’t take any type parameters
- values can have only types that are concrete types
:k Int -- Int :: *
-- `Maybe` type constructor takes one concrete type (like Int) and returns a concrete type (like Maybe Int):
:k Maybe -- Maybe :: * -> *
:k Maybe Int -- Maybe Int :: *
-- Either takes two concrete types as type parameters to produce a concrete type:
:k Either -- Either :: * -> * -> *
:k Either Int -- Either Int :: * -> *
:k Either Int String -- Either Int String :: *The Functor type class is for things that can be mapped over (e.g. list).
class Functor f where
fmap :: (a -> b) -> f a -> f bfmap takes a function from one type to another and a functor value applied with one type and returns a functor value applied with another type.
-- `map` is a `fmap` that works only on lists:
map :: (a -> b) -> [a] -> [b]
-- the list's instance of the Functor type class:
instance Functor [] where
fmap = mapHere’s how Maybe is a functor:
instance Functor Maybe where
fmap f (Just x) = Just (f x)
fmap f Nothing = Nothing
-- If it’s an empty value of Nothing, then just return a Nothing.
-- If it’s a single value packed in a `Just`, then we apply the function on the contents of the `Just`
fmap (*2) Nothing -- Nothing
fmap (*2) (Just 200) -- Just 400We wrote instance Functor Maybe where instead of instance Functor (Maybe m) where because a functor wants a type constructor that takes one type, and not a concrete type.
The Functor type class wants a type constructor that takes only one type parameter, but Either takes two - we’ll partially apply Either by feeding it only one parameter, so that it has one free parameter:
data Either a b = Left a | Right b
instance Functor (Either a) where
fmap f (Right x) = Right (f x)
fmap f (Left x) = Left xWe can think of the Left part as sort of an empty box with an error message written on the side telling us why it’s empty.
- if we map the id function over a functor value, the functor value that we get back should be the same as the original
functor value.
fmap id = id - composing two functions and then mapping the resulting function over a functor should be the same as first mapping one function over the functor and then mapping the other one.
fmap (f . g) = fmap f . fmap g
Applicative type class, in the Control.Applicative module defines two functions: pure and <*>.
class (Functor f) => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f bApplicative instance implementation for Maybe:
instance Applicative Maybe where
pure = Just
Nothing <*> _ = Nothing
(Just f) <*> something = fmap f something
Just (+3) <*> Just 9 -- Just 12
pure (+3) <*> Just 9 -- Just 12
Just (++"hahah") <*> Nothing -- Nothing
pure (+) <*> Just 3 <*> Just 5 -- Just 8
pure (+) <*> Nothing <*> Just 5 -- Nothing
pure (+) <*> Just 3 <*> Nothing -- NothingControl.Applicative exports a function called <$>, which is just fmap as an infix operator:
(<$>) :: (Functor f) => (a -> b) -> f a -> f b
f <$> x = fmap f x
(++) <$> Just "hey" <*> Just "ho" -- Just "heyho"pure f <*> x = fmap f xpure id <*> v = vpure (.) <*> u <*> v <*> w = u <*> (v <*> w)pure f <*> pure x = pure (f x)u <*> pure y = pure ($ y) <*> u
Monads are just beefed-up applicative functors, much like applicative functors are beefed-up functors.
Monads are a natural extension of applicative functors, and they provide a solution to the following problem: If we have a value with a context, m a, how do we apply to it a function that takes a normal a and returns a value with a context? In other words, how do we apply a function of type a -> m b to a value of type m a? Essentially, we want this function:
(>>=) :: (Monad m) => m a -> (a -> m b) -> m b -- `>>=` function is called *bind*.class Monad m where
return :: a -> m a -- pure function
(>>=) :: m a -> (a -> m b) -> m b -- bind function
(>>) :: m a -> m b -> m b
x >> y = x >>= \_ -> y
fail :: String -> m a
fail msg = error msgMaybe is an instance of Monad:
instance Monad Maybe where
return x = Just x
Nothing >>= f = Nothing
Just x >>= f = f x
fail _ = Nothing
return "WHAT" :: Maybe String -- Just "WHAT"
Just 9 >>= \x -> return (x*10) -- Just 90
Nothing >>= \x -> return (x*10) -- NothingFor gluing together monadic values in sequence.
foo :: Maybe String
foo = Just 3 >>= (\x -> Just "!" >>= (\y -> Just (show x ++ y)))
-- is the same as:
foo :: Maybe String
foo = do
x <- Just 3
y <- Just "!"
Just (show x ++ y)
foo -- Just "3!"do expressions are just different syntax for chaining monadic values.
- In a
doexpression, every line that isn’t aletline is a monadic value - To inspect its result, we use
<-(except the last monadic value)
In do notation, when we bind monadic values to names, we can utilize pattern matching, just as in let expressions and function parameters:
justH :: Maybe Char
justH = do
(x:xs) <- Just "hello"
return xWhen pattern matching fails in a do expression, the fail function (part of the Monad type class) enables it to result in a failure in the context of the current monad, instead of making the program crash.
-- the default implementation does make the program crash
fail :: (Monad m) => String -> m a
fail msg = error msg
```haskell
-- implementation for `Maybe` ignores the error message and makes a `Nothing`:
fail _ = NothingIf we take a value, put it in a default context with return, and then feed it to a function by using >>=, that’s the same as just taking the value and applying the function to it.
-
return x >>= fis same asf x.
return 3 >>= (\x -> Just (x+100000)) -- Just 100003
(\x -> Just (x+100000)) 3 -- Just 100003If we have a monadic value and we use >>= to feed it to return, the result is our original monadic value.
-
m >>= returnis same as justm
Just "abc" >>= (\x -> return x) -- Just "abc"When we have a chain of monadic function applications with >>=, it shouldn’t matter how they’re nested.
- doing
(m >>= f) >>= gis just like doingm >>= (\x -> f x >>= g)When we look at the law as a law of compositions, it states thatf <=< (g <=< h)should be the same as(f <=< g) <=< h.
liftM :: (Monad m) => (a -> b) -> m a -> m b -- pretty like `fmap`
liftM f m = m >>= (\x -> return (f x))
liftM (*3) (Just 8) -- Just 24join :: (Monad m) => m (m a) -> m a -- flatts monadic values
join mm = do
m <- mm
m
join (Just (Just 9)) -- Just 9
join (Just Nothing) -- Nothing
join Nothing -- NothingfilterM :: (Monad m) => (a -> m Bool) -> [a] -> m [a]foldM :: (Monad m) => (a -> b -> m a) -> a -> [b] -> m a
binSmalls :: Int -> Int -> Maybe Int
binSmalls acc x
| x > 9 = Nothing
| otherwise = Just (acc + x)
foldM binSmalls 0 [2,8,3,1] -- Just 14
foldM binSmalls 0 [2,11,3,1] -- Nothing- Functors: apply vanilla functions to structures.
- Applicatives: also, some functions are in the structure.
- Monads: also, some functions produce the structure.

A module is essentially a file that defines some functions, types, and type classes. A program is a collection of modules.
import Data.List
Data.List (nub, sort) -- only functions `nub` and `sort`
import Data.List hiding (nub) -- all the functions except `nub`
import qualified Data.Map -- call the function by `Data.Map.nub`
import qualified Data.Map as M -- call the function by `M.nub`module MyDir.MyModule (fce1, fce2, ...) where
fce1 :: Num a => a -> a
fce1 = (+) 2
...digitToInt '2' -- 2
digitToInt 'A' -- 10ord 'a' -- 97
chr 97 -- 'a'
words "hello world!" -- ["hello","world!"]
sort [2,3,1] -- [1,2,3]
tails "abc" -- ["abc","bc","c",""]
tails [1,2,3] -- [[1,2,3],[2,3],[3],[]]
isPrefixOf "ab" "abc" -- True
any (> 4) [1,2,3] -- False
find (== 'a') "abc" -- Just 'a'
find (> 4) [1,2,3] -- Nothing
foldl' (+) 0 (replicate 1000000 1) -- 1000000 (no stack overflow errors)fromList [("MS",1),("MS",2),("MS",3)] -- fromList [("MS",3)]
Map.lookup "betty" phoneBook -- Just "555-2938"
Map.lookup "diana" phoneBook -- Nothing
newBook = Map.insert "diana" "841-9021" phoneBook
Map.lookup "diana" phoneBook -- Just "841-9021"
Map.size phoneBook -- 3
Map.size newBook -- 4:t putStrLn -- putStrLn :: String -> IO ()By putting I/O actions together with do syntax, we glued them into one I/O action:
helloworld.hs
main = do
putStrLn "Hello, what's your name?"
name <- getLine
putStrLn ("Hey " ++ name ++ ", you rock!")- If we’re taking data out of an I/O action, we can take it out only when we’re inside another I/O action.
- To get the value out of an I/O action, you must perform it inside another I/O action by binding it to a name with
<-
In Haskell (and in I/O actions specifically), return makes an I/O action out of a pure value.
- using
returndoesn’t cause the I/O do block to end in execution -
returnis sort of the opposite of<-
putStr "Hey, "
putStr "I'm "
putStrLn "Andy!"
putChar 't'
print True -- True
print 2 -- 2
print "haha" -- "haha"
print 3.2 -- 3.2
print [3,4,3] -- [3,4,3]
print $ map (++"!") ["hey","ho"] -- ["hey!","ho!"]when takes a Bool and an I/O action, and if that value is True, it returns the same I/O action that we supplied to it. If it’s False, it returns the return () action, which doesn’t do anything.
import Control.Monad
main = do
input <- getLine
when (input == "SWORDFISH") $ do
putStrLn inputsequence function takes a list of I/O actions and returns an I/O action that will perform those actions one after the other. The result that this I/O action yields will be a list of the results of all the I/O actions that were performed.
main = do
rs <- sequence [getLine, getLine, getLine]
print rssequence $ map print [1,2,3,4,5]
1
2
3
4
5
[(),(),(),(),()]mapM takes a function and a list, maps the function over the list, and then sequences it.
mapM_ does the same thing, but it throws away the result later.
mapM print [1,2,3]
1
2
3
[(),(),()]forever function takes an I/O action and returns an I/O action that just repeats the I/O action it got forever.
import Control.Monad
import Data.Char
main = forever $ do
putStr "Give me some input: "
l <- getLine
putStrLn $ map toUpper lgetContents reads everything from the standard input until it encounters an end-of-file character. It does lazy I/O.
import Data.Char
main = do
contents <- getContents
putStr $ map toUpper contentsinteract takes a function of type String -> String as a parameter and returns an I/O action that will take some input, run that function on it, and then print out the function’s result.
main = interact shortLinesOnly
shortLinesOnly :: String -> String
shortLinesOnly = unlines . filter (\line -> length line < 10) . linesopenFile :: FilePath -> IOMode -> IO Handle
type FilePath = String
data IOMode = ReadMode | WriteMode | AppendMode | ReadWriteModeimport System.IO
main = do
handle <- openFile "data.txt" ReadMode
contents <- hGetContents handle
putStr contents
hClose handle// `withFile` makes sure that the file handle gets closed.
withFile :: FilePath -> IOMode -> (Handle -> IO a) -> IO aimport System.IO
main = do
withFile "girlfriend.txt" ReadMode (\handle -> do
contents <- hGetContents handle
putStr contents)readFile :: FilePath -> IO String
writeFile :: FilePath -> String -> IO ()import System.IO
import Data.Char
main = do
contents <- readFile "data.txt"
writeFile "data.txt" (map toUpper contents)import System.IO
main = do
todoItem <- getLine
appendFile "todo.txt" (todoItem ++ "\n")bracket :: IO a -> (a -> IO b) -> (a -> IO c) -> IO cwithFile :: FilePath -> IOMode -> (Handle -> IO a) -> IO a
withFile name mode f = bracket (openFile name mode)
(\handle -> hClose handle)
(\handle -> f handle)import System.Environment
import Data.List
main = do
args <- getArgs
progName <- getProgName
putStrLn "The arguments are:"
mapM putStrLn args
putStrLn "The program name is:"
putStrLn progName- Miran Lipovača: Learn You a Haskell for Great Good!
- O’Sullivan, Goerzen, Stewart: Real World Haskell
- http://www.haskellforall.com/2018/10/detailed-walkthrough-for-beginner.html
- Haskell training: https://github.com/google/haskell-trainings