diff --git a/notes/l9/Table.hs b/notes/l9/Table.hs new file mode 100644 index 0000000..53e4cee --- /dev/null +++ b/notes/l9/Table.hs @@ -0,0 +1,21 @@ +import Control.Applicative +import Control.Monad.State +import qualified Data.Map as Map + +type Address = String + +data Number = N !(Map.Map Address Int) !Int + deriving (Show) + +renumber :: [(Address,Address)] -> [(Int,Int)] +renumber xs = evalState (mapM pair xs) (N Map.empty 0) + where pair (x,y) = (,) <$> number x <*> number y + +number :: Address -> State Number Int +number a = do + N m i <- get + case Map.lookup a m of + Just j -> return j + Nothing -> do let i' = i + 1 + put $! N (Map.insert a i m) i' + return i' diff --git a/notes/l9/monads.md b/notes/l9/monads.md index 5d47360..b4a8861 100644 --- a/notes/l9/monads.md +++ b/notes/l9/monads.md @@ -704,7 +704,7 @@ the fundep: class (Monad m) => MonadState s m {- ... -} ~~~~ -And suppose we were to try to typecheck these type signatures: +And if we were to try to typecheck these type signatures: ~~~~ {.haskell} modify' :: MonadState s m => (s -> (a,s)) -> m a @@ -713,5 +713,116 @@ guess :: RandomGen g => State g Double ~~~~ Without the fundep, the compiler would choke on these, because it has -no information to tell it that the `g` parameter to `State` is related -to the `s` parameter to `MonadState`. +no information to assure it that the `g` parameter to `State` is +related to the `s` parameter to `MonadState`. + + +# Using the state monad + +Suppose we're running a social network, and we want to know who is +connected to whom. + +To build a matrix of connections, we need to represent user addresses +as integer positions on each axis. + +~~~~ {.haskell} +import Control.Applicative +import Control.Monad.State +import qualified Data.Map as Map + +type Address = String + +data Number = N !(Map.Map Address Int) !Int + deriving (Show) +~~~~ + +The `Number` type is the state we'll use (and possibly modify) while +numbering. + + +# Getting started + +This is the top-level address-numbering function. + +~~~~ {.haskell} +renumber :: [(Address,Address)] -> [(Int,Int)] +renumber xs = evalState (mapM pair xs) (N Map.empty 0) + where pair (x,y) = (,) <$> number x <*> number y +~~~~ + +This depends on a few functions we haven't seen before. + +Monadic mapping: + +~~~~ {.haskell} +mapM :: Monad m => (a -> m b) -> [a] -> m [b] +~~~~ + +The second of the three "run me a state monad" functions: + +~~~~ {.haskell} +runState :: State s a -> s -> (a, s) +evalState :: State s a -> s -> a +execState :: State s a -> s -> s +~~~~ + +The super-useful `<$>` operator is nothing but shorthand for `fmap`. + +And finally, a use in the wild for the `<*>` operator! + + +# What about the number function? + +This is where the real work happens. + +If an address is already stored in the numbering map, our function +returns the number associated with the address. + +~~~~ {.haskell} +number :: Address -> State Number Int +number addr = do + N numMap highest <- get + case Map.lookup addr numMap of + Just j -> return j + Nothing -> do let highest' = highest + 1 + newMap = Map.insert addr highest numMap + put $! N newMap highest' + return highest' +~~~~ + +Otherwise, we increment the highest number seen, associate the +previous number with the address, store the modified state, and return +the number. + + +# The Reader monad + +Reader is another widely used monad, and in fact we've already seen a +form of it: + +~~~~ {.haskell} +((->) a) +~~~~ + +This is best understood in comparison to the state monad: + +* In `State`, every function got passed a piece of state that it could + transform. This lets us achieve shared mutable state. + +* In `Reader`, every function is passed a piece of state + that it is not allowed to change. This lets us achieve shared + *read-only* state. + +As an example of a piece of immutable data that we might want to +thread around all over the place, think "application configuration". + +The reader monad lets us hide the plumbing of passing that information +around. + + +# Reader vs reader + +The `Reader` type is defined in `Control.Monad.Reader`. + +The *only* difference between it and `((->) a)` is that `Reader` is a +`newtype` wrapper around `((->) a)`. diff --git a/www/notes/index.html b/www/notes/index.html index 7db0f57..855ae82 100644 --- a/www/notes/index.html +++ b/www/notes/index.html @@ -52,6 +52,11 @@

CS240h lecture notes

