11

Over a year ago, I asked the question How to use a proxy in Haskell, and since then I have a small amount of use of the RankNTypes GHC extension. The trouble is every time I try to work with it, I end up with bizarre error messages and hacking around the code until they go away. Or else I give up.

Obviously I don't really understand higher rank polymorphism in Haskell. To try to resolve that, I decided to get right down to the simplest examples I could, test all my assumptions and see if I could get myself a Eureka moment.

First assumption - the reason higher rank polymorphism isn't a standard Haskell 98 (or 2010?) feature is that, provided you accept some not-that-obvious restrictions that a lot of programmers wouldn't even notice, it isn't needed. I can define rank 1 and rank 2 polymorphic functions that are, at first sight, equivalent. If I load them into GHCi and call them with the same parameters, they'll give the same results.

So - simple example functions...

{-# LANGUAGE RankNTypes #-}

rank0 :: [Int] -> Bool
rank0 x = null x

rank1a :: [a] -> Bool
rank1a x = null x

rank1b :: forall a. [a] -> Bool
rank1b x = null x

rank2 :: (forall a. [a]) -> Bool
rank2 x = null x

and a GHCi session...

GHCi, version 7.4.2: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Prelude> :load example01
[1 of 1] Compiling Main             ( example01.hs, interpreted )
Ok, modules loaded: Main.
*Main>

No errors so far - good start. Next, test each function with an empty list parameter...

*Main> rank0 []
True
*Main> rank1a []
True
*Main> rank1b []
True
*Main> rank2 []
True
*Main>

To be honest, I was a little surprised the rank1a and rank1b functions worked in this case. The list doesn't know what type elements it contains, the functions don't know either, yet surely the type must be decided to make that call? I was expecting to need to provide an explicit signature somewhere.

That's not really the issue ATM though, and the results seem promising. Next up, non-empty lists...

*Main> rank0 [1,2,3]
False
*Main> rank1a [1,2,3]
False
*Main> rank1b [1,2,3]
False
*Main> rank2 [1,2,3]

<interactive>:10:8:
    No instance for (Num a)
      arising from the literal `1'
    In the expression: 1
    In the first argument of `rank2', namely `[1, 2, 3]'
    In the expression: rank2 [1, 2, 3]
*Main>

Oh dear - it seems like the rank 2 version doesn't like it when the parameter knows a bit more about it's type. Still, maybe the issue is just that the literals 1 etc are polymorphic...

*Main> rank2 ([1,2,3] :: [Int])

<interactive>:11:8:
    Couldn't match type `a' with `Int'
      `a' is a rigid type variable bound by
          a type expected by the context: [a] at <interactive>:11:1
    Expected type: [a]
      Actual type: [Int]
    In the first argument of `rank2', namely `([1, 2, 3] :: [Int])'
    In the expression: rank2 ([1, 2, 3] :: [Int])
*Main>

The error is different, but it still didn't work and I still don't understand these error messages.

Messing around with various theories, one idea I had was that maybe I need to tell GHC to "forget" some of the static type of the list. On that theory, I tried various things including...

*Main> [1,2,3] :: [a]

<interactive>:12:2:
    No instance for (Num a1)
      arising from the literal `1'
    In the expression: 1
    In the expression: [1, 2, 3] :: [a]
    In an equation for `it': it = [1, 2, 3] :: [a]
*Main>

OK, GHCi doesn't know what I'm talking about. In case GHCi just needed to know exactly which type to forget, I also tried...

*Main> ([1,2,3] :: [Int]) :: [a]

<interactive>:15:2:
    Couldn't match type `a1' with `Int'
      `a1' is a rigid type variable bound by
           an expression type signature: [a1] at <interactive>:15:1
    Expected type: [a]
      Actual type: [Int]
    In the expression: ([1, 2, 3] :: [Int]) :: [a]
    In an equation for `it': it = ([1, 2, 3] :: [Int]) :: [a]
*Main>

So much for my hopes of getting an error to the effect that GHCi doesn't know how to show a value with a forgotten type. I don't know how to construct a list with a "forgotten" static type and I'm not even sure that makes sense.

At this point I'm not trying to do anything useful with higher rank polymorphism. The point here is simply to be able to call the rank2 function with a non-empty list, and to understand why it's not working exactly the same as the other functions. I want to carry on figuring this out step by step myself, but right now I'm just completely stuck.

4

3 回答 3

17

让我们考虑一下类型的rank2含义。

rank2 :: (forall a. [a]) -> Bool
rank2 x = null x

的第一个参数rank2需要是类型的东西forall a. [a]。这里的forall最外层意味着获得这样一个值的人可以选择他们的选择a。把它想象成把一个类型作为一个额外的参数。

因此,为了将某些内容作为参数提供给rank2,它需要是一个列表,其元素可以是内部实现可能需要的任何rank2类型。由于无法想象出这种任意类型的值,因此唯一可能的输入是[]包含undefined.

与此对比rank1b

rank1b :: forall a. [a] -> Bool
rank1b x = null x

这里 theforall已经是最外层的了,所以无论谁使用rank1b它自己都可以选择类型。

一个可行的变体是这样的:

rank2b :: (forall a. Num a => [a]) -> Bool
rank2b x = null x

现在您可以向它传递一个数字文字列表,这些文字在所有Num实例上都是多态的。另一种选择是这样的:

rank2c :: (forall a. [a -> a]) -> Bool
rank2c x = null x

这是因为你确实可以想出类型的值forall a. a -> a,特别是函数id

于 2013-05-04T17:21:43.513 回答
5

在这一点上,我并没有尝试对更高级别的多态性做任何有用的事情。这里的重点是能够使用非空列表调用 rank2 函数,并理解为什么它与其他函数的工作方式不完全相同。我想自己一步一步地解决这个问题,但现在我完全陷入了困境。

我不太确定更高级别的多态性是您认为的那样。我认为这个概念只对函数类型有意义。

例如:

reverse :: forall a. [a] -> [a]
tail    :: forall a. [a] -> [a]

告诉我们,无论列表元素的类型如何,它都可以工作reversetail现在,给定这个函数:

foo f = (f [1,2], f [True, False])

是什么类型的foo?标准 HM 推理找不到类型。具体来说,它无法推断f. 我们必须在这里帮助类型检查器,并保证我们只传递不关心列表元素类型的函数:

foo :: (forall a. [a] -> [a]) -> ([Int], [Bool])

现在我们可以

foo reverse
foo tail

因此有一个可用的 rank-2 类型。请注意,类型签名禁止传递以下内容:

foo (map (1+))

因为传递的函数并不完全独立于元素类型:它需要 Num 个元素。

于 2013-05-04T20:06:43.243 回答
5

让我们比较一下那些显式forall类型签名:

rank1b :: forall a. [a] -> Bool
rank1b x = null x

这意味着forall a.([a]->Bool),无论是什么类型,都可以按照您对所有列表的期望工作,并返回一个Bool.

rank2只能接受多态列表:

rank2 :: (forall a. [a]) -> Bool
rank2 x = null x

现在情况不同了——这意味着参数x本身必须是多态的。[]是多态的,因为它可以是任何类型:

>:t [] 
[] :: [a]

这暗地里的意思是forall a.[a],但rank2不会接受['2'],因为那有 type [Char]

rank2只会接受真正属于 type 的列表forall a.[a]

于 2013-05-04T17:22:23.727 回答