Haskell和Rank-N多态性

5
以下假设的 Haskell 代码有什么问题?当我在大脑中编译它时,应该输出“1”。
foo :: forall a. forall b. forall c. (a -> b) -> c -> Integer -> b
foo f x n = if n > 0 then f True else f x

bar :: forall a. a -> Integer
bar x = 1

main = do
     putStrLn (show (foo bar 1 2))

GHC抱怨:

$ ghc -XRankNTypes -XScopedTypeVariables poly.hs 

poly.hs:2:28:
     Couldn't match expected type `a' against inferred type `Bool'
       `a' is a rigid type variable bound by
           the type signature for `foo' at poly.hs:1:14
     In the first argument of `f', namely `True'
     In the expression: f True
     In the expression: if n > 0 then f True else f x

poly.hs:2:40:
     Couldn't match expected type `Bool' against inferred type `c'
       `c' is a rigid type variable bound by
            the type signature for `foo' at poly.hs:1:34
     In the first argument of `f', namely `x'
     In the expression: f x
     In the expression: if n > 0 then f True else f x

那是什么意思?这不是有效的Rank-N多态吗? (免责声明:我绝对不是Haskell程序员,但OCaml不支持这种显式类型签名。)

4
啊,大脑。我最喜欢的编译器 :) - Richard H
1个回答

17

你的代码实际上并没有使用 rank-N 多态性。

foo :: forall a. forall b. forall c. (a -> b) -> c -> Integer -> b

这是一个普通的一阶类型。它的意思是:对于所有的a、b和c,此函数可以接受一个类型为a -> b 的函数,一个类型为 c的值和一个 Integer ,并返回一个类型为b的值。因此它可以接受类型为Bool -> IntegerInteger -> Integer的函数。但它要求函数在其参数中具有多态性。如果要表达这一点,则需要使用:

foo :: forall b. forall c. (forall a. a -> b) -> c -> Integer -> b

现在你是在说这个函数的类型需要是 forall a. a -> b,其中 b 是固定的,但是 a 是一个新引入的变量,因此该函数需要在其参数上具有多态性。


4
有没有任何有趣的类型为forall a. a -> b的占用者?唯一想到的是一个接受参数但根本不使用它的函数:例如f x = 12,就像他上面的bar函数一样。 - chrisdb
1
@chrisdb: ……这恰好是const 12的结果。而且const已经找到了一些应用。 - user395760
@delnan: ...我可以看到在一些情况下它可能会有用(就像id一样有用)。但是它似乎并不特别“有趣”,就像id虽然有时候有用但也不是很有趣一样。 - chrisdb
2
我猜在Haskell的实现中,S和K非常重要,所以即使const看起来很无聊,我也不应该侮辱它。 - chrisdb
喜欢这个解释。 - rohaldb
直到我读到这句话:“现在你说函数的类型需要是forall a. a -> b,其中b是固定的,但a是一个新引入的变量,所以函数需要在其参数中具有多态性。”“函数需要在其参数中具有多态性。”是的!我终于明白了。我实际上像个孩子一样跳了一会儿。 - Gregory Higley

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接