Something I wanted to do for a long time and finally got around to.
Historical background: we used to take the ability to throw an error in a
computation for granted in any Monad, due to the mis-guided inclusion of the
'fail' method in the Monad class. This has been fixed several ghc versions
ago. To cope with that, I replaced calls to 'fail' with calls to 'throw'
(with manually crafted IOExceptions) when applying a patch fails. But using
the (pure) 'throw' is unclean; it is better to make the requirements for the
base monad explicit by using a MonadThrow constraint, which allows non-IO
base monads to use a pure implementation of 'throwM'.
1 patch for repository http://darcs.net/screened:
patch c2737fa99e15af26c36602ec445ffeb4a10db25d
Author: Ben Franksen <ben.franksen@online.de>
Date: Sun Sep 19 14:00:41 CEST 2021
* require MonadThrow from the base apply monads
This avoids having to throw from pure code when applying patches in a monad
that is not based on IO. In Prim.FileUUID we now correctly handle all patch
application errors as exceptions. In D.P.ApplyMonad, the FilePathMonad is
now a StateT over a Pure monad with an instance MonadThrow that calls error;
in the harness, the Fail monad is now a synonym for Either SomeException.
This seems like a good idea.
> hunk ./src/Darcs/Patch/ApplyMonad.hs 114
> +instance MonadThrow Pure where
> + throwM e = Pure (error (show e))
I don't think this instance satisfies the law for `throwM` described
at https://hackage.haskell.org/package/exceptions-0.10.7/docs/Control-
Monad-Catch.html :
> throwM e >> x = throwM e
I'm guessing it probably doesn't break anything now, given we
also previously just called `error`, but it doesn't feel like a
good long-term state to leave our code in.
> hunk ./harness/Darcs/Test/Patch/RepoModel.hs 15
> + unFail = either (error.show) id
Minor code style comment - in modern Haskell it's better to put spaces
around operators because the language keeps evolving to steal
punctuation for syntax :-)
>> hunk ./harness/Darcs/Test/Patch/RepoModel.hs 15
>> + unFail = either (error.show) id
>
> Minor code style comment - in modern Haskell it's better to put spaces
> around operators because the language keeps evolving to steal
> punctuation for syntax :-)
Agreed. I was just sloppy here.
>> hunk ./src/Darcs/Patch/ApplyMonad.hs 114
>> +instance MonadThrow Pure where
>> + throwM e = Pure (error (show e))
>
> I don't think this instance satisfies the law for `throwM` described
> at https://hackage.haskell.org/package/exceptions-0.10.7/docs/Control-
> Monad-Catch.html :
>
>> throwM e >> x = throwM e
Wouldn't it be enough to make 'Pure' strict to fix that?
> > throwM e >> x = throwM e
> Wouldn't it be enough to make 'Pure' strict to fix that?
Yes, I think so. Though strictness in the context of monadic operations
always confuses me a bit.
> Oops, it is a newtype, so it is already strict. What needs to be
> strict is then >>=, right?
And any custom definition of `>>` if there is one. Plus we need
to make sure that the `Applicative` and `Functor` instances are
consistent with that.
I sort of feel there should be some way to express this in the
datatype, but I'm not sure.
1 patch for repository http://darcs.net/screened:
patch 3d1d622014233786680f69ce0f7fdea5d401bea3
Author: Ben Franksen <ben.franksen@online.de>
Date: Sat Apr 1 17:44:46 CEST 2023
* use StrictIdentity as the Pure apply monad
This is so that we can satisfy the laws for MonadThrow.