Slide 1

Slide 1 text

Applied Functional Programming With Haskell Utrecht University Summer School

Slide 2

Slide 2 text

Course Details

Slide 3

Slide 3 text

Cost of 1.850€ including: 2 weeks (4th - 15th July) with housing, breakfast, lunch and a couple of dinners with the teachers. Each day consisted of 2 conferences and 2 labs. Around 200 hours of content packed into around 70 hours of course which I’ll skim in ~1 hour

Slide 4

Slide 4 text

20 people, 17 nationalities "#$%&'()*+&,-./012 of them 4 were girls

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

Teachers

Slide 13

Slide 13 text

Atze Dijkstra Developer and maintainer of UHC

Slide 14

Slide 14 text

Doaitse Swierstra EDSLs researcher with more than 30 years of experience

Slide 15

Slide 15 text

Johan Jeuring Responsible of master courses in Generic programming and Theory of Programming and Types

Slide 16

Slide 16 text

Wouter Swierstra Lecturer in the Software Technology Group of Utrecht University

Slide 17

Slide 17 text

Lectures

Slide 18

Slide 18 text

Lambda Calculus Doaitse Swierstra

Slide 19

Slide 19 text

λ-calculus Is a higher-order Term Rewriting System and simplistic programming language that is Turing-Complete and supports higher-order functions naturally.

Slide 20

Slide 20 text

Term Rewriting System Rewriting is the theory of stepwise/discrete transformation of objects.

Slide 21

Slide 21 text

Church-Turing thesis Establishes an equivalence in computing capability between: λ Calculus (Alonzo Church) Turing Machine (Alan Turing)

Slide 22

Slide 22 text

–Stephen Diehl “the design of the core calculus should drive development, not the fronted language”

Slide 23

Slide 23 text

Core Language As small as possible functional language so that is easy to implement but still rich enough to allow modern languages to be translated into it without losing efficiency.

Slide 24

Slide 24 text

Grammar e ::= x (variables)
 | e e (applications)
 | λx.e (abstractions) Application associates to the left. Only unary functions and unary application.

Slide 25

Slide 25 text

Rewrite rules β reduction - Used to simplify expressions: 
 (λx.e)a→ β e[x →a] α conversion - Used to avoid name capturing:
 (λx. λy. x y) y → (λx. λz. x z) y η conversion - Used to allow point-free style of programming:
 λx. e x → η e

Slide 26

Slide 26 text

Simply-Typed λ-calculus e ::= x (variables)
 | e e (applications)
 | λx:t.e (abstractions) t ::= τ (type variables)
 | τ → τ (function types) Function types nest to the right: τ → σ → ρ = τ → (σ → ρ). Programs always terminate so it’s not Turing complete. There are no polymorphic functions.

Slide 27

Slide 27 text

fixed-point combinator Is a combinator with the property that for any f: 
 fix f = f (fix f) We say that (fix f) is a fixed point of f. Fixed point combinators can be used to express recursion in the lambda calculus (Y-combinator is the most famous one). Not typeable in the simply-typed lambda calculus without introducing the (fix :: (a → a) → a) primitive.

Slide 28

Slide 28 text

polymorphism The typed lambda calculus based on the idea of type abstraction and application is called System F or polymorphic lambda calculus. Haskell’s core language isn’t really System F because in System F type inference is undecidable, so Haskell never infer a polymorphic type for a lambda argument without annotation (Monomorphism Restriction). System F has higher-rank polymorphism, Haskell allows it through RankNTypes language extension but they cannot be inferred.

Slide 29

Slide 29 text

Most Haskell constructs can be desugared to λ-calculus Constructors of recursive data types using the Scott- Encoding Constructors of data types using the Church-Encoding General recursion using a fixed-point combinator Pattern matching using nested applications of case functions if-then-else can be expressed as a function …

Slide 30

Slide 30 text

Generic Programming Johan Jeuring

Slide 31

Slide 31 text

Generic Programming Is a form of abstraction that allows defining functions that can operate on large class of datatypes.

Slide 32

Slide 32 text

Advantages Eliminate boilerplate code and focus in the interesting parts Programs that adapt automatically as they evolve

Slide 33

Slide 33 text

Implementation Idea If we can describe regular datatypes in a different way, using a limited number of combinators, we can use this common structure to formally define algorithms for all regular datatypes. Steps to follow: Abstract from recursion Describe the “remaining” structure systematically

Slide 34

Slide 34 text

Objective With this: data Bit = O | I class Encode a where put :: a -> [Bit] be able to do this: data Exp = Const Int | Plus Exp Exp deriving (Show,Encode)

Slide 35

Slide 35 text

Type-level fixed points data Fix (f :: * -> *) = In (f (Fix f))

Slide 36

Slide 36 text

Pattern Functor on Lists data [a] = [] | a:[a] data ListF a r = NilF | ConsF a r Isomorphic type: type List’ a = Fix (ListF a)

Slide 37

Slide 37 text

