Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

runT is silly; accumulating and simpler version desired #50

Open
treeowl opened this issue Jul 4, 2015 · 4 comments
Open

runT is silly; accumulating and simpler version desired #50

treeowl opened this issue Jul 4, 2015 · 4 comments

Comments

@treeowl
Copy link
Collaborator

treeowl commented Jul 4, 2015

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 is

runTS :: (Monad m, Semigroup b) => MachineT m k b -> m (Maybe b)

runT could be written in terms of runTS in a few different ways, either first mapping (:| []) over it and then reversing the result, or doing some trick or other with Endo; then at the end Maybe (NonEmpty a) gets turned into [a].

runTS itself can be defined in terms of fold1 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, return Nothing.

@treeowl
Copy link
Collaborator Author

treeowl commented Jul 4, 2015

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)

@treeowl
Copy link
Collaborator Author

treeowl commented Jul 4, 2015

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.

@treeowl
Copy link
Collaborator Author

treeowl commented Jul 4, 2015

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)

@treeowl
Copy link
Collaborator Author

treeowl commented Jul 7, 2015

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants