Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add MonadThrow and MonadCatch instances for Stream #121

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

mauke
Copy link

@mauke mauke commented Jun 28, 2023

No description provided.

These are equivalent to the code in the existing MonadError instance.
@mauke mauke force-pushed the monad-throw-catch branch from b9a5793 to 357749b Compare July 4, 2023 18:33
@treeowl
Copy link
Contributor

treeowl commented Jul 4, 2023

Please leave a comment proving the MonadCatch law. Please also document (somewhere in the Haddocks) how these instances behave, preferably with a few examples, including both "good" examples of desirable behavior and "bad" examples of behavior that might not be what the user expected/desired.

@mauke
Copy link
Author

mauke commented Jul 5, 2023

Is the following correct/complete?


I can prove the MonadThrow and MonadCatch laws under the assumption that a >> b = a >>= \_ -> b (which I don't see formally stated anywhere, but I'm going to assume it anyway).

Lemma 1:

throwM e >>= f  =  throwM e

Proof:

throwM e >>= f =
  -- MonadThrow law (in reverse) for some arbitrary action 'w'
  -- (in particular, you can always choose w = throwM e)
(throwM e >> w) >>= f =
  -- my assumption
(throwM e >>= \_ -> w) >>= f =
  -- Monad associativity
throwM e >>= (\x -> (\_ -> w) x >>= f) =
  -- beta reduction
throwM e >>= (\_ -> w >>= f) =
  -- my assumption, again
throwM e >> (w >>= f)
  -- MonadThrow law
throwM e

Lemma 2:

fmap f (throwM e)  =  throwM e

Proof:

fmap f (throwM e) =
  -- behavior of fmap for monads
throwM e >>= return . f
  -- lemma 1
throwM e

Theorem 1 (MonadThrow law for Stream f m):

throwM e >> f  =  throwM e

Proof:

throwM e >> f =
  -- definition of throwM, (>>) for Stream
lift (throwM e) *> f =
  -- definition of lift for Stream
Effect (fmap Return (throwM e)) *> f =
  -- lemma 2 for m
Effect (throwM e) *> f =
  -- definition of (*>) for Stream
Effect (fmap loop (throwM e)) =  -- this 'loop' is a local function in (*>) and hides a reference to 'f'
  -- lemma 2 for m
Effect (throwM e) =
  -- lemma 2 for m
Effect (fmap Return (throwM e)) =
  -- definition of lift for Stream
lift (throwM e) =
  -- definition of throwM for Stream
throwM e

Theorem 2 (MonadCatch law for Stream f m):

catch (throwM e) f  =  f e

Proof:

catch (throwM e) f =
  -- definition of throwM for Stream
catch (lift (throwM e)) f =
  -- definition of lift for Stream
catch (Effect (fmap Return (throwM e))) f =
  -- lemma 2 for m
catch (Effect (throwM e)) f =
  -- definition of catch for Stream
Effect (fmap loop (throwM e) `catch` (return . f)) =
  -- lemma 2 for m
Effect (throwM e `catch` (return . f)) =
  -- MonadCatch law for m
Effect (return (f e))
  -- definition of effect
effect (return (f e))
  -- specification/law of effect
(join . lift) (return (f e))
  -- associativity of (.)
join ((lift . return) (f e))
  -- MonadTrans law
join (return (f e))
  -- definition of join
return (f e) >>= id
  -- Monad law
id (f e)
  -- definition of id
f e

@mauke
Copy link
Author

mauke commented Jul 10, 2023

As for examples of desirable behavior, do you mean something like this?

S.print (do
    S.yield "start"
    do { xs <- liftIO $ fmap lines (readFile "some-imaginary-file");
         S.each xs }
    S.yield "finish"
)
{-
"start"
*** Exception: some-imaginary-file: openFile: does not exist (No such file or directory)
-}
S.print (do
    S.yield "start"
    do { xs <- liftIO $ fmap lines (readFile "some-imaginary-file");
         S.each xs }
      `catchIOError` (\e -> do
        liftIO $ hPutStrLn stderr $ "caught an error: " ++ show e
        S.yield "something else"
      )
    S.yield "finish"
)
{-
"start"
caught an error: some-imaginary-file: openFile: does not exist (No such file or directory)
"something else"
"finish"
-}

What would "bad" behavior look like?

@mauke
Copy link
Author

mauke commented Jul 26, 2023

@treeowl Ping?

@treeowl
Copy link
Contributor

treeowl commented Jul 26, 2023

Been sick for a week. Please ping again later.

@mauke
Copy link
Author

mauke commented Jul 26, 2023

Get well soon!

@mauke
Copy link
Author

mauke commented Oct 12, 2023

Ping?

@Topsii
Copy link

Topsii commented Nov 23, 2023

@treeowl ping

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants