Typeclassopedia
Ack
- This is my notes from reading Typeclassopedia from HaskellWiki
- Code
Functor
Container
Def
class Functor f where
fmap :: (a -> b) -> f a -> f b
Laws
fmap id = id
fmap (g . h) = (fmap g) . (fmap h)
Instances
- []
- Maybe
- Either
- ((,) e)
- ((->) e)
- IO
Applicative
encapsulates certain sorts of “effectful” computations in a functionally pure way, and encourages an “applicative” programming style
Def
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
Laws
-- identity
pure id <*> v = v
-- Homomorphism
pure f <*> pure x = pure (f x)
-- Interchange
u <*> pure y = pure ($ y) <*> u
-- Composition
u <*> (v <*> w) = pure (.) <*> u <*> v <*> w
-- Relate to Functor
fmap g x = pure g <*> x
Considered as left-to-right rewrite rules, the homomorphism, interchange, and composition laws actually constitute an algorithm for transforming any expression using pure and (<>) into a canonical form with only a single use of pure at the very beginning and only left-nested occurrences of (<>). Composition allows reassociating (<*>); interchange allows moving occurrences of pure leftwards; and homomorphism allows collapsing multiple adjacent occurrences of pure into one.
Instances
Most of the standard types which are instances of Functor are also instances of Applicative.
- Maybe
- []
- IO
- ((,) a)
- Const
- WrappedMonad, WrappedArrow
Intuition
Transformation of idealized notation \([[g \, x_1 \, x_2 \, \cdots \, x_n]]\) into haskell, with \(g: t_1 -> t_2 -> \cdots -> t_n\) and \(x_i : f t_i\).
Application falls short if only uses fmap. pure allows embedding “non-effectful” arguments in the middle of an idiomatic application.
g :: a -> b -> c -> d
x1 :: f a
x2 :: b
x3 :: f c
g <$> x1 <*> pure x2 <*> x3
Alternative formulation1
class Functor f => Monoidal f where
unit :: f ()
(**) :: f a -> f b -> f (a,b)
- left identity
- right identity
- associativity
- naturality
Other Combinators
u *> v = pure (const id) <*> u <*> v
u <* v = pure const <*> u <*> v
(<**>) = flip (<*>)
(<$) :: Functor f => a -> f b -> f a
(<$) = fmap . const
liftA ..
Monad
Def
class Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
(>>) :: m a -> m b -> m b
m >> n = m >>= \_ -> n
fail :: String -> m a
Laws
return a >>= k = k a
m >>= return = m
m >>= (\x -> k x >>= h) = (m >>= k) >>= h
fmap f xs = xs >>= return . f = liftM f xs
join . fmap join = join . join
join . fmap return = join . return = id
return . f = fmap f . return
join . fmap (fmap f) = fmap f . join
Alternative formulation of the laws are available with (>=>) operator that composes two functions of type a -> m b and b -> m c
Monad Reading List
- State
- Reader
- Writer
- Cont
Monad Transformers
Combine two monads into one will fail but some monads can be combined into one. Monad transformers can add a particular capability/feature/effect to any existing monad
Standard monad transformers
- IdentityT
- StateT
- ReaderT
- WriterT
- RWST
- MaybeT
- ErrorT
- ListT
- ContT
Def
class MonadTrans t where
lift :: Monad m => m a -> t m a
Allows arbitrary base monad computations to be “lifted” into computations in the transformed monad t m.
Laws
lift . return = return
lift (m >>= f) = lift m >>= (lift . f)
Transformer type classes and “capability” style
provided by mtl package * use put/get for different monad stack * flexibility to switch between different concrete monad stack
Composing monads
The composition of two monads isn’t always a monad.
join :: m (n (m (n a))) -> m (n a)
-- No general solution, but one situation is if n distribute over m
distrib :: n (m a) -> m (n a)
MonadFix(reading)
Semigroup
Def
set \(S\) together with a binary operator \(\oplus\) which combines elements from \(S\). operator required to be associative.
class Semigroup a where
(<>) :: a -> a -> a
sconcat :: NonEmpty a -> a
sconcat = sconcat (a :| as) = go a as where
go b (c:cs) = b <> go c cs
go b [] = b
times1p :: Whole n => n -> a -> a
times1p = ...
-- Equivalent to sconcat . replicate n
Laws
\((<>)\) must be associative
Monoid
Def
Semigroup plus special element \(e\) for which the binary operation is the identity.
class Monoid a where
mempty :: a
mappend :: a -> a -> a
-- (<>) is alias for mappend
mconcat :: [a] -> a
mconcat = foldr mappend mempty
Laws
mempty `mappend` x = x
x `mappend` mempty = x
(x `mappend` y) ` mappend` z = x `mappend` (y `mappend` z)
Instances
Defined in Data.Monoid
- [a]
- Sum / Product; for numbers
- Any / All; for bools
- First / Last; for maybes
- Endo a; for a -> a, monoid under composition
- lift monoid instances to that with additional structure
- Ordering is monoid, mempty = EQ
- Map / Set / Sequence
- Also plays a key role in Foldable
-- Monoid also used to enable several other type class instances. if Monad -> Writer class.
instance Monoid e => Applicative ((,) e) where
pure x = (mempty, x)
(u, f) <*> (v, x) = (u `mappend` v, f x)
Other monoidal classes
Alternative, for Applicative functors which also have a monoidal structure. ~ {.haskell} class Applicative f => Alternative f where empty :: f a (<|>) :: f a -> f a -> f a ~
MonadPlus, for Monads ~ {.haskell} class Monad m => MonadPlus m where mzero :: m a mplus :: m a -> m a -> m a – Add “Choice and Failure”, satisfy mzero >>= f = mzero v >> mzero = mzero – mzero denotes failure ~
What is ArrowZero & ArrowPlus?
Foldable
Def
class Foldable t where
fold :: Monoid m => t m -> m
foldMap :: Monoid m => (a -> m) -> t a -> m
foldr :: (a -> b -> b) -> b -> t a -> b
foldl :: (a -> b -> a) -> a -> t b -> a
foldr1 :: (a -> a -> a) -> t a -> a
foldl1 :: (a -> a -> a) -> t a -> a
-- Only foldMap or foldr needs to be provided
Instances
Containers: [], Tree, Maybe, Array, Map, Set, Tree, Sequence
Derived folds
using foldMap to compute the size of container or compute a list of elements satisfying a predicate…
Foldable actually isn’t
fold summarize only one level of structure where as Catamorphism2 can summarize an entire recursive structure.
Foldable allows only the left-right order of elements within a structure not the actual structure. => every use of Foldable -> in terms of toList3
Traversable
Def
class (Functor t, Foldable t) => Traversable t where
traverse :: Applicative f => (a -> f b) -> t a -> f (t b)
sequenceA :: Applicative f => t (f a) -> f (t a)
mapM :: Monad m => (a -> m b) -> t a -> m (t b)
sequence :: Monad m => t (m a) -> m (t a)
-- Note that every monad is also applicative
-- And any Traversable is also Functor and Foldable
Intuition: view Traversable as a generalization of Functor. Traverse is an “effectful fmap”.
Traversable and Functor instances for any type is almost identical: normal function application v.s. (<$>) (<>)* in Applicative context
- []
- Maybe
- Map
- Tree
- Sequence
Laws
traverse Identity = Identity
-- Data.Functor.Identity
-- No arbitrary effects
traverse (Compose . fmap g . f) = Compose . fmap (traverse g) . traverse f
-- Data.Functor.Compose (Composition of two functors)
-- How two traverse can be collapsed into one
Category
Def
Generalize the notion of function composition into general “morphisms”
class Category cat where
id :: cat a a
(.) :: cat b c -> cat a b -> cat a c
-- kind: Category :: * -> * -> *
-- (->) as Category
newtype Kleisli m a b = Kleisli { runKleisli :: a -> m b }
instance Monad m => Category (Kleisli m) where
id = Kleisli return
Kleisli g . Kleisli h = Kleisli (h >=> g)
Laws
- id is the identity of (.)
- (.) is associative
Arrow
Def
class Category arr => Arrow arr where
arr :: (b -> c) -> (b `arr` c)
first :: (b `arr` c) -> ((b, d) `arr` (c, d))
second :: (b `arr` c) -> ((d, b) `arr` (d, c))
(***) :: (b `arr` c) -> (b' `arr` c') -> ((b, b') `arr` (c, c'))
(&&&) :: (b `arr` c) -> (b `arr` c') -> (b `arr` (c, c'))
-- Category -> id & composition
-- arr & first needs to be defined
Intuition
- arr takes any function b -> c and turns it into a generalized arrow b
arr
c. - first turns any arrow from b to c into an arrow from (b,d) to (c,d)
- second defined by swap let arr a b = first f in arr (swap a) (swap b)
- (**)* parallel composition of arrows
- (&&&) fanout composition of arrows
Instances
- (->)
- Kleisli turns any function of type a -> m b into arrow for any Monad m
Laws4
arr id = id
arr (h . g) = arr g >>> arr h
first (arr g) = arr (g *** id)
first (g >>> h) = first g >>> first h
first g >>> arr (id *** h) = arr (id *** h) >>> first g
first g >>> arr fst = arr fst >>> g
first (first g) >>> arr assoc = arr assoc >>> first g
-- assoc ((x,y),z) = (x,(y,z))
ArrowChoice
Provide the ability to choose among a finite number of predefined execution paths based on the intermediate results.
class Arrow arr => ArrowChoice arr where
left :: (b `arr` c) -> (Either b d `arr` Either c d)
right :: (b `arr` c) -> (Either d b `arr` Either d c)
(+++) :: (b `arr` c) -> (b' `arr` c') -> (Either b b' `arr` Either c c')
(|||) :: (b `arr` d) -> (c `arr` d) -> (Either b c `arr` d)
- left and right resembles that of 1st & 2nd
- (+++) “multiplexing”
- (|||) “fanin”
ArrowApply
Allows to compute an arrow from intermediate results, and use this computed arrow to continue the computation.
class Arrow arr => ArrowApply arr where
app :: (b `arr` c, b) `arr` c
Define instance for Kleisli
ArrowLoop
Describes arrows that can use recursion to compute results, and to desugar the rec construct in arrow notation.
class Arrow a => ArrowLoop a where
loop :: a (b, d) (c, d) -> a b c
trace :: ((b,d) -> (c,d)) -> b -> c
trace f b = let (c,d) = f (b,d) in c
loop is a generalization of the trace function. laziness at work for g.
Arrow notation
Allows names to be assigned to intermediate results.
Paterson’s paper
Comonad
Categorical dual of Monad, with all the function arrows flipped.
class Functor w => Comonad w where
extract :: w a -> a
duplicate :: w a -> w (w a)
duplicate = extend id
extend :: (w a -> b) -> w a -> w b
extend f = fmap f . duplicate