[slides, source] +
  • + Monads and more + [slides, + source] +
  • diff --git a/www/notes/monads-slides.html b/www/notes/monads-slides.html index f6cb41f..2a74de6 100644 --- a/www/notes/monads-slides.html +++ b/www/notes/monads-slides.html @@ -418,9 +418,62 @@

    A new guesser

    Why functional dependencies?

    Suppose we were to write a simpler multi-parameter type class, without the fundep:

    class (Monad m) => MonadState s m {- ... -}
    -

    And suppose we were to try to typecheck these type signatures:

    +

    And if we were to try to typecheck these type signatures:

    modify' :: MonadState s m => (s -> (a,s)) -> m a

    guess :: RandomGen g => State g Double
    -

    Without the fundep, the compiler would choke on these, because it has no information to tell it that the g parameter to State is related to the s parameter to MonadState.

    +

    Without the fundep, the compiler would choke on these, because it has no information to assure it that the g parameter to State is related to the s parameter to MonadState.

    + + +
    + +

    Using the state monad

    +

    Suppose we're running a social network, and we want to know who is connected to whom.

    +

    To build a matrix of connections, we need to represent user addresses as integer positions on each axis.

    +
    import Control.Applicative
    import Control.Monad.State
    import qualified Data.Map as Map

    type Address = String

    data Number = N !(Map.Map Address Int) !Int
    deriving (Show)
    +

    The Number type is the state we'll use (and possibly modify) while numbering.

    +
    + +
    + +

    Getting started

    +

    This is the top-level address-numbering function.

    +
    renumber :: [(Address,Address)] -> [(Int,Int)]
    renumber xs = evalState (mapM pair xs) (N Map.empty 0)
    where pair (x,y) = (,) <$> number x <*> number y
    +

    This depends on a few functions we haven't seen before.

    +

    Monadic mapping:

    +
    mapM :: Monad m => (a -> m b) -> [a] -> m [b]
    +

    The second of the three "run me a state monad" functions:

    +
    runState  :: State s a -> s -> (a, s)
    evalState :: State s a -> s -> a
    execState :: State s a -> s -> s
    +

    The super-useful <$> operator is nothing but shorthand for fmap.

    +

    And finally, a use in the wild for the <*> operator!

    +
    + +
    + +

    What about the number function?

    +

    This is where the real work happens.

    +

    If an address is already stored in the numbering map, our function returns the number associated with the address.

    +
    number :: Address -> State Number Int
    number addr = do
    N numMap highest <- get
    case Map.lookup addr numMap of
    Just j -> return j
    Nothing -> do let highest' = highest + 1
    newMap = Map.insert addr highest numMap
    put $! N newMap highest'
    return highest'
    +

    Otherwise, we increment the highest number seen, associate the previous number with the address, store the modified state, and return the number.

    +
    + +
    + +

    The Reader monad

    +

    Reader is another widely used monad, and in fact we've already seen a form of it:

    +
    ((->) a)
    +

    This is best understood in comparison to the state monad:

    + +

    As an example of a piece of immutable data that we might want to thread around all over the place, think "application configuration".

    +

    The reader monad lets us hide the plumbing of passing that information around.

    +
    + +
    + +

    Reader vs reader

    +

    The Reader type is defined in Control.Monad.Reader.

    +

    The only difference between it and ((->) a) is that Reader is a newtype wrapper around ((->) a).

    diff --git a/www/notes/monads.html b/www/notes/monads.html index f64f7a8..85dd9da 100644 --- a/www/notes/monads.html +++ b/www/notes/monads.html @@ -251,8 +251,41 @@

    A new guesser

    Why functional dependencies?

    Suppose we were to write a simpler multi-parameter type class, without the fundep:

    class (Monad m) => MonadState s m {- ... -}
    -

    And suppose we were to try to typecheck these type signatures:

    +

    And if we were to try to typecheck these type signatures:

    modify' :: MonadState s m => (s -> (a,s)) -> m a

    guess :: RandomGen g => State g Double
    -

    Without the fundep, the compiler would choke on these, because it has no information to tell it that the g parameter to State is related to the s parameter to MonadState.

    +

    Without the fundep, the compiler would choke on these, because it has no information to assure it that the g parameter to State is related to the s parameter to MonadState.

    +

    Using the state monad

    +

    Suppose we're running a social network, and we want to know who is connected to whom.

    +

    To build a matrix of connections, we need to represent user addresses as integer positions on each axis.

    +
    import Control.Applicative
    import Control.Monad.State
    import qualified Data.Map as Map

    type Address = String

    data Number = N !(Map.Map Address Int) !Int
    deriving (Show)
    +

    The Number type is the state we'll use (and possibly modify) while numbering.

    +

    Getting started

    +

    This is the top-level address-numbering function.

    +
    renumber :: [(Address,Address)] -> [(Int,Int)]
    renumber xs = evalState (mapM pair xs) (N Map.empty 0)
    where pair (x,y) = (,) <$> number x <*> number y
    +

    This depends on a few functions we haven't seen before.

    +

    Monadic mapping:

    +
    mapM :: Monad m => (a -> m b) -> [a] -> m [b]
    +

    The second of the three "run me a state monad" functions:

    +
    runState  :: State s a -> s -> (a, s)
    evalState :: State s a -> s -> a
    execState :: State s a -> s -> s
    +

    The super-useful <$> operator is nothing but shorthand for fmap.

    +

    And finally, a use in the wild for the <*> operator!

    +

    What about the number function?

    +

    This is where the real work happens.

    +

    If an address is already stored in the numbering map, our function returns the number associated with the address.

    +
    number :: Address -> State Number Int
    number addr = do
    N numMap highest <- get
    case Map.lookup addr numMap of
    Just j -> return j
    Nothing -> do let highest' = highest + 1
    newMap = Map.insert addr highest numMap
    put $! N newMap highest'
    return highest'
    +

    Otherwise, we increment the highest number seen, associate the previous number with the address, store the modified state, and return the number.

    +

    The Reader monad

    +

    Reader is another widely used monad, and in fact we've already seen a form of it:

    +
    ((->) a)
    +

    This is best understood in comparison to the state monad:

    + +

    As an example of a piece of immutable data that we might want to thread around all over the place, think "application configuration".

    +

    The reader monad lets us hide the plumbing of passing that information around.

    +

    Reader vs reader

    +

    The Reader type is defined in Control.Monad.Reader.

    +

    The only difference between it and ((->) a) is that Reader is a newtype wrapper around ((->) a).

    diff --git a/www/notes/monads.md b/www/notes/monads.md index 5d47360..b4a8861 100644 --- a/www/notes/monads.md +++ b/www/notes/monads.md @@ -704,7 +704,7 @@ the fundep: class (Monad m) => MonadState s m {- ... -} ~~~~ -And suppose we were to try to typecheck these type signatures: +And if we were to try to typecheck these type signatures: ~~~~ {.haskell} modify' :: MonadState s m => (s -> (a,s)) -> m a @@ -713,5 +713,116 @@ guess :: RandomGen g => State g Double ~~~~ Without the fundep, the compiler would choke on these, because it has -no information to tell it that the `g` parameter to `State` is related -to the `s` parameter to `MonadState`. +no information to assure it that the `g` parameter to `State` is +related to the `s` parameter to `MonadState`. + + +# Using the state monad + +Suppose we're running a social network, and we want to know who is +connected to whom. + +To build a matrix of connections, we need to represent user addresses +as integer positions on each axis. + +~~~~ {.haskell} +import Control.Applicative +import Control.Monad.State +import qualified Data.Map as Map + +type Address = String + +data Number = N !(Map.Map Address Int) !Int + deriving (Show) +~~~~ + +The `Number` type is the state we'll use (and possibly modify) while +numbering. + + +# Getting started + +This is the top-level address-numbering function. + +~~~~ {.haskell} +renumber :: [(Address,Address)] -> [(Int,Int)] +renumber xs = evalState (mapM pair xs) (N Map.empty 0) + where pair (x,y) = (,) <$> number x <*> number y +~~~~ + +This depends on a few functions we haven't seen before. + +Monadic mapping: + +~~~~ {.haskell} +mapM :: Monad m => (a -> m b) -> [a] -> m [b] +~~~~ + +The second of the three "run me a state monad" functions: + +~~~~ {.haskell} +runState :: State s a -> s -> (a, s) +evalState :: State s a -> s -> a +execState :: State s a -> s -> s +~~~~ + +The super-useful `<$>` operator is nothing but shorthand for `fmap`. + +And finally, a use in the wild for the `<*>` operator! + + +# What about the number function? + +This is where the real work happens. + +If an address is already stored in the numbering map, our function +returns the number associated with the address. + +~~~~ {.haskell} +number :: Address -> State Number Int +number addr = do + N numMap highest <- get + case Map.lookup addr numMap of + Just j -> return j + Nothing -> do let highest' = highest + 1 + newMap = Map.insert addr highest numMap + put $! N newMap highest' + return highest' +~~~~ + +Otherwise, we increment the highest number seen, associate the +previous number with the address, store the modified state, and return +the number. + + +# The Reader monad + +Reader is another widely used monad, and in fact we've already seen a +form of it: + +~~~~ {.haskell} +((->) a) +~~~~ + +This is best understood in comparison to the state monad: + +* In `State`, every function got passed a piece of state that it could + transform. This lets us achieve shared mutable state. + +* In `Reader`, every function is passed a piece of state + that it is not allowed to change. This lets us achieve shared + *read-only* state. + +As an example of a piece of immutable data that we might want to +thread around all over the place, think "application configuration". + +The reader monad lets us hide the plumbing of passing that information +around. + + +# Reader vs reader + +The `Reader` type is defined in `Control.Monad.Reader`. + +The *only* difference between it and `((->) a)` is that `Reader` is a +`newtype` wrapper around `((->) a)`. diff --git a/www/sched/index.html b/www/sched/index.html index f51a0a2..56fbfc1 100644 --- a/www/sched/index.html +++ b/www/sched/index.html @@ -106,7 +106,7 @@

    CS240h schedule

    -Monads & parsers (bos)
    +Monads and more (bos)
    diff --git a/www/sched/sched.html b/www/sched/sched.html index a9b4268..6f23f47 100644 --- a/www/sched/sched.html +++ b/www/sched/sched.html @@ -35,7 +35,7 @@ -Monads & parsers (bos)
    +Monads and more (bos)