Open treeowl opened 9 years ago
Here's the gist of what I mean. You may be able to find a better approach.
-- Run a model until it either produces an
-- output or stops. If the model produces output,
-- both that output and the stepped model are produced.
runTFirstOutput :: Monad m => MachineT m k o -> m (Maybe (o, MachineT m k o))
runTFirstOutput (MachineT m) = do
res <- m
case res of
Stop -> return Nothing
Yield o r -> return $ Just (o, r)
Await _ _ r -> runTFirstOutput r
-- Run a model until it either produces output or stops.
-- If the model produces output, only the output is revealed;
-- the model cannot be resumed.
runTFirst :: Monad m => MachineT m k o -> m (Maybe o)
runTFirst = liftM (fmap fst) . runTFirstOutput
-- Run a model producing elements in some semigroup; accumulate
-- them.
runTS :: (Semigroup o, Monad m) => MachineT m k o -> m (Maybe o)
runTS m = runTFirst (m ~> fold1 (flip (<>)))
-- This could also be written to use endomorphism stuff. Whatever.
runT :: Monad m => MachineT m k o -> m [o]
runT m = runTS (fmap (: []) m) >>= maybe (return []) (return . reverse)
Actually, there should probably be a separate fold operation in between, specialized to deal with folding a semigroup. It should also be possible to write a more foldl
-style version of runT
, which I didn't even think about, but it's a little uglier.
OK, this seems a bit better developed:
-- Convert a MachineT to a SourceT by blocking off its input.
machineTtoSourceT :: forall m k o . Monad m => MachineT m k o -> SourceT m o
machineTtoSourceT m = construct $ go m
where
go mach = do
res <- lift $ runMachineT mach
case res of
Stop -> stop
Yield o r -> yield o >> go r
Await _ _ r -> go r
-- The exposed polymorphism of SourceT sometimes leads to problems
-- with impredicativity.This version uses Void instead.
type AltSourceT m = MachineT m (Const Void)
machineTtoAltSourceT :: forall m k o . Monad m => MachineT m k o -> AltSourceT m o
machineTtoAltSourceT = machineTtoSourceT
-- Every SourceT is automatically an AltSourceT
sourceTtoAltSourceT :: SourceT m o -> AltSourceT m o
sourceTtoAltSourceT m = m
-- An AltSourceT can't possibly await, so it can be converted to a SourceT
altSourceTtoSourceT :: Monad m => AltSourceT m o -> SourceT m o
altSourceTtoSourceT m = construct $ go m
where
go mach = do
res <- lift $ runMachineT mach
case res of
Stop -> stop
Yield o r -> yield o >> go r
Await _ (Const ab) _ -> absurd ab -- Unreachable
-- Run an AltSourceT until it either produces an
-- output or stops. If the model produces output,
-- both that output and the stepped model are produced.
runAltSourceTFirstOutput :: forall m o . Monad m => AltSourceT m o -> m (Maybe (o, AltSourceT m o))
runAltSourceTFirstOutput m = do
res <- runMachineT m
case res of
Stop -> return Nothing
Yield o r -> return $ Just (o, r)
Await _ (Const ab) _ -> absurd ab -- Unreachable
-- Run a model without input until it either produces an
-- output or stops. If the model produces output,
-- both that output and the stepped model are produced.
-- The stepped machine does not accept input. Note that
-- it is impossible to use SourceT here, at least directly,
-- as that would be impredicative.
runTFirstOutput' :: Monad m => MachineT m k o -> m (Maybe (o, AltSourceT m o))
runTFirstOutput' = runAltSourceTFirstOutput . machineTtoAltSourceT
-- Run a model without input until it either produces an
-- output or stops. If the model produces output,
-- both that output and the stepped model are produced.
-- The stepped machine may accept new input, which may or
-- may not make sense.
runTFirstOutput :: Monad m => MachineT m k o -> m (Maybe (o, MachineT m k o))
runTFirstOutput m = do
res <- runMachineT m
case res of
Stop -> return Nothing
Yield o r -> return $ Just (o, r)
Await _ _ r -> runTFirstOutput r
-- Run a model until it either produces output or stops.
-- If the model produces output, only the output is revealed;
-- the model cannot be resumed.
runTFirst :: Monad m => MachineT m k o -> m (Maybe o)
runTFirst = liftM (fmap fst) . runTFirstOutput'
-- Run a model producing elements in some semigroup; accumulate
-- them.
runTS :: (Semigroup o, Monad m) => MachineT m k o -> m (Maybe o)
runTS m = runTFirst (m ~> fold1 (flip (<>)))
runT :: Monad m => MachineT m k o -> m [o]
runT m = runTS (fmap (: []) m) >>= maybe (return []) (return . reverse)
Bah. The pretty idea of converting a MachineT
to a SourceT
and then running the SourceT
doesn't seem to optimize away as I hoped. The simpler original proposal is therefore more practical. I'm not sure if the flip
should be there or not. I'm leaning toward not.
It may be too late to replace it, but
runT
doesn't seem to be good for much other than debugging. I suspect the most sensible general purpose version isrunT
could be written in terms ofrunTS
in a few different ways, either first mapping(:| [])
over it and then reversing the result, or doing some trick or other withEndo
; then at the endMaybe (NonEmpty a)
gets turned into[a]
.runTS
itself can be defined in terms offold1
and an even more basic machine runner that runs a machine until it produces an output, then stop it. Return that output; if the machine stopped with no output, returnNothing
.