I've had to interface two libraries where metadata is represented as a type parameter in one and as a record field in the other. I wrote an adaptor using a GADT. Here's a distilled version:
{-# LANGUAGE GADTs #-}
newtype TFId a = MkId a
data TFDup a = MkDup !a !a
data GADT tf where
ConstructorId :: GADT TFId
ConstructorDup :: GADT TFDup
main = do
f ConstructorId
f ConstructorDup
f :: GADT tf -> IO ()
f = _
This works. (May not be perfect; comments welcome, but that's not the question.)
It took me some time to get to this working state. My initial intuition was to use a type family for TFId
, figuring: “GADT
has kind (* -> *) -> *
; in ConstructorDup
TFDup
has kind * -> *
; so for ConstructorId
I can use the following * -> *
type family:”
{-# LANGUAGE TypeFamilies #-}
type family TFId a where TFId a = a
The type constructor does have the same kind * -> *
, but GHC apparently won't have it in the same place:
error: …
- The type family ‘TFId’ should have 1 argument, but has been given none
- In the definition of data constructor ‘ConstructorId’ In the data type declaration for ‘GADT’
Well, if it says so…
I'm no sure I understand why it would make such a difference. No using type family stems without applying them? What's going on? Any other (better) way to do?
Injectivity.
here
F Int ~ F Char
. However,will never cause
G Int ~ G Char
. These are guaranteed to be distinct types.In universal quantifications like
f
is allowed to beG
(injective) but not allowed to beF
(not injective).This is to make inference work.
foo (... :: G Int)
can be inferred to have typeInt
.foo (... :: F Int)
is equivalent tofoo (... :: Bool)
which may have typeInt
, or typeChar
-- it's an ambiguous type.Also consider
foo True
. We can't expect GHC to choosef ~ F, a ~ Int (or Char)
for us. This would involve looking at all type families and see ifBool
can be produced by any on them -- essentially, we would need to invert all the type families. Even if this were feasible, it would generate a huge amount of possible solutions, so it would be ambiguous.