I have type classes, for all of which I would like to have some common behavior. My problem is explained in the following code:
class A a class B b class X x where method :: (A a, B b) => x -> a -> b data T = L | M | N data U = P | Q | R instance A T instance B U data Y = ZZZ instance X Y where method _ L = P method _ M = Q method _ N = R
When I load this module, I get the following error:
example.hs:19:14: Could not deduce (a ~ T) from the context (A a, B b) bound by the type signature for method :: (A a, B b) => Y -> a -> b at example.hs:(17,5)-(19,18) `a' is a rigid type variable bound by the type signature for method :: (A a, B b) => Y -> a -> b at example.hs:17:5 In the pattern: N In an equation for `method': method _ N = R In the instance declaration for `X Y' example.hs:19:18: Could not deduce (b ~ U) from the context (A a, B b) bound by the type signature for method :: (A a, B b) => Y -> a -> b at example.hs:(17,5)-(19,18) `b' is a rigid type variable bound by the type signature for method :: (A a, B b) => Y -> a -> b at example.hs:17:5 In the expression: R In an equation for `method': method _ N = R In the instance declaration for `X Y' Failed, modules loaded: none.
I am at loss what to do in this case. Even when T and U are instance of A and B, I get this error. If I cannot return a rigid type value from method
, how do I code this part?
The signature method :: (A a, B b) => x -> a -> b
promises that method
works for every pair of types (a, b)
with a
an instance of A
and b
an instance of B
, but you define it to work only with two specific types.
This is fundamentally different from interfaces in Java or the like, where the callee chooses which type is used, the only thing the caller knows is that interface X is implemented. In Haskell, given such a signature, the caller decides which types are used (here, what type is passed as second argument and what type shall be returned) and the callee has to be able to provide the demanded functionality (as long as the demanded types are instances of the required classes).
Without any methods in classes A
and B
to analyse respectively construct values of an instance of that class, you cannot implement method
other than with undefined
(various degrees of undefinedness are possible due to seq
), so you would have to tell the world that you are in fact using T
and U
.
Another way is to make X
a multiparameter type class,
{-# LANGUAGE MultiParamTypeClasses #-}
class (A a, B b) => X x a b where
method :: x -> a -> b
However, that might require functional dependencies to resolve instances. Another way would be to use associated types,
{-# LANGUAGE TypeFamilies #-}
class X x where
type AType x
type BType x
method :: x -> AType x -> BType x
instance X Y where
type AType Y = T
type BType Y = U
method ...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With