ITMO CTD Haskell
Lecture slides on Functional programming course at the ITMO university CT department. You can find course description here: https://github.com/jagajaga/FP-Course-ITMO
GHCi — GHC Haskell REPL: interactively execute commands
$ stack repl # you can also use 'stack ghci' to run 'ghci', or
$ cabal new-repl # from inside project
<Some logging information, bla-bla-bla>
Prelude> -- 'Prelude' prompt can be changed; slides use 'ghci>' prompt
ghci> 1 + 2 * 3
7
ghci> 3 / 5 * (7 + 1)
4.8
ghci> 2 ^ 16
65536
ghci> 1 + 1 == 3
False
ghci> 2 + 2 /= 5
True
ghci> 21 * 22 <= 20 * 23
False
ghci> 1 < 2 && 2 < 3
True
ghci> 0 > 0 || 10 >= 10
True
Basic arithmetic & boolens & comparisons
At least you can use Haskell as calculator!
Calling functions in ghci: arguments separated by space character
ghci> not True
False
ghci> div 7 3 -- integral division (7 divide by 3)
2
ghci> max 3 5
5
ghci> min 6 (10 * 2) -- not the same as 'min 6 10 * 2'
6
Personally I like space separated arguments; compare
max 1 2 // Haskell
std::max(1, 2) // C++
Math.max(1, 2) // Java
But you should think about arguments grouping
ghci> div 7 3 + 1
3
ghci> div 7 (3 + 1)
1
ghci> div 7 + 1 3
*** Compilation error!
ghci> div (7 + 1) 3
2
Infix and Prefix call syntax:
* Both — operators and functions — can be called in prefix and infix
* Wrap operator in () to call in prefix
* Wrap function in `` (bacticks, ~ on keyboard) to call in infix
ghci> 3 + 4
7
ghci> (+) 3 4
7
ghci> mod 7 3 -- 7 modulo 3
1
ghci> 7 `mod` 3
1
Assigning variables
-- Before GHC 8.0.1
ghci> let x = 3 + 5
ghci> x + 1
9
-- After GHC 8.0.1
ghci> x = 7 + 8
ghci> x * 2
30
String type
ghci> greeting = "Hello"
ghci> greeting ++ " world!" -- use ++ instead of + to concatenate strings
"Hello world!"
ghci> "DONE: say \"" ++ greeting ++ " world!\""
"DONE: say "Hello world!""
Types are important! Use `:t` command to see type in GHCi
ghci> :t 'x'
'x' :: Char
ghci> :t False
False :: Bool
ghci> :t not
not :: Bool -> Bool
ghci> :t (&&)
(&&) :: Bool -> Bool -> Bool
ghci> :t 42
42 :: Num t => t -- numeric constants are polymorphic;
-- we will get to that later
Use `:set +t` command to show types after each command
ghci> :set +t
ghci> 'x'
'x'
it :: Char
ghci> False || True
True
it :: Bool
ghci> let x = False
x :: Bool
-- This function adds first number to product of second and third
addMul :: Int -> Int -> Int -> Int -- This is type signature of 'addMul'
addMul x y z = x + y * z -- This is definition of 'addMul'
-- Btw, single-line comments start with --
greet :: String -> String
greet name = "Hello, " ++ name ++ "!"
Let's create our functions! Put next code into 'Lecture.hs' file
$ stack ghci # from folder where 'Lecture.hs' is
ghci> :l Lecture.hs -- :l is short form of :load command
ghci> addMul 1 2 3
7
ghci> greet "Haskell World"
"Hello, Haskell World!"
Reminder: arguments grouping
addMul 1 2 3 + 1
addMul 1 2 (3 + 1)
addMul 1 (1 + 1) 3
addMul 1 1 + 1 3
infixr 1 ==> -- specify associativity for 'implies' operator
(==>) :: Bool -> Bool -> Bool
a ==> b = not a || b
In Haskell you can create your own operators! It doesn't mean you should... But sometimes they are really handy.
ghci> False ==> False ==> False
True
ghci> False ==> (False ==> False)
True
ghci> (False ==> False) ==> False
False
[infix|infixl|infixr] <precedence ∈ [0..10)> [operators]
-- somewhere in 'base'
infixr 2 ||
(||) :: Bool -> Bool -> Bool
Function application — space — has highest precedence 10.
ghci> True || True ==> False
???
ghci> True || (True ==> False)
???
ghci> True || True ==> False
False
ghci> True || (True ==> False)
True
infixr 3 &&
(&&) :: Bool -> Bool -> Bool
ghci> [1 + 2, 3 + 4, 5 * 6] -- comma-separated
[3,7,30]
Linked lists: homogeneous collection of elements
ghci> let list = [2, 1, 3] -- create list variable
ghci> [5, 10] ++ list -- concatenate lists with ++ operator
[5, 10, 2, 1, 3]
ghci> 10 : list -- : operator adds element to the beginning of list
[10, 2, 1, 3]
ghci> list
[2, 1, 3]
ghci> reverse list -- this doesn't change 'list'
[3, 1, 2]
ghci> list
[2, 1, 3]
ghci> let anotherList = 5 : list -- new variable to save changes
ghci> anotherList
[5, 2, 1, 3]
ghci> list
[2, 1, 3]
All variables in Haskell are immutable by default!
emptyList :: [Int]
emptyList = []
listExample :: [Int]
listExample = [2, 1, 3] -- 2:1:3:[]
singletonList :: [Int]
singletonList = 1 : emptyList -- or simply [1]
listExample' = 5:10:listExample -- [5, 10, 2, 1, 3]
twoLists = singletonList ++ listExample -- [1, 2, 1, 3]
trinity = listExample ++ [7] ++ twoLists -- [2, 1, 3, 7, 1, 2, 1, 3]
string :: [Char] -- extremely inefficent representation of String
string = "str" -- ['s', 't', 'r']
otherString :: String -- other name for [Char]
otherString = "other " ++ string -- "other str"
ghci> "" == []
True
Java 8
IntStream.range(0, 5).toArray(); // {0, 1, 2, 3, 4};
IntStream.rangeClosed(0, 5).toArray(); // {0, 1, 2, 3, 4, 5};
IntStream.iterate(0, x -> x + 2).limit(5).toArray() // {0, 2, 4, 6, 8};
Kotlin
0..5 // 0, 1, 2, 3, 4, 5
0 until 5 // 0, 1, 2, 3, 4
0..5 step 2 // 0, 2, 4
Haskell
[0 .. 5] -- [0, 1, 2, 3, 4, 5]
[1, 3 .. 5] -- [1, 3, 5, 7]
[0..] -- [0, 1, 2, 3, ...] : infinite list
[0, 2 ..] -- [0, 2, 4, 6, ...] : all even numbers
[5, 4 .. 1] -- [5, 4, 3, 2, 1]
[5 .. 1] -- [] — empty list
Python
range(5) # 0, 1, 2, 3, 4
range(1, 5) # 1, 2, 3, 4
range(1, 5, 2) # 1, 3
ghci> let l = [2, 1, 3]
ghci> head l
2
ghci> tail l
[1, 3]
ghci> last l
3
ghci> init l
[2, 1]
Accessing elements of list
ghci> drop 2 [2, 1, 3]
[3]
ghci> [2, 1, 3] !! 2 -- l !! i ≡ l[i], O(i) time
3
Functions from Prelude
And moar: takeWhile, splitAt, iterate, reverse, lines, unlines, etc. Prelude has a lot of functions and operators to work with lists.
ghci> take 1 [2, 1, 3]
[2]
ghci> replicate 3 [1..5]
[[1,2,3,4,5], [1,2,3,4,5], [1,2,3,4,5]]
ghci> zip [1,2,3] "abc"
[(1, 'a'), (2, 'b'), (3, 'c')] -- (1, 'a') is pair of type (Int, Char)
ghci> unzip [(5, True), (10, False)]
([5, 10], [True, False])
ghci> words "Hello, Haskell \t\n\n world!"
["Hello,", "Haskell", "world!"]
ghci> unwords ["Hello,", "Haskell", "world!"]
"Hello, Haskell world!"
void f(); // function definition is statement
int random() {
return 4; // `return` is statement, 4 is expression
}
int main() {
int a = random(); // variable declaration: statement, `random()` is expression
if (a > 0) { // `if` operator is statement, `a > 0` is expression
printf("Wow, such C++"); // statement, or?...
}
}
С++
Common statements in imperative languages are expressions in Haskell
pythagoras :: Int -> Int -> Int
pythagoras x y = x^2 + y^2
let <bindings> in <expression>
Let's implement with let:
Already can do it in simple way!
With let
pythagoras :: Int -> Int -> Int
pythagoras x y = let x2 = x ^ 2 -- Alignment is EXTREMELY IMPORTANT!!!
y2 = y ^ 2 -- No tabs, only spaces!
in x2 + y2
Haskell is layout-sensitive language.
It means that ugly code won't compile.
ghci> let x = 10 -- saves variable 'x', doesn't print it
ghci> x
10
ghci> let y = 1 + 3 in y -- doesn't save variable 'y', prints result immediately
4
ghci> y
<interactive>:41:1: error: Variable not in scope: y
Believe me, there's a reason to have several ways to assign variables in ghci
pythagoras :: Double -> Double -> Double
pythagoras a b = a2 + b2
where -- details of implementation are inside 'where'
square x = x ^ 2
a2 = square a
b2 = square b
Haskell
Java
double pythagoras(double a, double b) {
class Squarer {
double eval(double x) { return x * x; }
}
final double a2 = new Squarer().eval(a);
final double b2 = new Squarer().eval(b);
return a2 + b2;
}
You want to define local functions or variables to: (1) not spoil global namespace or (2) for optimization purposes (in Haskell)
Difference between let and where?
factorial :: Integer -> Integer -- arbitrary precision integer type
factorial n = if n <= 1
then 1
else n * factorial (n - 1) -- this implementation is
-- extremely inefficient
if <predicate>
then <expression if predicate is True>
else <expression if predicate is False>
So it's more like ternary operator
Side note: use Natural from `Numeric.Natural` instead of Integer if you only mean non-negative (≥ 0) integers (like in example above)
public int collatzSum(int n) {
if (n < 0)
return 0;
else if (n == 1)
return 1;
else if (n % 2 == 0)
return n + collatzSum(n / 2);
else
return n + collatzSum(3 * n + 1);
}
Java
collatzSum :: Natural -> Natural
collatzSum n
| n == 1 = 1
| even n = n + collatzSum (n `div` 2)
| otherwise = n + collatzSum (3 * n + 1)
Haskell
otherwise :: Bool
otherwise = True
What is otherwise?
public String getFont(int fontConstant) {
String font = null;
switch (fontConstant) {
case 0: font = "PLAIN"; break;
case 1: font = "BOLD"; break;
case 2: font = "ITALIC"; break;
default: font = "UNKNOWN";
}
return font;
}
Java
Haskell
getFont :: Int -> String
getFont n = case n of
0 -> "PLAIN"
1 -> "BOLD"
2 -> "ITALIC"
_ -> "UNKNOWN"
caseOperation
:: Char -> Int -> Int -> Int
caseOperation op x y =
case op of
'+' -> x + y
'-' -> x - y
_ -> 0 -- _ should be
-- under ' and
-- not under -
case <expression> of
[<pattern> → <expression>]
inc, dec :: Int -> Int
inc x = x + 1
dec x = x - 1
Function in Haskell is a first class object. It means that you can pass functions as arguments to other functions.
changeTwiceBy :: (Int -> Int) -> Int -> Int
changeTwiceBy operation value = operation (operation value)
ghci> changeTwiceBy inc 5
7
ghci> changeTwiceBy dec 5
3
\arg1 arg2 ... argN → <expression>
ghci> changeTwiceBy (\x -> x + 1) 5
7
ghci> changeTwiceBy (\x -> x * 2) 5
20
tripleApply :: (Int -> Int -> Int) -> Int -> Int
tripleApply f x = (x `f` x) `f` (x `f` x)
ghci> tripleApply (\x y -> x ^ y) 2
256
ghci> tripleApply (^) 2 -- you can pass operators as functions
256
Without lambda
first :: a -> a -> a
first x y = x
public <T> T first(T x, T y) {
return x;
}
Java
Haskell
parametric polymorphism vs. ad-hoc polymorphism
overloading?
id :: a -> a
id x = x
fst :: (a, b) -> a
snd :: (a, b) -> b
emptyList :: [a]
emptyList = []
repeatThree :: a -> [a]
repeatThree x = [x, x, x]
ghci> repeatThree 'x'
"xxx"
it :: [Char]
ghci> repeatThree True
[True, True, True]
it :: [Bool]
ghci> tripleApply (^) 2
256
ghci> tripleApply (++) [3,1]
[3, 1, 3, 1, 3, 1, 3, 1]
tripleApply :: (a -> a -> a) -> a -> a
tripleApply f x = f (f x x) (f x x)
map :: (a -> b) -> [a] -> [b]
filter :: (a -> Bool) -> [a] -> [a]
foldr1 :: (a -> a -> a) -> [a] -> a
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
ghci> map negate [2, 1, 3]
[-2,-1,-3]
ghci> filter odd [1,2,3,4,5]
[1,3,5]
ghci> foldr1 (+) [1,2,4] -- sum [1,2,4]
7
ghci> takeWhile isUpper "HTMLXml"
"HTMLX"
ghci> zipWith max [1..5] [5, 4 .. 1]
[5,4,3,4,5]
uncurry :: (a -> b -> c) -> (a, b) -> c
uncurry f p = f (fst p) (snd p)
ghci> uncurry (+) (3, 4)
7
ghci> curry fst 3 4
3
ghci> curry snd 3 4
4
Functions in Haskell are very simple! They have only one argument and one result. Function arrow is right-associative.
bar :: (Int -> Int) -> Int -> Int
bar :: (Int -> Int) -> (Int -> Int) -- ^ these two bars are the same
evilBar :: (Int -> Int) -> Int -> Int
evilBar :: Int -> Int -> Int -> Int -- ^ and these two are not
foo :: Int -> Char -> String -> Double
foo :: Int -> (Char -> (String -> (Double))
foo, bar :: Int -> String
foo x = show x
bar = show
-- monomoprhic 'div'
div :: Int -> (Int -> Int)
Works with operators!
ghci> map (+2) [2, 1, 3]
[4, 3, 5]
ghci> filter (<3) [1..5]
[1, 2]
ghci> filter (3<) [1..5]
[4, 5]
-- div7By is partially applied div
ghci> div7By = div (7 :: Int)
div7By :: Int -> Int
ghci> div7By 2
3
ghci> div7By 3
2
ghci> (div 7) 3
2
ghci> map (div 7) [1..7]
[7,3,2,1,1,1,1]
ghci> map (5+) [1..5]
[6, 7, 8, 9, 10]
ghci> map (+5) [1..5]
[6, 7, 8, 9, 10]
You can partially apply (+) operator without problems
But you can't do the same with (-) operator because (-5) is a constant
ghci> map (5-) [1..5]
[4,3,2,1,0]
ghci> map (-5) [1..5]
• Non type-variable argument in the constraint: Num (a -> b)
(Use FlexibleContexts to permit this)
• When checking the inferred type
it :: forall a b. (Enum a, Num (a -> b), Num a) => [b]
Use `subtract` function instead
ghci> subtract 5 3
-2
ghci> map (subtract 5) [1..5]
[-4, -3, -2, -1, 0]
flip :: (a -> b -> c) -> b -> a -> c
flip f b a = f a b
When you finally understand flip
show2 :: Int -> Int -> String
show2 x y = show x ++ " and " ++ show y
showSnd, showFst, showFst'
:: Int -> String
showSnd = show2 1
showFst = flip show2 2
showFst' = (`show2` 2)
ghci> showSnd 10
"1 and 10"
ghci> showFst 10
"10 and 2"
ghci> showFst' 42
"42 and 2"
ghci> :t flip id
???
ghci> :t flip id
flip id :: b -> (b -> c) -> c
fact :: Integer -> Integer
fact 0 = 1
fact n = n * fact (n - 1)
map :: (a -> b) -> [a] -> [b]
map _ [] = []
map f (x:xs) = f x : map f xs -- [2, 1, 3] == 2 : 1 : 3 : []
sumList3 :: [Int] -> Int
sumList3 [x, y, z] = x + y + z
sumList3 _ = 0
puzzle :: [Int] -> [Int]
puzzle (x:xs@(y:z:_)) = if x < y && y > z then y : c else c
where
c = puzzle xs
puzzle _ = []
dropWhile :: (a -> Bool) -> [a] -> [a]
dropWhile _ [] = []
dropWhile p l@(x:xs) = if p x then dropWhile p xs else l
stringLit :: String -> String
stringLit "such" = "pattern"
stringLit "much" = "amaze"
stringLit x = "wow"
Some Haskell features are not enabled by default. They can be enabled by so-called 'language pragmas'.
-- in ghci
ghci> :set -X<NameOfPragma>
ghci> map (\x -> (42, x)) [1..5]
[(42,1), (42,2), (42,3), (42,4), (42,5)]
ghci> map (\x -> (x, 42)) [1..5]
[(1,42), (2,42), (3,42), (4,42), (5,42)]
Example: you want to pair each element of list with 42.
ghci> :set -XTupleSections
ghci> map (42,) [1..5]
[(42,1), (42,2), (42,3), (42,4), (42,5)]
ghci> map (,42) [1..5]
[(1,42), (2,42), (3,42), (4,42), (5,42)]
-- in MyModule.hs
{-# LANGUAGE NameOfPragma #-}
veryLongFunctionName :: Int -> String
veryLongFunctionName 0 = foo
veryLongFunctionName 1 = bar
veryLongFunctionName n = baz n
{-# LANGUAGE LambdaCase #-}
veryLongFunctionName :: Int -> String
veryLongFunctionName = \case
0 -> foo
1 -> bar
n -> baz n
-- syntax sugar for
veryLongFunctionName :: Int -> String
veryLongFunctionName x = case x of
0 -> foo
1 -> bar
n -> baz n
exactTwoWords :: String -> Bool
exactTwoWords s = case words s of
[_, _] -> True
_ -> False
{-# LANGUAGE ViewPatterns #-}
exactTwoWords :: String -> Bool
exactTwoWords (words -> [_, _]) = True
exactTwoWords _ = False
infixr 0 $
($) :: (a -> b) -> a -> b -- function application
f $ x = f x
In Functional programming everything is a function. Even function application — space ' ' — is function. What if...
Why? Useful to remove ()
foo, bar :: [Int] -> Int
foo list = length (filter odd (map (div 2) (filter even (map (div 7) list))))
bar list = length $ filter odd $ map (div 2) $ filter even $ map (div 7) list
ghci> length [0..5]
6
ghci> length $ [0..5]
6
ghci> let funs = [(+2), div 7, (*5)]
funs :: [Integer -> Integer]
ghci> let vals = [20, 30, 40]
vals :: [Integer]
ghci> :t zipWith
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
ghci> zipWith ($) funs vals
[22, 0, 200]
ghci> zipWith id funs vals
[22, 0, 200]
Reverse application
infixl 1 &
(&) :: a -> (a -> b) -> b -- in 'Data.Function' module
x & f = f x
($) :: (a -> b) -> a -> b
(&) :: a -> (a -> b) -> b
(&) = flip ($)
ghci> (\l -> l ++ reverse l) [2,1,3]
[2,1,3,3,1,2]
ghci> [2,1,3] & \l -> l ++ reverse l -- useful for removing parenthesis
[2,1,3,3,1,2]
infixr 9 .
(.) :: (b -> c) -> (a -> b) -> (a -> c) -- same as (b -> c) -> (a -> b) -> a -> c
f . g = \x -> f (g x)
incNegate :: Int -> Int
incNegate x = negate (x + 1)
incNegate x = negate $ x + 1
incNegate x = (negate . (+1)) x
incNegate x = negate . (+1) $ x
incNegate = negate . (+1) -- η-reduce
"outside . inside" is a composition
1. \x -> outside (inside x)
2. \x -> outside $ inside x
3. \x -> outside . inside $ x
4. outside . inside
foo, bar :: [Int] -> Int
foo patak = length $ filter odd $ map (div 2) $ filter even $ map (div 7) patak
bar = length . filter odd . map (div 2) . filter even . map (div 7)
Don't need to keep names of variables in mind
Instead of specifying what to do with variables, you specify how data should be transformed
stringsTransform :: [String] -> [String]
stringsTransform l = map (\s -> map toUpper s) (filter (\s -> length s == 5) l)
stringsTransform l = map (\s -> map toUpper s) $ filter (\s -> length s == 5) l
stringsTransform l = map (map toUpper) $ filter ((== 5) . length) l
stringsTransform = map (map toUpper) . filter ((== 5) . length)
Simplifying complex function
stringsTransform :: [String] -> [String]
stringsTransform l = map (\s -> map toUpper s) (filter (\s -> length s == 5) l)
stringsTransform l = map (\s -> map toUpper s) $ filter (\s -> length s == 5) l
stringsTransform l = map (map toUpper) $ filter ((== 5) . length) l
stringsTransform = map (map toUpper) . filter ((== 5) . length)
Simplifying complex function
ghci> [x | x <- [1..10], even x]
[2,4,6,8,10]
quickSort :: [Int] -> [Int]
quickSort [] = []
quickSort (x:xs)
= quickSort [y | y <- xs, y <= x] ++ [x] ++ quickSort [y | y <- xs, y > x]
ghci> filter even [1..10]
[2,4,6,8,10]
ghci> [if even x then "!" else "?" | x <- [1 .. 5]]
["?","!","?","!","?"]
ghci> [ x * y | x <- [1, 3, 5], y <- [2, 4, 6], x * y >= 10]
[12,18,10,20,30]
ghci> [13 | even 13] -- conditionally create singleton list
[]
ghci> [14 | even 14]
[14]
square x = x*x
square (1+2)
square (1+2)
=> (1+2)*(1+2)
(1+2)*(1+2)
=> 3*(1+2)
=> 3*3
=> 9
Evaluated (1+2) twice :(
Every expression can be represented as a graph
Every function corresponds to a reduction rule
No reduction rule for constructor
Lazy evaluation always tries to reduce the topmost function application first
No unevaluated redexes and graph represents the list 1:2:3:[].
42
(2, "hello")
\x -> (x + 1)
1 + 2
(\x -> x + 1) 2
"he" ++ "llo"
(1 + 1, 2 + 2)
10
2 : [1, 3]
'h' : ("e" ++ "llo")
[1, 2 * 2, 3 ^ 3]
[4, length undefined]
Just (2 + 7)
(1 + 2, 3 + 4)
\x -> x + 10
\zx -> foldr (+) zx [1, 2, 3]
1 + 1
1 + (2 + 3)
(\x -> x + 10) 3
length [1, 2, 3]
Just (2 + 7) >>=
\n -> Just $ n * 2
fromJust' :: Maybe a -> Int
fromJust' (Just a) = 10
ghci> fromJust' undefined
*** Exception: Prelude.undefined
first :: Int -> Int -> Int
first x _ = x
zeroF :: Int -> Int -> Int
zeroF x 0 = x
zeroF _ y = y
ghci> fromJust' $ Just undefined
10
ghci> first 10 undefined
10
ghci> zeroF 10 undefined
*** Exception: Prelude.undefined
Time: Lazy evaluation never performs more evaluation steps than eager evaluation.
Space: Memory used by an unevaluated expression may differ significantly from the memory used by its normal form
((((0 + 1) + 2) + 3) + 4)
It is called a space leak
ghci> [1..]
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,
31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,
58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,
85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,
109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,
129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,
149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,
169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187, ...
Lists in Haskell can be infinite. But only required part is evaluated.
ghci> repeat 1
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, ...
ghci> take 5 [1..]
[1,2,3,4,5]
ghci> zip [0..] "abacaba"
[(0,'a'), (1,'b'), (2,'a'), (3,'c'), (4,'a'), (5,'b'), (6,'a')]
-- | @'fix' f@ is the least fixed point of the function @f@,
-- i.e. the least defined @x@ such that @f x = x@.
fix :: (a -> a) -> a
fix f = let x = f x in x
ghci> fix id
<hangs>
fix :: (a -> a ) -> a [1]
fix :: ((b -> c) -> (b -> c)) -> (b -> c) [2]
fix :: ((b -> c) -> b -> c ) -> b -> c [3]
import Data.Function (fix)
fixLen :: [a] -> Int
fixLen = fix $ \f l -> case l of
[] -> 0
(x:xs) -> 1 + f xs
ghci> fixLen [1..5]
5
Small challenge: implement fixLen using accumulator
primes :: [Int]
primes = filterPrime [2..]
where
filterPrime (p:xs) = p : filterPrime [x | x <- xs, x `mod` p /= 0]
ghci> take 15 primes
[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47]
fibs :: [Int]
fibs = 0 : 1 : zipWith (+) fibs (drop 1 fibs)
fib :: Int -> Int
fib n = fibs !! n
ghci> take 10 fibs
[0,1,1,2,3,5,8,13,21,34]
ghci> fib 50
12586269025
q1 = filter (even . length)
q2 = filter (odd . sum . take 5) . map (replicate 10)
q3 = length . filter id . map even -- id x = x
q4 = (. length) . (+)
By ITMO CTD Haskell
Lecture contains basic haskell syntax constructions such as function definitions, simple keywords, polymorphism, lists with some juice, HOF's, currying, application and composition with comparison to other languages. Also introduces fun laziness.
Lecture slides on Functional programming course at the ITMO university CT department. You can find course description here: https://github.com/jagajaga/FP-Course-ITMO