Skip to content

Instantly share code, notes, and snippets.

@MichaelXavier
Created June 29, 2017 02:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MichaelXavier/5e3554416d48675c2fa05191d788629c to your computer and use it in GitHub Desktop.
Save MichaelXavier/5e3554416d48675c2fa05191d788629c to your computer and use it in GitHub Desktop.
A "stepped" free monad that can fast-forward to resume a job, saves state between steps
{-# OPTIONS_GHC -Wall -Werror #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE FlexibleContexts #-}
module Main
( main
) where
-------------------------------------------------------------------------------
import Control.Monad (void)
import Control.Monad.Free
import Control.Monad.Loops (iterateUntil)
-------------------------------------------------------------------------------
-- | This is an attempt at a Job monad that can do the following things:
-- 1. It can express a sequential list of steps.
-- 2. Given a StepNo it can "fast forward" to that step in order to resume.
-- 3. It automatically keeps the StepNo up-to-date between steps and "saves" the job.
main :: IO ()
main = runJobM initJob process
where
-- Start on step 3. should skip into the first loop at step 3, then loop 2 more times, then run step 4
initJob = Job Three 0
-------------------------------------------------------------------------------
-- Free Monad
-------------------------------------------------------------------------------
data StepNo = One
| Two
| Three
| Four
deriving (Show, Eq)
data Job = Job
{ stepNo :: StepNo
, loops :: Int
} deriving (Show)
process :: JobM IO ()
process = void $ do
_ <- step One one
_ <- iterateUntil loopedThrice $ do
_ <- step Two two
step Three three
step Four four
where
loopedThrice = (>= 3) . loops
one j = j <$ putStrLn "one"
two j = j <$ putStrLn "two"
three j = do
putStrLn "three"
pure j { loops = succ (loops j)}
four j = j <$ putStrLn "four"
data JobF m next = Step StepNo (Job -> m Job) (Job -> next) deriving (Functor)
type JobM m = Free (JobF m)
step :: StepNo -> (Job -> m Job) -> JobM m Job
step sn f = liftF (Step sn f id)
-------------------------------------------------------------------------------
-- Interpreter
-------------------------------------------------------------------------------
runJobM :: Job -> JobM IO () -> IO ()
runJobM _ (Pure a) = pure a
runJobM j1 (Free (Step sn f next)) = do
if stepNo j1 == sn
then do
j2 <- f j1
let j3 = case getStep (next j2) of
Nothing -> j2
Just ns -> j2 { stepNo = ns}
save j3
runJobM j3 (next j3)
else fastForward
where
fastForward = runJobM j1 (next j1)
getStep (Free (Step x _ _)) = Just x
getStep (Pure _) = Nothing
save j = putStrLn ("Save: " ++ show j)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment