Reputation: 6435
I'm using hspec and QuickCheck to validate functor laws for instances of Functor. I have functions
functorIdentity :: (Functor f, Eq (f a)) => f a -> Bool
and
functorComposition :: (Functor f, Eq (f c)) => (Fun a b) -> (Fun b c) -> f a -> Bool
I'm then testing these two using blocks of code like this:
testListFunctorness :: IO ()
testListFunctorness =
hspec $ do
describe "list" $ do
it "should obey functor identity" $ do
property (functorIdentity :: [Int] -> Bool)
it "should obey functor composition" $ do
property
(functorComposition :: (Fun Int String) -> (Fun String Int) -> [Int] -> Bool)
The thing is, to test the same properties for a different Functor instance, I need to copy everything except the [Int]
s:
testMaybeFunctorness :: IO ()
testMaybeFunctorness =
hspec $ do
describe "maybe" $ do
it "should obey functor identity" $ do
property (functorIdentity :: Maybe Int -> Bool)
it "should obey functor composition" $ do
property
(functorComposition :: (Fun Int String) -> (Fun String Int) -> Maybe Int -> Bool)
It feels like I should be able to write an expression which is in some way polymorphic over different Functor
instances, but I can't even think how to begin that.
How can I conveniently reuse that block of test logic for multiple different Functor
s?
Upvotes: 2
Views: 123
Reputation: 1505
What you could do is to explicitly pass the desired type to testFunctorness
:
import Data.Proxy
testFunctorness :: forall a. Functor a => Proxy a -> IO ()
testFunctorness _ =
hspec $ do
describe "the type" $ do
it "should obey functor identity" $ do
property (functorIdentity :: a Int -> Bool)
it "should obey functor composition" $ do
property
(functorComposition :: (Fun Int String) -> (Fun String Int) -> a Int -> Bool)
The call would look like testFunctorness (Proxy :: Proxy [])
.
You will need to enable {-# LANGUAGE ScopedTypeVariables #-}
so that the a
inside the function refers to the a
in the type signature. The forall
is then needed to let the typechecker know this a
should be lexically scoped.
Upvotes: 3