I would like to create a Haskell class which perform operations on multiples data types. For an example, I would like to add a value on all elements of a Map structure and retrieve a new Map.
To do that I created a class and instances with 3 types using the MultiParamTypeClasses and FlexibleInstances pragma :
class OPER a b c where
add:: a -> b -> c
instance OPER (Maybe Double) (Maybe Double) (Maybe Double) where
add (Just a) (Just b) = Just (a + b)
add _ _ = Nothing
instance OPER (Map.Map Int (Maybe Double)) (Maybe Double) (Map.Map Int (Maybe Double)) where
add ma mbdlb = Map.map (mbi -> add mbi mbdlb ) ma
It work fine and give me what I want :
main = do
print $ (add (Just (3.5::Double)) (Just (9.7::Double)) :: (Maybe Double))
print $ (add (Map.fromList [(1,Just 3.4),(2,Just 9.4),(3::Int,Just (9.7::Double)),(4,Just 4.8)]) (Just (9.7::Double)) :: (Map.Map Int (Maybe Double)))
Just 13.2
fromList [(1,Just 13.1),(2,Just 19.1),(3,Just 19.4),(4,Just 14.5)]
Now, I would like to embed all possibles types in an AnyData type. I created the following :
data AnyData = forall s . (Show s, Eq s) => DAT s
and the instances :
instance Show AnyData where
show (DAT a) = "DAT "++show a
instance OPER AnyData AnyData AnyData where
add (DAT a) (DAT b) = DAT (add a b)
But when I try to compile, I get the message :
• Could not deduce (OPER s s1 s0) arising from a use of ‘add’
from the context: (Show s, Eq s)
bound by a pattern with constructor:
DAT :: forall s. (Show s, Eq s) => s -> AnyData,
in an equation for ‘add’
at testMultiFun_test5.hs:40:8-12
or from: (Show s1, Eq s1)
bound by a pattern with constructor:
DAT :: forall s. (Show s, Eq s) => s -> AnyData,
in an equation for ‘add’
at testMultiFun_test5.hs:40:16-20
The type variable ‘s0’ is ambiguous
Relevant bindings include
b :: s1 (bound at testMultiFun_test5.hs:40:20)
a :: s (bound at testMultiFun_test5.hs:40:12)
• In the first argument of ‘DAT’, namely ‘(add a b)’
In the expression: DAT (add a b)
In an equation for ‘add’: add (DAT a) (DAT b) = DAT (add a b)
|
40 | add (DAT a) (DAT b) = DAT (add a b)
| ^^^^^^^
I tried to modify my AnyData type declaration with :
data AnyData = forall s . (OPER s s s, Show s, Eq s) => DAT s
or
data AnyData = forall s s1 s2 . (OPER s s1 s2) => DAT s
with the AllowAmbiguousTypes Pragma.
and more simply :
data AnyData = forall s . DAT s
but I always have the same kind of message.
• No instance for (OPER s s1 s0) arising from a use of ‘add’
• In the first argument of ‘DAT’, namely ‘(add a b)’
In the expression: DAT (add a b)
In an equation for ‘add’: add (DAT a) (DAT b) = DAT (add a b)
|
43 | add (DAT a) (DAT b) = DAT (add a b)
|
As far as I understand, the compiler doesn’t understand that there can be different types to my add function when I use it with AnyData and can’t resolve the inference of types.
Is there a solution to embed all the types usable with my OPER class in a single type (to make a list of results) ?
Are there other ways of doing this? with GADTs or type families ?