I have something similar to this class in my code. It does not make sense for my situation to add a' as another parameter to class Foo.
class Foo a where
type FCtx a a' :: Constraint
type FCtx a a' = ()
f :: (FCtx a a') => a -> a'
data D b = D b
instance (Integral b) => Foo (D b) where
-- the next line does not compile because b' does not appear on the LHS
type FCtx (D b) a' = (a' ~ D b', Integral b')
f (D x) = D $ fromIntegral x
For this specific instance of Foo
, I want a'
to be related in this way. The only way I've come up with to make this work is by adding a "dummy" class with an "obvious" type synonym:
class DClass d where
type DType d
instance DClass (D b) where
type DType (D b) = b
The Foo
instance now becomes:
instance (Integral b) => Foo (D b) where
type FCtx (D b) a' = (a' ~ D (DType a'), Integral (DType a'))
f (D x) = D $ fromIntegral x
The problem is that I had to create an entire class (and instance) for my specific data type just to express the type synonym/functional dependency that (D b) determines b. I will not have any other instances of this class, because I always want DType a'
to mean the type parameter to D b
.
What I would like to do instead is something like:
type DParam (D b) = b
instance (Integral b) => Foo (D b) where
type FCtx (D b) a' = (a' ~ D (DParam a'), Integral (DParam a'))
f (D x) = D $ fromIntegral x
or maybe even some nicer way of expressing this Constraint without using type synonyms at all. It seems silly that I should be forced to create an (open) class with a type synonym with a single instance just to accomplish this, and unsafe that others could potentially create new instances that I did not intend.
At the least, would there not be some canonical way of turning a "pattern matched type synonym" into the class/instance DClass
above?
Slightly lighter weight, using type families:
type family DParam d :: *
type instance DParam (D b) = b
Not sure if you can do much better right now...