为什么此功能无法检测?

在函数式编程的讲座中,我们看到了以下Haskell函数:

f :: Bool -> Int -> (a -> Int) -> Int
f x y z = if x then y + y else (z x) + (z y)

预计此功能将无法检测。 但是,这种情况发生的原因没有解释。 在GHCI中试用时,我得到了以下输出:

Prelude> :l test [1 of 1] Compiling Main             ( test.hs,
interpreted )

test.hs:2:35:
    Couldn't match expected type `a' with actual type `Bool'
      `a' is a rigid type variable bound by
          the type signature for f :: Bool -> Int -> (a -> Int) -> Int
          at test.hs:1:6
    Relevant bindings include
      z :: a -> Int (bound at test.hs:2:7)
      f :: Bool -> Int -> (a -> Int) -> Int (bound at test.hs:2:1)
    In the first argument of `z', namely `x'
    In the first argument of `(+)', namely `(z x)' Failed, modules loaded: none.

为什么会发生?


f :: Bool -> Int -> (a -> Int) -> Int
f x y z = if x then y + y else (z x) + (z y)

类型签名声明我们的函数z在它的第一个参数中是多态的。 它需要的任何类型的值a ,并返回一个Int 。 但是,类型变量a的范围也意味着它在所有用途中必须是相同的类型aa不能在相同的使用地点实例化为不同的类型。 这是“一等多性主义”。

你可以阅读这个类型:

f :: forall a. Bool -> Int -> (a -> Int) -> Int

所以:

z (x :: Bool) + z (y :: Int)

是无效的,因为a限于两种不同的独立类型。

语言扩展使我们能够改变的范围a ,以便它可以被实例化多态变量-即在相同的使用部位来容纳不同类型,包括具有多态函数类型:

Prelude> :set -XRankNTypes

Prelude> let f :: Bool -> Int -> (forall a . a -> Int) -> Int 
             f x y z = if x then y + y else (z x) + (z y)

现在,类型a不具有全局范围,并且各个实例可以变化。 这让我们编写“更多多态”函数f并使用它:

Prelude> f True 7 (const 1)
14

所以这就是更高级的多态性。 更多的代码重用。


这不仅仅是简单的参数多态性的工作原理。 函数z在函数的签名中是多态的,但在本体中它是唯一的单形态。

当类型检查定义时,类型检查器推断类型变量a在整个函数定义中使用的单形态类型。 你f但是试图调用z两种不同类型,因此类型检查推断两种相互冲突的类型a


甚至

f :: Bool -> Int -> (a -> Int) -> Int
f x y z = if x then y + y else (z y) + (z y)

将不会typecheck(正如在注释中指出的那样),并且会生成相同的错误,因为Haskell推断出表达式的最少一般类型,并且您的类型比推断的更一般。 正如“Haskell的简洁介绍”所说,

表达式或函数的主体类型是最不常用的类型,它直观地“包含表达式的所有实例”。

如果你明确地指定了一个类型,Haskell就会假设你出于某种原因做了这个,并且坚持将推断的类型与给定的类型进行匹配。

对于上面的推断类型表达式为(Num t) => Bool -> t -> (t -> t) -> t ,所以匹配的类型时,它看到你给Inty和类型z变得(Int -> Int) 。 这比(a -> Int)不那么一般。 但是你坚持要有a (不是一个Int ) - 一个刚性类型变量。 换句话说,你的函数f只能接受类型为Int -> Int函数,但你坚持要给它一个函数:: a -> Int ,包括:: String -> Int等等(如@augustsson points在评论中)。 您声明的类型太宽泛。

同样,如果你只有一个(zx) ,它将与给定类型的x匹配,并且为z函数获得比声明类型(Bool -> Int)更窄的类型。 然后再次抱怨刚性类型变量。

实际上,您声明类型(Num t) => Bool -> t -> (t1 -> t) -> t但它确实是(Num t) => Bool -> t -> (t -> t) -> t 。 它是一个不同的类型。

链接地址: http://www.djcxy.com/p/43129.html

上一篇: Why does this function fail to typecheck?

下一篇: Int and Num type of haskell