Monad with no wrapped value?

后端 未结 4 944
爱一瞬间的悲伤
爱一瞬间的悲伤 2020-12-30 06:02

Most of the monad explanations use examples where the monad wraps a value. E.g. Maybe a, where the a type variable is what\'s wrapped. But I\'m won

4条回答
  •  执笔经年
    2020-12-30 07:02

    TL;DR Monad without its wrapped value isn't very special and you get all the same power modeling it as a list.

    There's a thing known as the Free monad. It's useful because it in some sense is a good representer for all other monads---if you can understand the behavior of the Free monad in some circumstance you have a good insight into how Monads generally will behave there.

    It looks like this

    data Free f a = Pure a
                  | Free (f (Free f a))
    

    and whenever f is a Functor, Free f is a Monad

    instance Functor f => Monad (Free f) where
      return       = Pure
      Pure a >>= f = f a
      Free w >>= f = Free (fmap (>>= f) w)
    

    So what happens when a is always ()? We don't need the a parameter anymore

    data Freed f = Stop 
                 | Freed (f (Freed f))
    

    Clearly this cannot be a Monad anymore as it has the wrong kind (type of types).

    Monad f ===> f       :: * -> *
                 Freed f :: *
    

    But we can still define something like Monadic functionality onto it by getting rid of the a parts

    returned :: Freed f
    returned = Stop
    
    bound :: Functor f                          -- compare with the Monad definition
       => Freed f -> Freed f                    -- with all `a`s replaced by ()
       -> Freed f
    bound Stop k      = k                       Pure () >>= f = f ()
    bound (Freed w) k =                         Free w  >>= f =
      Freed (fmap (`bound` k) w)                  Free (fmap (>>= f) w)
    
    -- Also compare with (++)
    (++) []     ys = ys
    (++) (x:xs) ys = x : ((++) xs ys)
    

    Which looks to be (and is!) a Monoid.

    instance Functor f => Monoid (Freed f) where
      mempty  = returned
      mappend = bound
    

    And Monoids can be initially modeled by lists. We use the universal property of the list Monoid where if we have a function Monoid m => (a -> m) then we can turn a list [a] into an m.

    convert :: Monoid m => (a -> m) -> [a] -> m
    convert f = foldr mappend mempty . map f
    
    convertFreed :: Functor f => [f ()] -> Freed f
    convertFreed = convert go where
      go :: Functor f => f () -> Freed f
      go w = Freed (const Stop <$> w)
    

    So in the case of your robot, we can get away with just using a list of actions

    data Direction = Left | Right | Forward | Back
    data ActionF a = Move Direction Double a
                   | Rotate Double a
                   deriving ( Functor )
    
    -- and if we're using `ActionF ()` then we might as well do
    
    data Action = Move Direction Double
                | Rotate Double
    
    robotMovementScript = [ Move Left    10
                          , Move Forward 25
                          , Rotate       180
                          ]
    

    Now when we cast it to IO we're clearly converting this list of directions into a Monad and we can see that as taking our initial Monoid and sending it to Freed and then treating Freed f as Free f () and interpreting that as an initial Monad over the IO actions we want.

    But it's clear that if you're not making use of the "wrapped" values then you're not really making use of Monad structure. You might as well just have a list.

提交回复
热议问题