HMock Compatibility (Part 4)
After resolving
the MonadUnliftIO
issue in HMock, I have been
using it with GHC 8.6 through 9.2 in various projects. Mocking the
higher-rank withFile
function, as described in HMock and
Impredicative Polymorphism (and Part
2), causes issues with GHC 8.6, however.
I am currently experimenting with mocking withFile
in the Redact
project. In this project, I would like to represent “normal errors” in
the types. By “normal errors,” I mean errors that are expected in the
normal usage of the program. For example, withFile
may fail if the file cannot be read. This may happen due to one of many
different causes, including the file not existing as well as all kinds
of permissions issues. In this case, I use tryIOError
to catch IOError
exceptions and return them.
class Monad m => MonadHandle m where
...
withFile :: Typeable r
=> FilePath
-> IOMode
-> (Handle -> m r)
-> m (Either IOError r)
instance MonadHandle IO where
...
= tryIOError . IO.withFile path mode withFile path mode
Using GHC 8.6.5, the code created by makeMockable
causes the build to fail with the following errors.
/test/Redact/Terminal/Mock.hs:47:1: error:
• Could not deduce: t0
~ (GHC.IO.Handle.Types.Handle
-> Test.HMock.Internal.State.MockT m r)
from the context: (name ~ "withFile", a ~ Either IOError r,
base-4.12.0.0:Data.Typeable.Internal.Typeable r)
bound by a pattern with constructor:
WithFile :: forall r (b :: * -> *).
base-4.12.0.0:Data.Typeable.Internal.Typeable r =>
FilePath
-> IOMode
-> (GHC.IO.Handle.Types.Handle
-> Test.HMock.Internal.State.MockT b r)
-> Test.HMock.Mockable.Action
MonadHandle "withFile" b (Either IOError r),
in an equation for ‘showMatcher’
at test/Redact/Terminal/Mock.hs:47:1-29
• When checking that the pattern signature: t0
fits the type of its context:
GHC.IO.Handle.Types.Handle -> Test.HMock.Internal.State.MockT m r
In the pattern: _ :: t_acTn
In the pattern: WithFile _ _ (_ :: t_acTn)
• Relevant bindings include
showMatcher :: Maybe
(Test.HMock.Mockable.Action MonadHandle name m a)
-> Matcher MonadHandle name m b -> String
(bound at test/Redact/Terminal/Mock.hs:47:1)
|
47 | makeMockable [t|MonadHandle|]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/test/Redact/Terminal/Mock.hs:47:1: error:
• Could not deduce: t0
~ (GHC.IO.Handle.Types.Handle
-> Test.HMock.Internal.State.MockT m r0)
from the context: (name ~ "withFile", a ~ Either IOError r,
base-4.12.0.0:Data.Typeable.Internal.Typeable r)
bound by a pattern with constructor:
WithFile :: forall r (b :: * -> *).
base-4.12.0.0:Data.Typeable.Internal.Typeable r =>
FilePath
-> IOMode
-> (GHC.IO.Handle.Types.Handle
-> Test.HMock.Internal.State.MockT b r)
-> Test.HMock.Mockable.Action
MonadHandle "withFile" b (Either IOError r),
in an equation for ‘showMatcher’
at test/Redact/Terminal/Mock.hs:47:1-29
or from: (name ~ "withFile", b ~ Either IOError r1)
bound by a pattern with constructor:
WithFile_ :: forall (b :: * -> *) r.
Test.Predicates.Predicate FilePath
-> Test.Predicates.Predicate IOMode
-> (forall r1.
base-4.12.0.0:Data.Typeable.Internal.Typeable r1 =>
Test.Predicates.Predicate
(GHC.IO.Handle.Types.Handle
-> Test.HMock.Internal.State.MockT b r1))
-> Matcher MonadHandle "withFile" b (Either IOError r),
in an equation for ‘showMatcher’
at test/Redact/Terminal/Mock.hs:47:1-29
Expected type: Test.Predicates.Predicate t0
Actual type: Test.Predicates.Predicate
(GHC.IO.Handle.Types.Handle
-> Test.HMock.Internal.State.MockT m r0)
• In the first argument of ‘show’, namely
‘(p_acTq :: Test.Predicates.Predicate t_acTn)’
In the first argument of ‘(++)’, namely
‘show (p_acTq :: Test.Predicates.Predicate t_acTn)’
In the second argument of ‘(++)’, namely
‘(show (p_acTq :: Test.Predicates.Predicate t_acTn) ++ "»")’
• Relevant bindings include
p_acTq :: forall r.
base-4.12.0.0:Data.Typeable.Internal.Typeable r =>
Test.Predicates.Predicate
(GHC.IO.Handle.Types.Handle -> Test.HMock.Internal.State.MockT m r)
(bound at test/Redact/Terminal/Mock.hs:47:1)
showMatcher :: Maybe
(Test.HMock.Mockable.Action MonadHandle name m a)
-> Matcher MonadHandle name m b -> String
(bound at test/Redact/Terminal/Mock.hs:47:1)
|
47 | makeMockable [t|MonadHandle|]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
I do not know how to fix these errors, and I do not have time to dig into it at this time. For now, I am going to simply only enable the mock tests for GHC 8.8 and later, as described in HMock Compatibility (Part 3).