Pattern Functor on Trees data Tree a = Tip a | Bin (Tree a) a (Tree a) data TreeF a r = TipF a | BinF r a r Isomorphic type: type Tree’ a = Fix (TreeF a)

Slide 38

Slide 38 text

Combinators for creating pattern functors systematically data (f:+:g) r = L (f r) | R (f r) data (f:*:g) r = f r:*:g r data I r = I r data K a r = K a data U r = U

Slide 39

Slide 39 text

Pattern Functor on Lists data ListF a r = NilF | ConsF a r becomes: type ListS a r = U:+:(K a:*:I) r

Slide 40

Slide 40 text

Pattern Functor on Trees data TreeF a r = TipF a | BinF r a r becomes: type TreeS a r = K a:+:(I:*:K a:*:I) r

Slide 41

Slide 41 text

What’s left is define the encoding of each combinator and primitive data types.

Slide 42

Slide 42 text

Fusion Doaitse Swierstra

Slide 43

Slide 43 text

Problem sum $ map (+1) $ map (*2) [1 ..10] Allocates intermediate lists that consume memory even when they not appear in the result.

Slide 44

Slide 44 text

Fusion With fusion we want the compiler to automatically eliminate such intermediate data structures. Fusion involves inlining recursive functions which is really hard but GHC is already really good at inlining non- recursive functions.

Slide 45

Slide 45 text

One technique Define our functions in terms of foldr/build (not all of them can) and exploit the fact that GHC’s simplifier will perform the following transformation: foldr c n (build g) -> g c n

Slide 46

Slide 46 text

The main idea behind this trick is that build is the inverse of foldr, build builds a list with (:) and [], on the other hand foldr op n on the other hand replaces all (:) by op and [] by n. Knowing that we can tell why foldr op n (build g) is doing some redundant work that can be removed directly with 
 g op n

Slide 47

Slide 47 text

Other technique Two important functions foldl and zip are not folds, in this case a technique using the dual of folds: unfolds, is used because they (as folds) can also fuse… but I still don’t understand it

Slide 48

Slide 48 text

Monads, IO and Transformers Atze Dijkstra

Slide 49

Slide 49 text

Monads Monads encode a common pattern that arises when working with multiple data types. The interface of such pattern is: sequencing embedding

Slide 50

Slide 50 text

Monads are not About sequencing Haskell already has a primitive that allows sequencing without the need of monads: let Impure or about side-effects About making Haskell imperative

Slide 51

Slide 51 text

Maybe Type sequencing: 
 stepResult >>= nextComp = case stepResult of
 Nothing -> Nothing
 Just result -> nextComp result embedding:
 return result = Just result
 maybe1 >>= \r1 -> maybe2 >>= \r2 -> …

Slide 52

Slide 52 text

Either Type sequencing: 
 stepResult >>= nextComp = case stepResult of
 Left err -> Left err
 Right result -> nextComp result embedding:
 return result = Right result
 either1 >>= \r1 -> either2 >>= \r2 -> …

Slide 53

Slide 53 text

State Type type State s a = s -> (a, s) sequencing: 
 stepResult >>= nextComp = \s -> 
 let (s’, result) = stepResult s
 in (nextComp result) s’ embedding:
 return result = \s -> (result, s)
 state1 >>= \r1 -> state2 >>= \r2 -> …

Slide 54

Slide 54 text

Maybe: >>= sequences operations that may give no results and shortcuts evaluation in those cases, return embeds a function that never fails. Either: >>= sequences operations that may result in an error and shortcuts evaluation in those cases, return embeds a function that never gives an error. State: >>= sequences operations that may modify a piece of state and threads that state through the operations, return embeds a function that never modifies the state.

Slide 55

Slide 55 text

IO The IO type is just another types that uses sequencing and embedding so it’s abstracted as a Monad, although it is kind of a special type: is a primitive type, >>= and return are primitive functions named bindIO and returnIO there is no (politically correct) function IO a -> a values of IO a denote side-effecting computations that can be executed by the runtime system Notice: The specialty of IO has really not much to do with being a Monad

Slide 56

Slide 56 text

IO in Haskell More and more features have been integrated into IO, for instance: classic file and terminal IO: putStr, hPutStr references: newIORef, readIORef, writeIORef access to the system: getArgs, getClockTime exceptions: throwIO, try, catch concurrency: forkIO

Slide 57

Slide 57 text

Transformers A way of stacking different Monads on top of each other and use functionality of the underlying monads in the top monad. For (nearly) any monad, we can define a corresponding monad transformer.

Slide 58

Slide 58 text

Transformers 
 transformers package Lift a computation from the argument monad to the constructed monad. class MonadTrans t where lift :: Monad m => m a -> t m a

Slide 59

Slide 59 text

MaybeT Type newtype MaybeT m a = MaybeT (m (Maybe a)) import Control.Monad.Trans.Class
 import Control.Monad.Trans.Maybe checkPassword :: MaybeT IO Bool
 checkPassword = do
 password <- lift getLine
 if password == "secret"
 then return True
 else fail "invalid password"

