Let's have the following data types:
data Foo1 a = Foo1
data Foo2 a = Foo2 (Foo3 a)
data Foo3 a = C1 (Foo1 a) | C2 Int
And now we want to be able to get a Foo3 from a Foo1 or an Int. A solution could be to use a type class:
class ToFoo3 a where
toFoo3 :: a -> Foo3 b -- Here start the problems with this phantom type b...
instance ToFoo3 (Foo1 b) where
toFoo3 foo1 = C1 foo1
instance ToFoo3 Int where
toFoo3 int = C2 int
And here the compiler complains (rightly!) that it cannot match b with b1 because the "b" of Foo3 in the class definition is not the same as the one of Foo1 in the instance.
Is there a way to solve this?
A multiparameter typeclass without a functional dependency compiles for me:
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
data Foo1 a = Foo1
data Foo2 a = Foo2 (Foo3 a)
data Foo3 a = C1 (Foo1 a) | C2 Int
class ToFoo3 a b where
toFoo3 :: a -> Foo3 b
instance ToFoo3 (Foo1 b) b where
toFoo3 foo1 = C1 foo1
instance ToFoo3 Int b where
toFoo3 int = C2 int
The way I understand it, you cannot have a functional dependency in either direction, since an Int
needs to be able to convert to any Foo3 a
type, and Foo1 a
also needs to be able to convert to the same Foo3 a
type.
Of course this means you cannot expect any of the argument or result type of toFoo3
to help with infering the other, so you might sometimes need an annoying amount of type annotations to use this, but other than that this should work.
EDIT: I am assuming you don't want to be able to convert from Foo1 a
to Foo3 b
with a
and b
different. If I am wrong about that, then your OP code with a single-parameter class should work if you change one instance to
instance ToFoo3 (Foo1 b) where
toFoo3 Foo1 = C1 Foo1
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