That is, what I am asking about is a loop.
effectful :: Int -> IO Int
effectful n = do
putStrLn $ "Effect: " ++ show n
return n
condition = (== 3)
final :: Int -> IO ()
final n = putStrLn $ "Result: " ++ show n
loop = ?
It should work like this:
λ loop [1..10]
Effect: 1
Effect: 2
Effect: 3
Result: 3
I can offer a recursive definition:
loop (x: xs) = do
r <- effectful x
if condition r
then final r
else loop xs
However, I am having trouble representing this effect with any combination of Functor
, Monad
,
Foldable
and Traversable
methods, because they always insist on evaluating all of the actions,
while what I need is to stop at some point within the list.
For instance, with an unfoldrM
(which is an effectful version of Data.List.unfoldr
that I
made up for the occasion) I can perform exactly the actions I need, but I cannot attain the value
of the last action, because the function parameter returns Nothing
:
unfoldrM :: Monad m => (a -> MaybeT m (b, a)) -> a -> m [b]
unfoldrM f x = fmap reverse $ unfoldrM' f x (return [ ])
where
-- unfoldrM' :: (a -> MaybeT m (b, a)) -> a -> m [b] -> m [b]
unfoldrM' f x ys = runMaybeT (f x) >>= \r -> case r of
Just (y, x') -> unfoldrM' f x' (fmap (y:) ys)
Nothing -> ys
f :: [Int] -> MaybeT IO (Int, [Int])
f (x: xs) = (lift . effectful $ x) >>= \y ->
if condition y
then MaybeT (return Nothing)
else lift . return $ (y, xs)
— Which got me thinking: "What if I used Either
instead, then unwrapped the Left
result?"
This line of consideration led me to Control.Monad.Except
and then to the idea that I should
consider the desired result to be the exception in the control flow.
exceptful :: Int -> ExceptT Int IO ()
exceptful n = do
r <- lift (effectful n)
if condition r
then throwError r
else return ()
loop' xs = fmap (fromRight ())
$ runExceptT (traverse_ exceptful xs `catchError` (lift . final))
λ loop' [1..10]
Effect: 1
Effect: 2
Effect: 3
Result: 3
What I think about this solution is that it is awful. First, it is counter-intuitive to use the
left side as the actual result carrier, second, this code is so much more complex than the
recursive loop
that I started with.
What can be done?