Proposal: add replay function to Control.Monad.Cont.Class
Alexandre Esteves
alexandre.fmp.esteves at gmail.com
Sun Mar 7 03:25:16 UTC 2021
I'd like to propose adding the following function (method?) to
Control.Monad.Cont.Class, possibly with another name:
replay :: MonadCont m => m (m a)
replay = callCC $ pure . fix
Using this in a do-notation block allows one to bind a name to the
sub-block that starts immediately after.
I reached for continuations to try to get this behavior for use with
recursive flows without disrupting the reading of the main forward flow and
found how to do it in https://jsdw.me/posts/haskell-cont-monad/ under the
name "goto".
While "goto" is as familiar as a name can be, I feel "replay" more
accurately conveys, that you can only 'go back', by stating what actually
is happening - that a sub-block we're currently evaluating is replayed from
its beginning.
As a motivating example, here's the same recursive IO flow written in 3
ways - one with replay, one with fix, and one with where clauses.
{-# LANGUAGE LambdaCase #-}
import Control.Monad.Cont.Class (MonadCont(callCC))
import Control.Monad.IO.Class (MonadIO, liftIO)
import Control.Monad.Trans.Cont (evalContT)
import Data.Function (fix)
import Text.Read (readMaybe)
replay :: MonadCont m => m (m a)
replay = callCC $ pure . fix
prompt :: MonadIO m => String -> m String
prompt t = liftIO $ do
putStrLn t
putStr "> "
getLine
flowContT :: IO ()
flowContT = evalContT $ do
liftIO $ putStrLn "Welcome to the totally not contrived game"
numberPromptStep <- replay
readMaybe <$> prompt "Pick a number" >>= \case
Nothing -> liftIO (putStrLn "Not a number") *> numberPromptStep
Just n -> liftIO $ putStrLn $ "You picked " <> show (n :: Int)
exitPromptStep <- replay
prompt "Stop? y/n" >>= \case
"y" -> pure ()
"n" -> numberPromptStep
_ -> liftIO (putStrLn "Invalid choice") *> exitPromptStep
flowFix :: IO ()
flowFix = do
putStrLn "Welcome to the totally not contrived game"
fix $ \numberPromptStep -> do
readMaybe <$> prompt "Pick a number" >>= \case
Nothing -> liftIO (putStrLn "Not a number") *> numberPromptStep
Just n -> liftIO $ putStrLn $ "You picked " <> show (n :: Int)
fix $ \exitPromptStep -> do
prompt "Stop? y/n" >>= \case
"y" -> pure ()
"n" -> numberPromptStep
_ -> liftIO (putStrLn "Invalid choice") *> exitPromptStep
flowWhere :: IO ()
flowWhere = do
putStrLn "Welcome to the totally not contrived game"
numberPromptStep
where
numberPromptStep = do
readMaybe <$> prompt "Pick a number" >>= \case
Nothing -> liftIO (putStrLn "Not a number") *> numberPromptStep
Just n -> do
liftIO $ putStrLn $ "You picked " <> show (n :: Int)
exitPromptStep
exitPromptStep = do
prompt "Stop? y/n" >>= \case
"y" -> pure ()
"n" -> numberPromptStep
_ -> liftIO (putStrLn "Invalid choice") *> exitPromptStep
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/libraries/attachments/20210307/1d820523/attachment.html>
More information about the Libraries
mailing list