Upload
lawrence-evans
View
767
Download
0
Embed Size (px)
Citation preview
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