我对这个意味着要微不足道的高等级多态性练习做错了什么?

Ste*_*314 11 haskell ghc

一年多以前,我问过如何在Haskell中使用代理问题,从那时起我就少量使用了RankNTypes GHC扩展.麻烦是每次我尝试使用它时,我最终都会收到奇怪的错误消息,并且会破解代码直到它们消失为止.否则我放弃了.

显然我并不真正理解Haskell中更高级别的多态性.为了解决这个问题,我决定直接找到我能做的最简单的例子,测试我所有的假设,看看我是否可以让自己成为尤里卡时刻.

第一个假设 - 更高级别的多态性不是标准Haskell 98(或2010?)功能的原因是,如果您接受许多程序员甚至不会注意到的一些不那么明显的限制,则不需要.我可以定义等级1和等级2的多态函数,这些函数初看起来是等价的.如果我将它们加载到GHCi并使用相同的参数调用它们,它们将给出相同的结果.

所以 - 简单的示例函数......

{-# 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
Run Code Online (Sandbox Code Playgroud)

和GHCi会议......

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>
Run Code Online (Sandbox Code Playgroud)

到目前为止没有错误 - 良好的开端.接下来,使用空列表参数测试每个函数...

*Main> rank0 []
True
*Main> rank1a []
True
*Main> rank1b []
True
*Main> rank2 []
True
*Main>
Run Code Online (Sandbox Code Playgroud)

说实话,我对这种情况下的功能rank1arank1b功能感到有些惊讶.该列表不知道它包含哪些类型的元素,函数也不知道,但肯定必须决定类型才能进行调用?我原以为需要在某处提供明确的签名.

这不是ATM的问题,结果似乎很有希望.接下来,非空列表......

*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>
Run Code Online (Sandbox Code Playgroud)

哦,亲爱的 - 当参数更多地了解它的类型时,似乎排名2版本不喜欢它.不过,也许问题只是文字1等是多态的......

*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>
Run Code Online (Sandbox Code Playgroud)

错误是不同的,但它仍然无法正常工作,我仍然不理解这些错误消息.

用各种理论搞清楚,我有一个想法,也许我需要告诉GHC"忘记"列表中的一些静态类型.在那个理论上,我试过各种各样的事情,包括

*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>
Run Code Online (Sandbox Code Playgroud)

好吧,GHCi不知道我在说什么.如果GHCi只需要知道要忘记哪种类型,我也试过......

*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>
Run Code Online (Sandbox Code Playgroud)

非常希望得到一个错误,即GHCi不知道如何show使用遗忘类型的值.我不知道如何构建一个带有"遗忘"静态类型的列表,我甚至不确定它是否有意义.

在这一点上,我并没有尝试用更高级别的多态性做任何有用的事情.这里的要点只是能够rank2使用非空列表调用函数,并理解为什么它与其他函数的工作方式不完全相同.我想继续自己一步一步地搞清楚这一点,但是现在我只是完全卡住了.

C. *_*ann 17

让我们考虑一下这种rank2意味着什么.

rank2 :: (forall a. [a]) -> Bool
rank2 x = null x
Run Code Online (Sandbox Code Playgroud)

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

因此,为了给出一些参数rank2,它需要是一个列表,其元素可以是内部实现可能需要的任何类型rank2.由于没有办法让人想起这种任意类型的值,唯一可能的输入是[]或包含的列表undefined.

与此对比rank1b:

rank1b :: forall a. [a] -> Bool
rank1b x = null x
Run Code Online (Sandbox Code Playgroud)

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

这一个变化工作会是这样的:

rank2b :: (forall a. Num a => [a]) -> Bool
rank2b x = null x
Run Code Online (Sandbox Code Playgroud)

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

rank2c :: (forall a. [a -> a]) -> Bool
rank2c x = null x
Run Code Online (Sandbox Code Playgroud)

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

  • @ Steve314是的,`null`并不关心元素的类型.这就是为什么一般`rank1x`有效.对于`rank2`,你指定了一个更具限制性的类型,但你没有提供一个可能具有限制类型`forall a的参数.并[a]`.一旦指定的类型不如推断类型一般,就限制了函数的可能用例. (2认同)

And*_*ewC 5

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

rank1b :: forall a. [a] -> Bool
rank1b x = null x
Run Code Online (Sandbox Code Playgroud)

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

rank2 只能接受多态列表:

rank2 :: (forall a. [a]) -> Bool
rank2 x = null x
Run Code Online (Sandbox Code Playgroud)

现在这是不同的 - 这意味着论证x本身必须是多态的.[]是多态的,因为它可以是任何类型:

>:t [] 
[] :: [a]
Run Code Online (Sandbox Code Playgroud)

秘密意味着forall a.[a],但rank2不会接受,['2']因为它有类型[Char].

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


Ing*_*ngo 5

在这一点上,我并没有尝试用更高级别的多态性做任何有用的事情.这里的要点只是能够使用非空列表调用rank2函数,并理解为什么它与其他函数的工作方式不完全相同.我想继续自己一步一步地搞清楚这一点,但是现在我只是完全卡住了.

我不太确定更高等级的多态性是你认为的.我认为这个概念只对函数类型有意义.

例如:

reverse :: forall a. [a] -> [a]
tail    :: forall a. [a] -> [a]
Run Code Online (Sandbox Code Playgroud)

告诉我们,这reversetail工作列表元素的类型无关.现在,给定此功能:

foo f = (f [1,2], f [True, False])
Run Code Online (Sandbox Code Playgroud)

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

foo :: (forall a. [a] -> [a]) -> ([Int], [Bool])
Run Code Online (Sandbox Code Playgroud)

现在我们可以

foo reverse
foo tail
Run Code Online (Sandbox Code Playgroud)

因此具有可用的秩-2类型.请注意,类型签名禁止传递类似于:

foo (map (1+))
Run Code Online (Sandbox Code Playgroud)

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