Continuation monad for a yield/await function in Haskell

前端 未结 2 1723
北荒
北荒 2020-12-20 21:52

I want to create an automata type with a type like this:

newtype Auto i o = Auto {runAuto :: i -> (o, Auto i o)}

I know this is the type

2条回答
  •  半阙折子戏
    2020-12-20 22:04

    You could extend your automaton in the spirit of Conduit, that is, allow it to exit and return a value on finitely many inputs:

    data Auto i o a
        = Step (i -> (o, Auto i o a))
        | End a
    

    Then you can define a monad instance that concatenates two automata using >>=: When the first one finishes, the second one continues.

    The good news is that you don't need to implement it yourself. Returning a value or nesting using a functor is exactly what the free monad does (see its haddock docs). So let's define

    {-# LANGUAGE DeriveFunctor #-}
    import Control.Monad.Free
    import Data.Void
    
    -- | A functor describing one step of the automaton
    newtype AutoF i o t = AutoF (i -> (o, t))
      deriving (Functor)
    

    Then the original Auto type can be defined just as an alias:

    type Auto i o = Free (AutoF i o)
    

    This automatically gives you all the instances of Free, and you can also define your original functions:

    -- | If @a@ is 'Void', the machine runs forever:
    runAuto :: Auto i o Void -> i -> (o, Auto i o Void)
    runAuto (Pure v)  _         = absurd v
    runAuto (Free (AutoF f)) i  = f i
    
    yield :: o -> Auto i o ()
    yield x = liftF (AutoF $ \_ -> (x, ()))
    

    Note that using the same functor with FreeT you get the corresponding monad transformer:

    import Control.Monad.Trans.Free
    
    type AutoT i o = FreeT (AutoF i o)
    
    yieldT :: (Monad m) => o -> AutoT i o m ()
    yieldT x = liftF (AutoF $ \_ -> (x, ()))
    
    ...
    

提交回复
热议问题