(2015 06-16) Three Approaches to Monads

Post on 19-Jan-2017

767 views 0 download

Transcript of (2015 06-16) Three Approaches to Monads

Three Approaches to MonadsChris Evans

June 16, 2015

Three Approaches

I. How Monads Arise. (Ref: You Could Have Invented Monads)

II. Monads in Haskell (Ref: Real World Haskell)

III.Monads in Category Theory (Ref: Wikibooks - Haskell/Category Theory)

I. How Monads Arise

In a typed language considerf,g : Float -> Float

and their compositiong . f : Float -> Float

Example (A)

What if we want debugging information?f : Float -> (Float, String)f(x) = (2*x, “Doubled!”)

g : Float -> (Float, String)g(x) = (x/4, “Quartered!”)

Now we can’t compose them. Yet there is an obvious “composition”:

x |-> ((2*x)/4, “Doubled! Quartered!”)

Example (B)Consider multi-valued functionsf : Advertisable -> [Campaign]g : Campaign -> [Ad]

We can’t compose them but there is a natural “composition”

(In Python)

adv |-> [g(camp) for camp in f(adv)]

But this gets ugly for multiple compositions...

Example (C)Consider functions with possible failure

f : Logline -> TimeStamp (Not in Logline?)

g : TimeStamp -> DayOfWeek (Parsing fails?)

We can add a Bool to indicate successf : Logline -> (TimeStamp, Bool)g : TimeStamp -> (DayOfWeek, Bool)

We can no longer compose them but there is again a natural “composition” (propagate failure)

These three examples are similar.

Each involves a “decorated type”(A) (Float, String) for Float (B) [Campaign] for Campaign(C) (TimeStamp, Bool) for TimeStamp

For each example, we first define two functions, unit and bind.

unit -- maps basic type to decorated typebind -- “upgrades” function which takes basic

type to instead take decorated type

Example (A)unit : Float -> (Float, String)

bind : (Float -> (Float, String)) -> ((Float, String) -> (Float, String))

Example (A)unit : Float -> (Float, String) x |-> (x, “”)

bind : (Float -> (Float, String)) -> ((Float, String) -> (Float, String))

Example (A)unit : Float -> (Float, String) x |-> (x, “”)

bind : (Float -> (Float, String)) -> ((Float, String) -> (Float, String))

(bind f)(x, old_str) = let (y, new_str) = f x in (y, old_str ++ new_str)

Example (B)unit : Campaign -> [Campaign]

bind : (Campaign -> [Ad]) -> ([Campaign] -> [Ad])

Example (B)unit : Campaign -> [Campaign] camp |-> [camp]

bind : (Campaign -> [Ad]) -> ([Campaign] -> [Ad])

Example (B)unit : Campaign -> [Campaign] camp |-> [camp]

bind : (Campaign -> [Ad]) -> ([Campaign] -> [Ad])

(bind f) campaigns = flatten([f(camp) for camp in campaigns])

Example (C)unit : TimeStamp -> (TimeStamp, Bool)

bind : (TimeStamp -> (DayOfWeek, Bool)) -> ((TimeStamp, Bool) -> (DayOfWeek, Bool))

Example (C)unit : TimeStamp -> (TimeStamp, Bool) ts |-> (ts, True)

bind : (TimeStamp -> (DayOfWeek, Bool)) -> ((TimeStamp, Bool) -> (DayOfWeek, Bool))

Example (C)unit : TimeStamp -> (TimeStamp, Bool) ts |-> (ts, True)

bind : (TimeStamp -> (DayOfWeek, Bool)) -> ((TimeStamp, Bool) -> (DayOfWeek, Bool)) (bind f)(x,b) = case b of True -> f(x) False -> (??, False)

Pointsbind gives us the desired composition! Instead of g.f do (bind g).f

Pointsbind gives us the desired composition! Instead of g.f do (bind g).funit can be used to start the chain. Instead of g.f do (bind g).(bind f).unit

Pointsbind gives us the desired composition! Instead of g.f do (bind g).funit can be used to start the chain. Instead of g.f do (bind g).(bind f).unitunit/bind depend only on the “type extension”. e.g. there is a unit/bind for List not List-of-Strings.

The Big RevealEach example is an example of a monad! But what is a monad?

For Part I we’ll define a monad as “A type extension together with an implementation of unit and bind”

Ex: [](, String)(, Bool)

II. Monads in Haskell

HaskellStrongly/Statically typed (cannot even add Integer and Float!)

Pure functional language (absolutely no side effects. I/O must be handled specially)

There’s a type for that (where other languages use enums/structs/classes, Haskell uses types)

Defining Haskell Types

Type Synonyms: (type)type TimeStamp = Stringtype Advertisable = Stringtype Campaign = String

Syntactic sugar for existing types. In facttype String = [Char]

New Data Types: (data)type Name = Stringtype Age = Intdata Customer = Customer Name Age

“type constructor” “value constructor”

Algebraic Data Types: data Bool = True | Falsedata Color = Red | Blue | Greendata LineType = Impression |Click |Win |Bid

value constructors

More Algebraic Data Types: type CPM = Floattype VCPC = Floattype ClickProb = Floatdata PricingStrategy = FixedBid CPM |BidIQ VCPC

bid_price :: PricingStrategy -> ClickProb -> CPMbid_price strat p = case strat of FixedBid cpm -> cpm BidIQ vcpc -> vcpc * p

Parameterized Types:

data Maybe a = Just a | Nothingdata List a = Cons a (List a) | Nildata Tree a = Node a (Tree a) (Tree a) |Empty

Then can use Maybe String or List Float

Type Classes:

<<WARNING>> NOTHING LIKE CLASSES IN OO LANGUAGES! More like interfaces in OO languages...

Example: The Show Typeclassclass Show a where show :: a -> String

data LineType = Impression | Click | Win

instance Show LineType where show Impression = “imp” show Click = “cli” show Win = “win”

In fact the Haskell REPL uses show to display results!

Example: The Eq Typeclassclass Eq a where (==),(/=) :: a -> a -> Bool x == y = not (x /= y) x /= y = not (x == y)

Only need to define one, the other comes free!

Enter Monads!

Monads in Haskell are just a typeclass

class Monad m where (>>=) :: m a -> (a -> m b) -> m b (bind) return :: a -> m a (unit)

...which a parameterized type can implement!

Revisit the Examples in Part I

Example (B) -- The List Monad

instance Monad [] where return x = [x] xs >>= f = concat (map f xs)

(This is in the Haskell Prelude)

The Control.Monad library provides >=> for direct composition.

Haskell do syntax and list comprehensions

Example (C) -- The Maybe Monad

instance Monad Maybe where return x = Just x (Just x) >>= f = f x Nothing >>= f = Nothing

(This is in the Haskell Prelude)

Example (A) -- The History Monaddata History a = History a String

instance Monad History where return x = History x “” (History x old) >>= f = let (History y new) = f x in History y (old ++ new)

-- Implementation of Show omitted --

(Not in Prelude -- this is our creation)

Bonus Monad -- IO MonadI/O in Haskell is handled via the IO Monad.

e.g. Haskell’s “print” isputStrLn :: String -> IO ()

Part II TakeawaysIn Haskell, a Monad is just a typeclass... which a parametric type can implement by

implementing return and (>>=).Basic I/O in Haskell uses the IO Monad.do syntactic sugar emulates imperative code.

III. Monads in Category Theory

Category TheoryA category is a collection of objectsarrows/morphisms between pairs of objectsnotion of composition of arrowssatisfying(Associativity) h.(g.f) = (h.g).f(Identity) id . f = f , g . id = g

Ex: Set (Category of Sets)

Objects: SetsArrows: Functions between sets

Note: Set contains all sets and all functions between sets in the universe!

Set

{1, 3}

{2, 4}

{7, 8, 1}

id

f

id

g

h

g . f

r

Ex: Hask (Category of Haskell Types)

Objects: Haskell typesArrows: Haskell functions

Note: Hask contains all types and all functions in the universe!

Hask

[String]

String

Integer

id

my_join

id

my_len

my_func

my_split

FunctorsA functor is a mapping F between categories C and D.Maps each object X in C to F(X) in DMaps each arrow f : X -> Y to F(f) : F(X) -> F(Y)satisfyingF(id) = idF(g . f)=F(g) . F(f)

Ex: Powerset Functor P : Set -> SetMaps each set (object) to its power set e.g. P({1,2}) = { ϕ , {1}, {2}, {1,2} }

Maps arrow f : X -> Y to P(f) : P(X) -> P(Y) by [F(f)](S) = { f(s) | s in S}

(exercise: check that functor conditions are satisfied)

MonadsA monad is a functor M : C -> C along with two mappings for every object X in C.unit : X -> M(X)join : M(M(X)) -> M(X)

such that unit and join satisfy certain properties (“monad laws”)...

Monad Laws

1.join . M(join) = join . join2.join . M(unit) = join . unit = id3.unit . f = M(f) . unit4.join . M(M(f)) = M(f) . join

Ex: Powerset Functor is a Monadwhere unit : X -> P(X)

join : P(P(X)) -> P(X)

Ex: Powerset Functor is a Monadwhere unit : X -> P(X) unit(x) = {x}

join : P(P(X)) -> P(X)

Ex: Powerset Functor is a Monadwhere unit : X -> P(X) unit(x) = {x}

join : P(P(X)) -> P(X)

join(S) = U

s in Ss

Check Monad Laws (We’ll just check #3 here)3) unit . f = P(f) . unitConsider f : {1, 2} -> {3, 4, 5} 1 |-> 3, 2 |-> 5

(unit . f)(1) = unit(f(1)) = unit(3) = {3}(P(f).unit)(1) = [P(f)](unit(1)) = [P(f)]({1}) = {3}

same!

Back to Hask[ ] is a functor from Hask to Hask.● Maps objects:

● Maps functions:

Back to Hask[ ] is a functor from Hask to Hask.● Maps objects:

Maps type to list of that type

● Maps functions:

Back to Hask[ ] is a functor from Hask to Hask.● Maps objects:

Maps type to list of that type

● Maps functions: e.g. maps function f : String -> Float to [ ](f): [String] -> [Float] by [ ](f) = map f

In fact, [ ] is a monad from Hask to Hask.

unit : X -> [X]

join : [[X]] -> [X]

In fact, [ ] is a monad from Hask to Hask.

unit : X -> [X] x |-> [x]

join : [[X]] -> [X]

In fact, [ ] is a monad from Hask to Hask.

unit : X -> [X] x |-> [x]

join : [[X]] -> [X] list_of_lists |-> concat list_of_lists

In fact, [ ] is a monad from Hask to Hask.

unit : X -> [X] x |-> [x]

join : [[X]] -> [X] list_of_lists |-> concat list_of_lists

(Exercise: Check the monad laws)

Maybe is a functor from Hask to Hask.● Maps objects:

● Maps functions:

Maybe is a functor from Hask to Hask.● Maps objects:

e.g. maps String to Maybe String

● Maps functions:

Maybe is a functor from Hask to Hask.● Maps objects:

e.g. maps String to Maybe String

● Maps functions: given f : X -> Y define Maybe f : Maybe X -> Maybe Y Just x |-> Just (f x) Nothing |-> Nothing

In fact, Maybe is a monad from Hask to Hask.

unit : X -> Maybe X

join : Maybe (Maybe X) -> Maybe X

In fact, Maybe is a monad from Hask to Hask.

unit : X -> Maybe X x |-> Just x

join : Maybe (Maybe X) -> Maybe X

In fact, Maybe is a monad from Hask to Hask.

unit : X -> Maybe X x |-> Just x

join : Maybe (Maybe X) -> Maybe X Just (Just x) |-> Just x Just Nothing |-> Nothing Nothing |-> Nothing

In fact, Maybe is a monad from Hask to Hask.

unit : X -> Maybe X x |-> Just x

join : Maybe (Maybe X) -> Maybe X Just (Just x) |-> Just x Just Nothing |-> Nothing Nothing |-> Nothing

(Exercise: Check the monad laws)

Wait… definitions seem different?Part II (Haskell):Monad is a typeclassand you defineunit/returnbind/(>>=)

Part III (Categories):Monad is a functorand you definehow it maps functionsunitjoin

and check monad laws!

Answers● Haskell Monad is just a programming construct

○ Doesn’t require monad laws to compile○Typeclass could have a different name

Answers● Haskell Monad is just a programming construct

○ Doesn’t require monad laws to compile○Typeclass could have a different name

● Haskell also has a typeclass Functor○ Implements fmap ○But Monad instance need not be Functor instance

Answers● return, (>>=) are equivalent to unit, join, fmap by unit = return join x = x >>= id fmap f x = x >>=(return.f)

return = unit x >>= f = join(fmap f x)

Fin

BibliographyPiponi, Dan “You Could Have Invented Monads!”

http://blog.sigfpe.com/2006/08/you-could-have-invented-monads-and.html. August, 2006. Accessed June, 2015.

O’Sullivan, B., Stewart, D., Goerzen, J. Real World Haskell. O’Reilly Media, 2008. Print.

https://en.wikibooks.org/wiki/Haskell/Category_theory