Say I have a multi-param typeclass:
class B b => C a b | a -> b where
getB :: a -> (T, b)
Then I want a function:
f :: C a b => a -> c
f = g . getB
Which uses another function g :: B b => (T, b) -> c
and getB
, so an instance C a b
is needed.
(Disclaimer: the real problem is much more complicated, the above-mentioned is only a simplified version.)
The problem is, given the functional dependency C a b | a -> b
, we know that b
can be completely decided by a
, so theoretically I should be possible not to mention b
in the type of f
(since it is not used elsewhere but in instance C a b
), but I didn't find any way to achieve this.
Also note that due to the existence of constraint B b
in class C
, I think I cannot use a TypeFamilies extension instead, for the syntax of that leaves nowhere for the type constraint B b
to live.
So is there any way that I can hide the implementation details (the irrelevant type parameter b
) from the user of this function f
?
Using TypeFamilies
, you could rewrite C
into a single-parameter class as follows:
class B (BOf a) => C a where
type BOf a :: *
getB :: a -> (T, BOf a)
Which gives you the better looking constraint for f
:
f :: C a => a -> c
There is (sadly) no way to omit parameters of a multi-parameter class to obtain a similar result.
If you turn on RankNTypes
you can make a type alias that lets you elide b
like this:
type C' a t = forall b. C a b => t
f :: C' a (a -> c)
f = g . getB
You can even include further contexts, if necessary, inside the second argument:
f' :: C' a ((Num a, Ord c) => a -> c)
f' = g . getB
This is pretty non-standard, though. You'll want to leave an explanatory comment... and the effort of writing it might be bigger, in the end, than the effort of just including a b
at each use of C
.
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