Skip to content

Commit 0d9cfd0

Browse files
authored
Merge pull request #87 from alexfmpe/cont-label
Add 'label' method to MonadCont
2 parents 046bd05 + 07b52b3 commit 0d9cfd0

File tree

2 files changed

+42
-1
lines changed

2 files changed

+42
-1
lines changed

Control/Monad/Cont.hs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ to understand and maintain.
5353
module Control.Monad.Cont (
5454
-- * MonadCont class
5555
MonadCont.MonadCont(..),
56+
MonadCont.label,
57+
MonadCont.label_,
58+
5659
-- * The Cont monad
5760
Cont.Cont,
5861
Cont.cont,
@@ -71,9 +74,12 @@ module Control.Monad.Cont (
7174

7275
-- * Example 2: Using @callCC@
7376
-- $callCCExample
74-
77+
7578
-- * Example 3: Using @ContT@ Monad Transformer
7679
-- $ContTExample
80+
81+
-- * Example 4: Using @label@
82+
-- $labelExample
7783
) where
7884

7985
import qualified Control.Monad.Cont.Class as MonadCont
@@ -165,3 +171,19 @@ and passes it to the continuation.
165171
and returning @IO ()@.
166172
Compare its signature to 'runContT' definition.
167173
-}
174+
175+
{-$labelExample
176+
177+
The early exit behavior of 'Control.Monad.Cont.Class.callCC' can be leveraged to produce other idioms:
178+
179+
> whatsYourNameLabel :: IO ()
180+
> whatsYourNameLabel = evalContT $ do
181+
> (beginning, attempts) <- label (0 :: Int)
182+
> liftIO $ putStrLn $ "Attempt #" <> show attempts
183+
> liftIO $ putStrLn $ "What's your name?"
184+
> name <- liftIO getLine
185+
> when (null name) $ beginning (attempts + 1)
186+
> liftIO $ putStrLn $ "Welcome, " ++ name ++ "!"
187+
188+
Calling @beggining@ will interrupt execution of the block, skipping the welcome message, which will be printed only once at the very end of the loop.
189+
-}

Control/Monad/Cont/Class.hs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,11 @@ to understand and maintain.
5656

5757
module Control.Monad.Cont.Class (
5858
MonadCont(..),
59+
label,
60+
label_,
5961
) where
6062

63+
import Control.Monad.Fix (fix)
6164
import Control.Monad.Trans.Cont (ContT)
6265
import qualified Control.Monad.Trans.Cont as ContT
6366
import Control.Monad.Trans.Except (ExceptT)
@@ -152,3 +155,19 @@ instance
152155
, MonadCont m
153156
) => MonadCont (AccumT w m) where
154157
callCC = Accum.liftCallCC callCC
158+
159+
-- | Introduces a recursive binding to the continuation.
160+
-- Due to the use of @callCC@, calling the continuation will interrupt execution
161+
-- of the current block creating an effect similar to goto/setjmp in C.
162+
--
163+
-- @since 2.3.1
164+
--
165+
label :: MonadCont m => a -> m (a -> m b, a)
166+
label a = callCC $ \k -> let go b = k (go, b) in return (go, a)
167+
168+
-- | Simplified version of `label` without arguments
169+
--
170+
-- @since 2.3.1
171+
--
172+
label_ :: MonadCont m => m (m a)
173+
label_ = callCC $ return . fix

0 commit comments

Comments
 (0)