Reputation: 23135
Lets say I have the following:
class C a where
f :: a -> Blah
f_impl_for_d :: D a => a -> Blah
f_impl_for_d = _
newtype Alice f a = Alice (f a)
And maybe some existing instances like so:
instance C a => C [a] where ...
instance C a => C (Identity a) where ...
instance C a => C (Maybe a) where ...
What I want is an instance like this:
instance (D a, C a => C (f a)) => C (Alice f a)
As in, wherever D a
is valid, and there's an instance C (f a)
with the constraint C a
, then I should be able to derive C (Alice f a)
For example, if I have D a
, then I should have C (Alice [] a)
, and C (Alice Maybe a)
etc.
Now I could do this:
instance D a => C a where
f = f_impl_for_d
instance C (f a) => C (Alice f a) where
f (Alice x) = f x
But that top instance is rediculously overlapping.
Any other way? The only way I've worked out is to make a newtype like so:
newtype T x = T x
instance D x => C (T x) where
f (T x) = f_impl_for_d x
instance (Functor f, C (f (T x))) => C (Alice f x) where
f (Alice x) = f (T <$> x)
But that seems somewhat convoluted. Any nicer way? I tried mucking around with quantified constraints but I didn't get far.
Upvotes: 1
Views: 91
Reputation: 33519
C a => C (f a)
isn't quite what you need. You might think of it as a function C a -> C (f a)
but its meaning is technically a bit more refined than that: the C a
constraint can only be solved using instances; that function cannot take arbitrary arguments.
You can define a (kind of) subclass which provides such a function instead:
class (forall a. C a => C (f a)) => C1 f where
f1 :: (a -> Blah) -> (f a -> Blah)
Which then must be implemented explicitly, for example Maybe
, using your current implementation of C a => C (Maybe a)
, and that C
instance can be replaced with a default f1 f
.
instance C1 Maybe where ...
instance C a => C (Maybe a) where
f = f1 f
Then you can have
instance (D a, C1 f) => C (Alice f a) where
f (Alice x) = f1 f_impl_for_d x
This is similar to the Eq1
, Ord1
, Show1
classes in base. The initial intent was to encode a quantified constraint (forall a. Eq a => Eq (f a))
(before they were a thing), but technically these classes are slightly more expressive than that because the constraint Eq a
(which identifies a globally unique value) is replaced with a type (which may have many values).
Upvotes: 5