Skip to content

Instantly share code, notes, and snippets.

@ocharles
Last active December 11, 2015 19:59
Show Gist options
  • Save ocharles/556b35e3d16565cab457 to your computer and use it in GitHub Desktop.
Save ocharles/556b35e3d16565cab457 to your computer and use it in GitHub Desktop.
> import Control.Monad.Trans
> import Control.Monad.Trans.Iter
The completely iterative free monad transformer lets us capture non-termination as an effect. Furthermore, it's a monad *transformer*, so we can add non-termination on top of other effects.
A convenient combinator is
> untilSuccess :: Monad m => m (Maybe a) -> IterT m a
> untilSuccess f = maybe (delay (untilSuccess f)) return =<< lift f
Given a effectful computation that may fail, we repeatedly run it until it succeeds. Of course, just trying the same thing over and over again isn't guaranteed to terminate, so we work under `IterT` to capture the non-termination.
Now given a computation that may fail
> data TargetHit
> launchMissles :: IO (Maybe TargetHit)
> launchMissles = putStrLn "Launching..." >> return Nothing
we have a few choices.
If we're certain that a computation will succeed, and we're willing to wait indefinitely, we can just `retract`:
> ultimatelyLaunchMissles :: IO TargetHit
> ultimatelyLaunchMissles = retract (untilSuccess launchMissles)
However, our time is often limited - so we can retry a limited number of times:
> tryAndLaunchMissles :: IO (Maybe TargetHit)
> tryAndLaunchMissles = retract (cutoff 10 (untilSuccess launchMissles))
This will `launchMissles` at most 10 times, and give us back either `Nothing`, or the successful `TargetHit`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment