Suppose I have a list of type types [*] :
let Ts = '[Int, Bool, Char]
I want to convert this to a chain of tuples:
type family Tupled (ts :: [*]) z :: * type instance Tupled (t ': ts) z = (t, Tupled ts z) type instance Tupled '[] z = z
So far so good:
> :kind! Tupled Ts () Tupled Ts () :: * = (Int, (Bool, (Char, ())))
Now I'd like to write the Fun type to represent functions that are polymorphic in the "bottom" of this chain. For example, Fun Ts Ts should work with any of these types:
(Int, (Bool, (Char, (String, ())))) (Int, (Bool, (Char, (Word, (ByteString, ())))))
I tried this:
newtype Fun as bs = Fun { unfun :: forall z. Tupled as z -> Tupled bs z }
But it cannot be verified:
Couldn't match type 'Tupled bs z' with 'Tupled bs z0' NB: 'Tupled' is a type function, and may not be injective The type variable 'z0' is ambiguous Expected type: Tupled as z -> Tupled bs z Actual type: Tupled as z0 -> Tupled bs z0 In the ambiguity check for the type of the constructor 'Fun': Fun :: forall z. Tupled as z -> Tupled bs z To defer the ambiguity check to use sites, enable AllowAmbiguousTypes In the definition of data constructor 'Fun' In the newtype declaration for 'Fun'
I saw recommendations for using a data family to avoid problems with injectivity:
data family Tupled (ts :: [*]) z :: * data instance Tupled (t ': ts) z = Cons t (Tupled ts z) data instance Tupled '[] z = Nil
Indeed, the compilation is Fun , but it looks like it makes me "stuck" in the country of Cons and Nil when Im looking for work with tuples, for example:
Fun $ \ (i, (b, (c, z))) -> (succ i, (not b, (pred c, z)))
Is there any way around this?