31

In ‘Practical type inference for arbitrary-rank types’, the authors talk about subsumption:

3.3 Subsumption

I try to test things in GHCi as I read, but even though g k2 is meant to typecheck, it doesn't when I try with GHC 7.8.3:

λ> :set -XRankNTypes
λ> let g :: ((forall b. [b] -> [b]) -> Int) -> Int; g = undefined
λ> let k1 :: (forall a. a -> a) -> Int; k1 = undefined
λ> let k2 :: ([Int] -> [Int]) -> Int; k2 = undefined
λ> :t g k1

<interactive>:1:3: Warning:
    Couldn't match type ‘a’ with ‘[a]’
      ‘a’ is a rigid type variable bound by
          the type forall a1. a1 -> a1 at <interactive>:1:3
    Expected type: (forall b. [b] -> [b]) -> Int
      Actual type: (forall a. a -> a) -> Int
    In the first argument of ‘g’, namely ‘k1’
    In the expression: g k1
g k1 :: Int
λ> :t g k2

<interactive>:1:3: Warning:
    Couldn't match type ‘[Int] -> [Int]’ with ‘forall b. [b] -> [b]’
    Expected type: (forall b. [b] -> [b]) -> Int
      Actual type: ([Int] -> [Int]) -> Int
    In the first argument of ‘g’, namely ‘k2’
    In the expression: g k2
g k2 :: Int

I haven't really gotten to the point where I understand the paper, yet, but still, I worry that I have misunderstood something. Should this typecheck? Are my Haskell types wrong?

beta
  • 2,380
  • 21
  • 38

1 Answers1

17

The typechecker doesn't know when to apply the subsumption rule.

You can tell it when with the following function.

Prelude> let u :: ((f a -> f a) -> c) -> ((forall b. f b -> f b) -> c); u f n = f n

This says, given a function from a transformation for a specific type, we can make a function from a natural transformation forall b. f b -> f b.

We can then try it successfully on the second example.

Prelude> :t g (u k2)
g (u k2) :: Int

The first example now gives a more informative error as well.

Prelude> :t g (u k1)
    Couldn't match type `forall a. a -> a' with `[a0] -> [a0]'
    Expected type: ([a0] -> [a0]) -> Int
      Actual type: (forall a. a -> a) -> Int
    In the first argument of `u', namely `k1'
    In the first argument of `g', namely `(u k1)'

I don't know if we can write a more general version of u; we'd need a constraint-level notion of less polymorphic to write something like let s :: (a :<: b) => (a -> c) -> (b -> c); s f x = f x

Cirdec
  • 24,019
  • 2
  • 50
  • 100
  • 5
    This is a great example of Haskell not taking its own notion of subtyping seriously... But it's generally not so bad to be a little more explicit when you need it. – J. Abrahamson Nov 07 '14 at 18:59
  • 3
    GHC, you disappoint me. I was so sure GHC got this right I even glossed over my stupid mistake in my deleted answer here. – András Kovács Nov 07 '14 at 19:05
  • 3
    The type checker as described in the paper **does** know when to apply the subsumtion rule. It's apparently just GHC. I know this because I implemented the type checker described in that paper in Frege, and the Frege typechecker accepts `g k2` without complaints. (See here for an example: https://github.com/Frege/frege/issues/80#issuecomment-62257574) – Ingo Nov 08 '14 at 13:31