I am going through "Haskell programming from first principles" and I found myself writing code in the following fashion over and over:
type IntToInt = Fun Int Int
type TypeIdentity = ConcreteFunctorType Int -> Bool
type TypeComposition = ConcreteFunctorType Int -> IntToInt -> IntToInt -> Bool
checkSomething :: IO ()
checkSomething = hspec $ do
describe "Some functor" $ do
it "identity property" $ do
property $ (functorIdentity :: TypeIdentity)
it "composition property" $ do
property $ (functorComposition :: TypeComposition)
I tried abstracting this but at my level I am not able to figure out a way to make it work
what I would have liked to accomplish was something like this
checkFunctor :: (Functor f) => String -> f a -> IO ()
checkFunctor description f = hspec $ do
describe description $ do
it "identity property" $ do
property $ (functorIdentity :: f a -> TypeIdentity)
it "composition property" $ do
property $ ( functorComposition :: f a -> TypeComposition)
EDIT: After Sapanoia's answer I tried as follows
type TypeIdentity = Bool
type TypeComposition = Fun Int Int -> Fun Int Int -> Bool
checkFunctor :: forall f a. (Functor f) => String -> f a -> IO ()
checkFunctor description f = hspec $ do
describe description $ do
it "identity property" $ do
property $ (functorIdentity :: f a -> TypeIdentity)
it "composition property" $ do
property $ (functorCompose' :: f a -> TypeComposition)
but I get the following error
FunctorCheck.hs:22:25: error:
• Couldn't match type ‘a’ with ‘Int’
‘a’ is a rigid type variable bound by
the type signature for:
checkFunctor :: forall (f :: * -> *) a.
Functor f =>
String -> f a -> IO ()
at FunctorCheck.hs:16:26
Expected type: f a -> TypeComposition
Actual type: f Int -> Fun Int Int -> Fun Int Int -> Bool
It then becomes quite complicated for me to define the types to generate the arbitrary values and functions.
Is there a way I can bind the type of checkFunctor to a specific type like the following?
checkFuntor :: checkFunctor :: forall f Int. (Functor f) => String -> f a -> IO ()
of course I tried this and it gives me a parse error, I assume it's just me not using 'forall' correctly.
Since you did not add an error message, I assume the problem is a type error where (functorIdentity :: f a -> TypeIdentity)
is defined. The problem is that the f
introduced here is new and different from the f
in the top-level signature. To remedy this, enable the following extension:
{-# LANGUAGE ScopedTypeVariables #-}
And change the signature of checkFunctor
to:
checkFunctor :: forall f a. (Functor f) => String -> f a -> IO ()
forall
introduces new type variables. Without ScopedTypeVariables and an explicit forall
it is always present implicitly, and (functorIdentity :: f a -> TypeIdentity)
becomes (functorIdentity :: forall f a. f a -> TypeIdentity)
. However, you do not want a forall here, because you want the type variables f
and a
to be the same as the top-level ones.