[Haskell-cafe] Extensionality principles for kinds?

Richard Eisenberg eir at cis.upenn.edu
Sun Nov 9 03:30:12 UTC 2014


The problem is that GHC doesn't do eta expansion for product types. See #7259, which won't be fixed by 7.10, as I would imagine a fair amount of theory needs to be done for this to be valid.

Luckily, you can do eta expansion manually yourself: either by requiring that the caller know the eta-expanded form (1) or by storing that knowledge in a GADT (2):

(1): Change the type signature of doubleEq to

> doubleEq :: (forall a b. f a -> f b -> Maybe (a :~: b))
>          -> Dbl f (TwiceVal a1 a2)
>          -> Dbl f (TwiceVal b1 b2)
>          -> Maybe (TwiceVal a1 a2 :~: TwiceVal b1 b2)

Here, I've dumped the unnecessary `forall`s (for my own sanity) and simply eta-expanded `a` and `b`. With this type, the original implementation of `doubleEq` type checks.

(2): Learn about the eta-expansion via a GADT pattern-match by changing `Dbl` to this:

> data Dbl (f :: k -> *) (tv :: Twice k) where
>   Dbl :: f a -> f b -> Dbl f (TwiceVal a b)

With this definition, when you pattern-match on the constructor `Dbl`, you also learn that `tv` really must be a `TwiceVal`. Then, the original type and definition for `doubleEq` type-check.

The trade-off between (1) and (2) is where the caller needs to know about the eta-expansions: in (1), it's the caller of `doubleEq` that must know it's passing in a real `TwiceVal`. In (2), it's the caller of the `Dbl` constructor that must know. I rather favor (2), as it allows you to pass the expansion knowledge around quite easily, and you don't need the `TwiceLeft` and `TwiceRight` type families.

Note that you need only solution (1) or (2), not both. (Though both works, too.)

I hope this helps!
Richard


On Nov 8, 2014, at 7:26 PM, Rob <rdockins at galois.com> wrote:

> Haskellers,
> 
> I've waded into some pretty deep waters, but I'm hoping there's a GHC type system wizard in here who can help me.  The following code fragment is extracted from something I've been working on involving some pretty fancy type-level programming.  I'm using GHC 7.8.3.
> 
> I've run into a problem where the typechecker refuses to believe that two types are equal; it seems to lack the necessary extensionality principles for lifted data kinds.  Basically (see below for definitions) I get into a contex where I know that (TwiceLeft a ~ TwiceLeft b) and (TwiceRight a ~ TwiceRight b), but the typechecker will not believe that (a ~ b).
> 
> Are there any tricks I can employ to make this code typecheck (using only safe operations)?  Bonus if it doesn't add any runtime cost.
> 
> Thanks,
>  Rob Dockins
> 
> 
> ====== code follows ======
> 
> {-# LANGUAGE DataKinds #-}
> {-# LANGUAGE PolyKinds #-}
> {-# LANGUAGE RankNTypes #-}
> {-# LANGUAGE TypeOperators #-}
> {-# LANGUAGE TypeFamilies #-}
> 
> import Data.Type.Equality
> 
> -- lifted kind (Twice k) consists of a pair of things of kind k
> data Twice k = TwiceVal k k
> 
> -- type family accessor functions for the Twice kind
> type family TwiceLeft (x::Twice k) :: k where
>  TwiceLeft (TwiceVal a b) = a
> 
> type family TwiceRight (x::Twice k) :: k where
>  TwiceRight (TwiceVal a b) = b
> 
> -- Double is already taken, use Dbl instead...
> data Dbl (f :: k -> *) (tv :: Twice k) = Dbl (f (TwiceLeft tv)) (f (TwiceRight tv))
> 
> -- This function doesn't typecheck...
> doubleEq :: forall (f:: k -> *)
>          . (forall (a::k) (b::k). f a -> f b -> Maybe (a :~: b))
>         -> (forall (a::Twice k) (b::Twice k). Dbl f a -> Dbl f b -> Maybe (a :~: b))
> doubleEq teq (Dbl x y) (Dbl z w) =
>   case teq x z of
>     Nothing -> Nothing
>     Just Refl ->
>       case teq y w of
>         Nothing -> Nothing
>         Just Refl -> Just Refl
> _______________________________________________
> Haskell-Cafe mailing list
> Haskell-Cafe at haskell.org
> http://www.haskell.org/mailman/listinfo/haskell-cafe



More information about the Haskell-Cafe mailing list