假设您有一个函数 f 将按如下方式使用:
(f f (x-1))
关于 f 的类型,你能推断出什么?
它似乎是递归的,即f :: (ftype) -> int -> int。
假设您有一个函数 f 将按如下方式使用:
(f f (x-1))
关于 f 的类型,你能推断出什么?
它似乎是递归的,即f :: (ftype) -> int -> int。
如果函数的参数是该函数本身,那么该类型必须是递归的和无限的,这在 Haskell 中是非法的。然而,有一个漏洞:如果函数在那个参数中是多态的,那么它很好(尽管作为一个函数可能不是很有用)。有效的两个示例f
是id
and const id
(其中任何一个都f f (x-1)
将评估为x-1
)。
在您的标题中,您询问了f
. 在通常的说法中,表达式的 HM 类型是通过应用于特定上下文的正式 HM 类型推断规则为表达式推断的主要(最一般)类型。因此,f
没有上下文就没有类型,并且表达式f f (x-1)
没有提供足够的上下文信息来键入f
。上下文可以在“开始时”给出,也可以通过使用 lambda 或let
表达式来开发。开发的上下文将确定是否可以推断出 HM 类型f
,如果可以,那么该类型是什么。
例如,较大的表达式:
let f = \y -> y in \x -> f f (x-1)
可以在空上下文中键入。如果运算符是减法,它会推断整个表达式的类型和类型forall a. a -> a
,而无需详细介绍。(HM 类型系统没有类型类和约束的概念,例如。)因此,这里的 HM 类型为is ,它在表达式中的使用并不会真正影响其类型。这对于用表达式引入的表达式很典型。例如,较大的表达式:f
Integer -> Integer
-
Integer
Num
f
forall a. a -> a
f f (x-1)
f
let
let f = \y -> \z -> y in \x -> f f (x-1)
也很好打字。推断的类型f
是forall a b. a -> b -> a
(这是显而易见的类型,\y -> \z -> y
不受表达式其余部分的影响)。整体表达式有 type forall a b. Integer -> a -> b -> a
。
相反,如果f
是通过 lambda 表达式引入的,那么情况就不同了,表达式f f (x-1)
确实会影响 的推断类型f
。例如,较大的表达式:
\f -> \x -> f f (x-1)
在 HM 中是错误类型的,因此不会f
为整个表达式推断类型。如果双 lambda 的主体发生变化,则可以f
根据表达式推断出不同的 HM 类型:
\f -> \x -> f (f x x) (x-1) -- f :: Integer -> Integer -> Integer
\f -> \x -> f x + f x -- f :: forall a. a -> Integer
f
Sooooo ...总之,仅从表达式中无法推断出任何类型,f f (x-1)
因为上下文不足。在f
由let
表达式引入的上下文中, 的类型f
将严格从 的定义中推断出来f
,只要类型与表达式 兼容f f (x-1)
,类型推断就会成功。相反,在f
由 lambda 表达式引入的上下文中,表达式f f (x-1)
是错误类型的,因此无法推断出 for 的f
类型。
要回答这个问题,您可以应用类型推断算法。非正式地讨论它,您可以先说明这一点,f :: a -> b -> c
因为它需要两个咖喱论点。您也可以推断出约束Num b
,因为x - 1
, 所以f :: Num b => a -> b -> c
。如果您知道类型x
是什么,可能会更多。但仅此而已。
例如,函数可以定义为f g x = undefined
,在这种情况下,它的两个参数都被丢弃,并且返回类型不与任何输入类型统一。如果f
有一个函数体,那么你可以推断出更多。