-- | Run a retryable block.
-- withRetry :: MonadFree (RetryF m) m =>
--              (RetryT m) m a  -- ^ Computation to retry.
--           -> m a      -- ^ Computation that retries until succeeds.

-- | We can run a retriable program in any MonadIO.
runRetryT :: MonadIO m => (RetryT m) m a -> m a
runRetryT = iterT run
  where
    run :: MonadIO m => (RetryF m) (m a) -> m a

    run (Output s next) = do
      liftIO $ putStrLn s
      next

    run (Input next) = do
      s <- liftIO getLine
      case readMaybe s of
        Just x  -> next x
        Nothing -> fail "invalid input"

    run (WithRetry block next) = do
      -- Here we use
      -- runRetryT :: MonadIO m => (RetryT m) m a -> MaybeT (m a)
      -- to control failure with MaybeT.
      -- We repeatedly run retriable block until we get it to work.
      --Just x <- runMaybeT . F.msum $ repeat (runRetryT block)
      --
      -- XXX dhess: note that the actual retry mechanism is disabled.
      -- Because RetryT is a transformer, the monad which it wraps
      -- may perform side effects in the retried block; how do
      -- we un-do those effects when we retry, or how could we
      -- prevent the monad from performing side effects in the first
      -- place?
      --
      -- One way to do this might be to only wrap STM, so that the
      -- transaction could be rolled back upon failure. Anyway, the
      -- point of this DSL is only to make a 'with'-style command
      -- available, such that the interpreter could be run within the
      -- interpreter. Therefore, we don't address the larger problem
      -- of retrying with side effects and we simply punt here.
      x <- runRetryT block
      next x

    run Retry = fail "forced retry"

-- | Sample program.
runRetryT :: MonadIO m => (RetryT m) m a -> m a
runRetryT = iterT run
  where
    run :: MonadIO m => (RetryF m) (m a) -> m a

    run (Output s next) = do
      liftIO $ putStrLn s
      next

    run (Input next) = do
      s <- liftIO getLine
      case readMaybe s of
        Just x  -> next x
        Nothing -> fail "invalid input"

    run (WithRetry block next) = do
      -- Here we use
      -- runRetryT :: MonadIO m => (RetryT m) m a -> MaybeT (m a)
      -- to control failure with MaybeT.
      -- We repeatedly run retriable block until we get it to work.
      --Just x <- runMaybeT . F.msum $ repeat (runRetryT block)
      --
      -- XXX dhess: note that the actual retry mechanism is disabled.
      -- Because RetryT is a transformer, the monad which it wraps
      -- may perform side effects in the retried block; how do
      -- we un-do those effects when we retry, or how could we
      -- prevent the monad from performing side effects in the first
      -- place?
      --
      -- One way to do this might be to only wrap STM, so that the
      -- transaction could be rolled back upon failure. Anyway, the
      -- point of this DSL is only to make a 'with'-style command
      -- available, such that the interpreter could be run within the
      -- interpreter. Therefore, we don't address the larger problem
      -- of retrying with side effects and we simply punt here.
      x <- runRetryT block
      next x

    run Retry = fail "forced retry"

-- | Sample program.
runRetryT = iterT run
  where
    run :: MonadIO m => (RetryF m) (m a) -> m a

    run (Output s next) = do
      liftIO $ putStrLn s
      next

    run (Input next) = do
      s <- liftIO getLine
      case readMaybe s of
        Just x  -> next x
        Nothing -> fail "invalid input"

    run (WithRetry block next) = do
      -- Here we use
      -- runRetryT :: MonadIO m => (RetryT m) m a -> MaybeT (m a)
      -- to control failure with MaybeT.
      -- We repeatedly run retriable block until we get it to work.
      --Just x <- runMaybeT . F.msum $ repeat (runRetryT block)
      --
      -- XXX dhess: note that the actual retry mechanism is disabled.
      -- Because RetryT is a transformer, the monad which it wraps
      -- may perform side effects in the retried block; how do
      -- we un-do those effects when we retry, or how could we
      -- prevent the monad from performing side effects in the first
      -- place?
      --
      -- One way to do this might be to only wrap STM, so that the
      -- transaction could be rolled back upon failure. Anyway, the
      -- point of this DSL is only to make a 'with'-style command
      -- available, such that the interpreter could be run within the
      -- interpreter. Therefore, we don't address the larger problem
      -- of retrying with side effects and we simply punt here.
      x <- runRetryT block
      next x

    run Retry = fail "forced retry"

-- | Sample program.