What is a monad?

前端 未结 30 1599
太阳男子
太阳男子 2020-11-30 13:39

Having briefly looked at Haskell recently, what would be a brief, succinct, practical explanation as to what a monad essentially is?

I have found most expla

30条回答
  •  借酒劲吻你
    2020-11-30 14:17

    tl;dr

    {-# LANGUAGE InstanceSigs #-}
    
    newtype Id t = Id t
    
    instance Monad Id where
       return :: t -> Id t
       return = Id
    
       (=<<) :: (a -> Id b) -> Id a -> Id b
       f =<< (Id x) = f x
    

    Prologue

    The application operator $ of functions

    forall a b. a -> b
    

    is canonically defined

    ($) :: (a -> b) -> a -> b
    f $ x = f x
    
    infixr 0 $
    

    in terms of Haskell-primitive function application f x (infixl 10).

    Composition . is defined in terms of $ as

    (.) :: (b -> c) -> (a -> b) -> (a -> c)
    f . g = \ x -> f $ g x
    
    infixr 9 .
    

    and satisfies the equivalences forall f g h.

         f . id  =  f            :: c -> d   Right identity
         id . g  =  g            :: b -> c   Left identity
    (f . g) . h  =  f . (g . h)  :: a -> d   Associativity
    

    . is associative, and id is its right and left identity.

    The Kleisli triple

    In programming, a monad is a functor type constructor with an instance of the monad type class. There are several equivalent variants of definition and implementation, each carrying slightly different intuitions about the monad abstraction.

    A functor is a type constructor f of kind * -> * with an instance of the functor type class.

    {-# LANGUAGE KindSignatures #-}
    
    class Functor (f :: * -> *) where
       map :: (a -> b) -> (f a -> f b)
    

    In addition to following statically enforced type protocol, instances of the functor type class must obey the algebraic functor laws forall f g.

           map id  =  id           :: f t -> f t   Identity
    map f . map g  =  map (f . g)  :: f a -> f c   Composition / short cut fusion
    

    Functor computations have the type

    forall f t. Functor f => f t
    

    A computation c r consists in results r within context c.

    Unary monadic functions or Kleisli arrows have the type

    forall m a b. Functor m => a -> m b
    

    Kleisi arrows are functions that take one argument a and return a monadic computation m b.

    Monads are canonically defined in terms of the Kleisli triple forall m. Functor m =>

    (m, return, (=<<))
    

    implemented as the type class

    class Functor m => Monad m where
       return :: t -> m t
       (=<<)  :: (a -> m b) -> m a -> m b
    
    infixr 1 =<<
    

    The Kleisli identity return is a Kleisli arrow that promotes a value t into monadic context m. Extension or Kleisli application =<< applies a Kleisli arrow a -> m b to results of a computation m a.

    Kleisli composition <=< is defined in terms of extension as

    (<=<) :: Monad m => (b -> m c) -> (a -> m b) -> (a -> m c)
    f <=< g = \ x -> f =<< g x
    
    infixr 1 <=<
    

    <=< composes two Kleisli arrows, applying the left arrow to results of the right arrow’s application.

    Instances of the monad type class must obey the monad laws, most elegantly stated in terms of Kleisli composition: forall f g h.

       f <=< return  =  f                :: c -> m d   Right identity
       return <=< g  =  g                :: b -> m c   Left identity
    (f <=< g) <=< h  =  f <=< (g <=< h)  :: a -> m d   Associativity
    

    <=< is associative, and return is its right and left identity.

    Identity

    The identity type

    type Id t = t
    

    is the identity function on types

    Id :: * -> *
    

    Interpreted as a functor,

       return :: t -> Id t
    =      id :: t ->    t
    
        (=<<) :: (a -> Id b) -> Id a -> Id b
    =     ($) :: (a ->    b) ->    a ->    b
    
        (<=<) :: (b -> Id c) -> (a -> Id b) -> (a -> Id c)
    =     (.) :: (b ->    c) -> (a ->    b) -> (a ->    c)
    

    In canonical Haskell, the identity monad is defined

    newtype Id t = Id t
    
    instance Functor Id where
       map :: (a -> b) -> Id a -> Id b
       map f (Id x) = Id (f x)
    
    instance Monad Id where
       return :: t -> Id t
       return = Id
    
       (=<<) :: (a -> Id b) -> Id a -> Id b
       f =<< (Id x) = f x
    

    Option

    An option type

    data Maybe t = Nothing | Just t
    

    encodes computation Maybe t that not necessarily yields a result t, computation that may “fail”. The option monad is defined

    instance Functor Maybe where
       map :: (a -> b) -> (Maybe a -> Maybe b)
       map f (Just x) = Just (f x)
       map _ Nothing  = Nothing
    
    instance Monad Maybe where
       return :: t -> Maybe t
       return = Just
    
       (=<<) :: (a -> Maybe b) -> Maybe a -> Maybe b
       f =<< (Just x) = f x
       _ =<< Nothing  = Nothing
    

    a -> Maybe b is applied to a result only if Maybe a yields a result.

    newtype Nat = Nat Int
    

    The natural numbers can be encoded as those integers greater than or equal to zero.

    toNat :: Int -> Maybe Nat
    toNat i | i >= 0    = Just (Nat i)
            | otherwise = Nothing
    

    The natural numbers are not closed under subtraction.

    (-?) :: Nat -> Nat -> Maybe Nat
    (Nat n) -? (Nat m) = toNat (n - m)
    
    infixl 6 -?
    

    The option monad covers a basic form of exception handling.

    (-? 20) <=< toNat :: Int -> Maybe Nat
    

    List

    The list monad, over the list type

    data [] t = [] | t : [t]
    
    infixr 5 :
    

    and its additive monoid operation “append”

    (++) :: [t] -> [t] -> [t]
    (x : xs) ++ ys = x : xs ++ ys
    []       ++ ys = ys
    
    infixr 5 ++
    

    encodes nonlinear computation [t] yielding a natural amount 0, 1, ... of results t.

    instance Functor [] where
       map :: (a -> b) -> ([a] -> [b])
       map f (x : xs) = f x : map f xs
       map _ []       = []
    
    instance Monad [] where
       return :: t -> [t]
       return = (: [])
    
       (=<<) :: (a -> [b]) -> [a] -> [b]
       f =<< (x : xs) = f x ++ (f =<< xs)
       _ =<< []       = []
    

    Extension =<< concatenates ++ all lists [b] resulting from applications f x of a Kleisli arrow a -> [b] to elements of [a] into a single result list [b].

    Let the proper divisors of a positive integer n be

    divisors :: Integral t => t -> [t]
    divisors n = filter (`divides` n) [2 .. n - 1]
    
    divides :: Integral t => t -> t -> Bool
    (`divides` n) = (== 0) . (n `rem`)
    

    then

    forall n.  let { f = f <=< divisors } in f n   =   []
    

    In defining the monad type class, instead of extension =<<, the Haskell standard uses its flip, the bind operator >>=.

    class Applicative m => Monad m where
       (>>=) :: forall a b. m a -> (a -> m b) -> m b
    
       (>>) :: forall a b. m a -> m b -> m b
       m >> k = m >>= \ _ -> k
       {-# INLINE (>>) #-}
    
       return :: a -> m a
       return = pure
    

    For simplicity's sake, this explanation uses the type class hierarchy

    class              Functor f
    class Functor m => Monad m
    

    In Haskell, the current standard hierarchy is

    class                  Functor f
    class Functor p     => Applicative p
    class Applicative m => Monad m
    

    because not only is every monad a functor, but every applicative is a functor and every monad is an applicative, too.

    Using the list monad, the imperative pseudocode

    for a in (1, ..., 10)
       for b in (1, ..., 10)
          p <- a * b
          if even(p)
             yield p
    

    roughly translates to the do block,

    do a <- [1 .. 10]
       b <- [1 .. 10]
       let p = a * b
       guard (even p)
       return p
    

    the equivalent monad comprehension,

    [ p | a <- [1 .. 10], b <- [1 .. 10], let p = a * b, even p ]
    

    and the expression

    [1 .. 10] >>= (\ a ->
       [1 .. 10] >>= (\ b ->
          let p = a * b in
             guard (even p) >>       -- [ () | even p ] >>
                return p
          )
       )
    

    Do notation and monad comprehensions are syntactic sugar for nested bind expressions. The bind operator is used for local name binding of monadic results.

    let x = v in e    =   (\ x -> e)  $  v   =   v  &  (\ x -> e)
    do { r <- m; c }  =   (\ r -> c) =<< m   =   m >>= (\ r -> c)
    

    where

    (&) :: a -> (a -> b) -> b
    (&) = flip ($)
    
    infixl 0 &
    

    The guard function is defined

    guard :: Additive m => Bool -> m ()
    guard True  = return ()
    guard False = fail
    

    where the unit type or “empty tuple”

    data () = ()
    

    Additive monads that support choice and failure can be abstracted over using a type class

    class Monad m => Additive m where
       fail  :: m t
       (<|>) :: m t -> m t -> m t
    
    infixl 3 <|>
    
    instance Additive Maybe where
       fail = Nothing
    
       Nothing <|> m = m
       m       <|> _ = m
    
    instance Additive [] where
       fail = []
       (<|>) = (++)
    

    where fail and <|> form a monoid forall k l m.

         k <|> fail  =  k
         fail <|> l  =  l
    (k <|> l) <|> m  =  k <|> (l <|> m)
    

    and fail is the absorbing/annihilating zero element of additive monads

    _ =<< fail  =  fail
    

    If in

    guard (even p) >> return p
    

    even p is true, then the guard produces [()], and, by the definition of >>, the local constant function

    \ _ -> return p
    

    is applied to the result (). If false, then the guard produces the list monad’s fail ( [] ), which yields no result for a Kleisli arrow to be applied >> to, so this p is skipped over.

    State

    Infamously, monads are used to encode stateful computation.

    A state processor is a function

    forall st t. st -> (t, st)
    

    that transitions a state st and yields a result t. The state st can be anything. Nothing, flag, count, array, handle, machine, world.

    The type of state processors is usually called

    type State st t = st -> (t, st)
    

    The state processor monad is the kinded * -> * functor State st. Kleisli arrows of the state processor monad are functions

    forall st a b. a -> (State st) b
    

    In canonical Haskell, the lazy version of the state processor monad is defined

    newtype State st t = State { stateProc :: st -> (t, st) }
    
    instance Functor (State st) where
       map :: (a -> b) -> ((State st) a -> (State st) b)
       map f (State p) = State $ \ s0 -> let (x, s1) = p s0
                                         in  (f x, s1)
    
    instance Monad (State st) where
       return :: t -> (State st) t
       return x = State $ \ s -> (x, s)
    
       (=<<) :: (a -> (State st) b) -> (State st) a -> (State st) b
       f =<< (State p) = State $ \ s0 -> let (x, s1) = p s0
                                         in  stateProc (f x) s1
    

    A state processor is run by supplying an initial state:

    run :: State st t -> st -> (t, st)
    run = stateProc
    
    eval :: State st t -> st -> t
    eval = fst . run
    
    exec :: State st t -> st -> st
    exec = snd . run
    

    State access is provided by primitives get and put, methods of abstraction over stateful monads:

    {-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}
    
    class Monad m => Stateful m st | m -> st where
       get :: m st
       put :: st -> m ()
    

    m -> st declares a functional dependency of the state type st on the monad m; that a State t, for example, will determine the state type to be t uniquely.

    instance Stateful (State st) st where
       get :: State st st
       get = State $ \ s -> (s, s)
    
       put :: st -> State st ()
       put s = State $ \ _ -> ((), s)
    

    with the unit type used analogously to void in C.

    modify :: Stateful m st => (st -> st) -> m ()
    modify f = do
       s <- get
       put (f s)
    
    gets :: Stateful m st => (st -> t) -> m t
    gets f = do
       s <- get
       return (f s)
    

    gets is often used with record field accessors.

    The state monad equivalent of the variable threading

    let s0 = 34
        s1 = (+ 1) s0
        n = (* 12) s1
        s2 = (+ 7) s1
    in  (show n, s2)
    

    where s0 :: Int, is the equally referentially transparent, but infinitely more elegant and practical

    (flip run) 34
       (do
          modify (+ 1)
          n <- gets (* 12)
          modify (+ 7)
          return (show n)
       )
    

    modify (+ 1) is a computation of type State Int (), except for its effect equivalent to return ().

    (flip run) 34
       (modify (+ 1) >>
          gets (* 12) >>= (\ n ->
             modify (+ 7) >>
                return (show n)
          )
       )
    

    The monad law of associativity can be written in terms of >>= forall m f g.

    (m >>= f) >>= g  =  m >>= (\ x -> f x >>= g)
    

    or

    do {                 do {                   do {
       r1 <- do {           x <- m;                r0 <- m;
          r0 <- m;   =      do {            =      r1 <- f r0;
          f r0                 r1 <- f x;          g r1
       };                      g r1             }
       g r1                 }
    }                    }
    

    Like in expression-oriented programming (e.g. Rust), the last statement of a block represents its yield. The bind operator is sometimes called a “programmable semicolon”.

    Iteration control structure primitives from structured imperative programming are emulated monadically

    for :: Monad m => (a -> m b) -> [a] -> m ()
    for f = foldr ((>>) . f) (return ())
    
    while :: Monad m => m Bool -> m t -> m ()
    while c m = do
       b <- c
       if b then m >> while c m
            else return ()
    
    forever :: Monad m => m t
    forever m = m >> forever m
    

    Input/Output

    data World
    

    The I/O world state processor monad is a reconciliation of pure Haskell and the real world, of functional denotative and imperative operational semantics. A close analogue of the actual strict implementation:

    type IO t = World -> (t, World)
    

    Interaction is facilitated by impure primitives

    getChar         :: IO Char
    putChar         :: Char -> IO ()
    readFile        :: FilePath -> IO String
    writeFile       :: FilePath -> String -> IO ()
    hSetBuffering   :: Handle -> BufferMode -> IO ()
    hTell           :: Handle -> IO Integer
    . . .              . . .
    

    The impurity of code that uses IO primitives is permanently protocolized by the type system. Because purity is awesome, what happens in IO, stays in IO.

    unsafePerformIO :: IO t -> t
    

    Or, at least, should.

    The type signature of a Haskell program

    main :: IO ()
    main = putStrLn "Hello, World!"
    

    expands to

    World -> ((), World)
    

    A function that transforms a world.

    Epilogue

    The category whiches objects are Haskell types and whiches morphisms are functions between Haskell types is, “fast and loose”, the category Hask.

    A functor T is a mapping from a category C to a category D; for each object in C an object in D

    Tobj :  Obj(C) -> Obj(D)
       f :: *      -> *
    

    and for each morphism in C a morphism in D

    Tmor :  HomC(X, Y) -> HomD(Tobj(X), Tobj(Y))
     map :: (a -> b)   -> (f a -> f b)
    

    where X, Y are objects in C. HomC(X, Y) is the homomorphism class of all morphisms X -> Y in C. The functor must preserve morphism identity and composition, the “structure” of C, in D.

                        Tmor    Tobj
    
          T(id)  =  id        : T(X) -> T(X)   Identity
    T(f) . T(g)  =  T(f . g)  : T(X) -> T(Z)   Composition
    

    The Kleisli category of a category C is given by a Kleisli triple

    
    

    of an endofunctor

    T : C -> C
    

    (f), an identity morphism eta (return), and an extension operator * (=<<).

    Each Kleisli morphism in Hask

          f :  X -> T(Y)
          f :: a -> m b
    

    by the extension operator

       (_)* :  Hom(X, T(Y)) -> Hom(T(X), T(Y))
      (=<<) :: (a -> m b)   -> (m a -> m b)
    

    is given a morphism in Hask’s Kleisli category

         f* :  T(X) -> T(Y)
    (f =<<) :: m a  -> m b
    

    Composition in the Kleisli category .T is given in terms of extension

     f .T g  =  f* . g       :  X -> T(Z)
    f <=< g  =  (f =<<) . g  :: a -> m c
    

    and satisfies the category axioms

           eta .T g  =  g                :  Y -> T(Z)   Left identity
       return <=< g  =  g                :: b -> m c
    
           f .T eta  =  f                :  Z -> T(U)   Right identity
       f <=< return  =  f                :: c -> m d
    
      (f .T g) .T h  =  f .T (g .T h)    :  X -> T(U)   Associativity
    (f <=< g) <=< h  =  f <=< (g <=< h)  :: a -> m d
    

    which, applying the equivalence transformations

         eta .T g  =  g
         eta* . g  =  g               By definition of .T
         eta* . g  =  id . g          forall f.  id . f  =  f
             eta*  =  id              forall f g h.  f . h  =  g . h  ==>  f  =  g
    
    (f .T g) .T h  =  f .T (g .T h)
    (f* . g)* . h  =  f* . (g* . h)   By definition of .T
    (f* . g)* . h  =  f* . g* . h     . is associative
        (f* . g)*  =  f* . g*         forall f g h.  f . h  =  g . h  ==>  f  =  g
    

    in terms of extension are canonically given

                   eta*  =  id                 :  T(X) -> T(X)   Left identity
           (return =<<)  =  id                 :: m t -> m t
    
               f* . eta  =  f                  :  Z -> T(U)      Right identity
       (f =<<) . return  =  f                  :: c -> m d
    
              (f* . g)*  =  f* . g*            :  T(X) -> T(Z)   Associativity
    (((f =<<) . g) =<<)  =  (f =<<) . (g =<<)  :: m a -> m c
    

    Monads can also be defined in terms not of Kleislian extension, but a natural transformation mu, in programming called join. A monad is defined in terms of mu as a triple over a category C, of an endofunctor

         T :  C -> C
         f :: * -> *
    

    and two natural tranformations

       eta :  Id -> T
    return :: t  -> f t
    
        mu :  T . T   -> T
      join :: f (f t) -> f t
    

    satisfying the equivalences

           mu . T(mu)  =  mu . mu               :  T . T . T -> T . T   Associativity
      join . map join  =  join . join           :: f (f (f t)) -> f t
    
          mu . T(eta)  =  mu . eta       =  id  :  T -> T               Identity
    join . map return  =  join . return  =  id  :: f t -> f t
    

    The monad type class is then defined

    class Functor m => Monad m where
       return :: t -> m t
       join   :: m (m t) -> m t
    

    The canonical mu implementation of the option monad:

    instance Monad Maybe where
       return = Just
    
       join (Just m) = m
       join Nothing  = Nothing
    

    The concat function

    concat :: [[a]] -> [a]
    concat (x : xs) = x ++ concat xs
    concat []       = []
    

    is the join of the list monad.

    instance Monad [] where
       return :: t -> [t]
       return = (: [])
    
       (=<<) :: (a -> [b]) -> ([a] -> [b])
       (f =<<) = concat . map f
    

    Implementations of join can be translated from extension form using the equivalence

         mu  =  id*           :  T . T -> T
       join  =  (id =<<)      :: m (m t) -> m t
    

    The reverse translation from mu to extension form is given by

         f*  =  mu . T(f)     :  T(X) -> T(Y)
    (f =<<)  =  join . map f  :: m a -> m b
    

    • Philip Wadler: Monads for functional programming

    • Simon L Peyton Jones, Philip Wadler: Imperative functional programming

    • Jonathan M. D. Hill, Keith Clarke: An introduction to category theory, category theory monads, and their relationship to functional programming ´

    • Kleisli category

    • Eugenio Moggi: Notions of computation and monads

    • What a monad is not

    But why should a theory so abstract be of any use for programming?

    The answer is simple: as computer scientists, we value abstraction! When we design the interface to a software component, we want it to reveal as little as possible about the implementation. We want to be able to replace the implementation with many alternatives, many other ‘instances’ of the same ‘concept’. When we design a generic interface to many program libraries, it is even more important that the interface we choose have a variety of implementations. It is the generality of the monad concept which we value so highly, it is because category theory is so abstract that its concepts are so useful for programming.

    It is hardly suprising, then, that the generalisation of monads that we present below also has a close connection to category theory. But we stress that our purpose is very practical: it is not to ‘implement category theory’, it is to find a more general way to structure combinator libraries. It is simply our good fortune that mathematicians have already done much of the work for us!

    from Generalising Monads to Arrows by John Hughes

提交回复
热议问题