Slide 60

Slide 60 text

ExceptT Type newtype ExceptT e m a = ExceptT (m (Either e a)) import Control.Monad.Trans.Class
 import Control.Monad.Trans.Except checkPassword :: ExceptT String IO Bool
 checkPassword = do
 password <- lift getLine
 if password == "secret"
 then return True
 else throwE "invalid password"

Slide 61

Slide 61 text

StateT Type newtype StateT s m a = StateT (s -> m (a, s)) import Control.Monad.Trans.Class
 import Control.Monad.Trans.State
 import qualified Data.Map as Map getContact :: StateT (Map.Map String String) IO ()
 getContact = do
 input <- lift getLine
 let [name, phone] = words input
 modify (Map.insert name phone)
 return () …
 execStateT getContact Map.empty

Slide 62

Slide 62 text

Transformers 
 mtl package This library extends the transformers library with multi- parameter type classes with functional dependencies such as MonadState and MonadReader. Transformers is Haskell 98 and thus more portable, and doesn't tie you to functional dependencies (GHC specific). But because it lacks the monad classes, you'll have to lift operations to the composite monad yourself.

Slide 63

Slide 63 text

GADTs Johan Jeuring

Slide 64

Slide 64 text

Motivation data Expr = ExprInt Int
 | ExprBool Bool
 | ExprPlus Expr Expr Our type allows to mix Int and Bool when adding.

Slide 65

Slide 65 text

Rewrite example using GADTs… {-# LANGUAGE GADTs, KindSignatures # -} data Expr :: * where
 ExprInt :: Int -> Expr
 ExprBool :: Bool -> Expr
 ExprPlus :: Expr -> Expr -> Expr

Slide 66

Slide 66 text

… and we encode the types of the terms {-# LANGUAGE GADTs, KindSignatures # -} data Expr :: * -> * where
 ExprInt :: Int -> Expr Int
 ExprBool :: Bool -> Expr Bool
 ExprPlus :: Expr Int -> Expr Int -> Expr Int

Slide 67

Slide 67 text

GADTs GADTs lift the restriction that constructors must target a simple type.

Slide 68

Slide 68 text

Lazy Evaluation Doaitse Swierstra

Slide 69

Slide 69 text

What’s the result? take' :: Int -> [a] -> [a]
 take' 0 _ = []
 take' n l = head l:take' (n - 1) (tail l) let v = error “kaboom”
 v -> ?
 length (take 3 v) -> ?

Slide 70

Slide 70 text

What’s the result? take' :: Int -> [a] -> [a]
 take' 0 _ = []
 take' n l = head l:take' (n - 1) (tail l) let v = error “kaboom”
 v -> ***Exception
 length (take 3 v) -> 3

Slide 71

Slide 71 text

What drives evaluation Every expression is evaluated when needed. It is pattern- matching (and evaluation of conditions) what drives evaluation in Haskell. With garbage collection we do not have to worry when the life of a value ends. With lazy evaluation we do not have to worry when the life of a value starts.

Slide 72

Slide 72 text

Where lazy evaluation matters Describing process-like structures Recurrent relations Combining function, e.g. by building an infinite structure and inspecting only a finite part of it Composing functions, e.g. map (+1) . map (*2) $ …

Slide 73

Slide 73 text

Non-termination In lambda calculus we can encode recursion and therefore nonterminating terms A nonterminating value is usually written ⊥ In Haskell ⊥ is available as undefined :: a In the presence of ⊥, a function f is called strict if 
 f ⊥ = ⊥ We can force evaluation with the primitive function
 seq, ($!) :: a -> b -> b

Slide 74

Slide 74 text

Concurrency Atze Dijkstra

Slide 75

Slide 75 text

Concurrency Language constructs or library functions that allow you to structure a program into multiple threads of control that are executed concurrently. Mainly a structuring mechanism No parallel hardware required to make use of it Useful in many settings that have nothing directly to do with parallelism Non deterministic

Slide 76

Slide 76 text

Threads Control.Concurrent forkIO :: IO () -> IO ThreadId threadDelay :: Int -> IO () yield :: IO () myThreadId :: IO ThreadId killThread :: ThreadId -> IO () throwTo :: Exception e => ThreadId -> e -> IO () Very lightweight. They are created and scheduled by the GHC runtime. Use -thread to use OS threads behind the scenes Use STM for communicating between threads

Slide 77

Slide 77 text

Parallelism The concept of running programs on multiple CPUs in the hope of getting a speedup. It really is about speed, not structuring Deterministic

Slide 78

Slide 78 text

Running in parallel Control.Parallel We mark parts of the program that we consider suitable for parallel evaluation We let the runtime system decide about all the details par :: a -> b -> b Indicates that it may be beneficial to evaluate the first argument in parallel with the second. Eval monad makes easier to define parallel strategies. It is a strict identity monad

Slide 79

Slide 79 text

exitWith ExitSuccess thank you!