HMock and Impredicative Polymorphism (Part 2)
I reviewed the documentation on impredicative polymorphism yesterday while calming a tired baby, and I realized that I could probably resolve the issue described in the HMock and Impredicative Polymorphism blog entry by simply specifying types! I was able to try it out this morning, and I was indeed able to resolve the issue.
Following the types, I discovered an error in the test code. Here is the original code, for reference.
main :: IO ()
= defaultMain . testCase "experiment" $ do
main <- runMockT $ do
isEmpty $ WithFile_ anything
expect |=> \(WithFile "test.txt" ReadMode _action) -> pure True
"test.txt"
isFileEmpty True @=? isEmpty
The WithFile_
function, created by makeMockable
constructs a Matcher
,
which is a specification for matching actions. The following definition
is displayed by dumping the Template Haskell splices.
data Test.HMock.Mockable.Matcher MonadHandle
:: ghc-prim-0.6.1:GHC.Types.Symbol
-> (ghc-prim-0.6.1:GHC.Types.Type -> ghc-prim-0.6.1:GHC.Types.Type)
-> ghc-prim-0.6.1:GHC.Types.Type
-> ghc-prim-0.6.1:GHC.Types.Type
where
HIsEOF_
:: (Test.Predicates.Predicate Handle)
-> Test.HMock.Mockable.Matcher MonadHandle "hIsEOF" m_a6hj Bool
WithFile_
:: (Test.Predicates.Predicate FilePath)
-> (Test.Predicates.Predicate IOMode)
-> ( forall r_a6lZ
. Typeable r_a6lZ
=> Test.Predicates.Predicate
Handle -> Test.HMock.Internal.State.MockT m_a6hj r_a6lZ)
(
)-> Test.HMock.Mockable.Matcher MonadHandle "withFile" m_a6hj r_a6lZ
A Matcher
constructor of course takes a Predicate
for each parameter of the mocked function! I had only seen examples with
a single Predicate
and only put one anything
argument, but I need one for each argument.
main :: IO ()
= defaultMain . testCase "experiment" $ do
main <- runMockT $ do
isEmpty $ WithFile_ anything anything anything
expect |=> \(WithFile "test.txt" ReadMode _action) -> pure True
"test.txt"
isFileEmpty True @=? isEmpty
For this minimal test, that change alone resolves the issue!
In my actual code, the return value is more complicated. The impredicative
polymorphism error goes away, but I got a Typeable
instance error instead. It seems that the constraint in
withFile
was lost, resolving one issue but causing another.
This was easily solved by specifying the type of the Matcher
.
$
expect WithFile_ anything anything anything
( :: Matcher MonadHandle "withFile" IO ConcreteType
)|=> \(WithFile "test.md" ReadMode _action) -> pure concreteValue
Note that the underlying monad is IO
because that is the
monad used by HUnit and Tasty.
The updated code is available on GitHub.