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
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, ()))
...