为什么这两个函数的类型不同?

时间:2018-07-29 11:57:42

标签: haskell types

我有两个功能

(\f b -> (\a -> a) f b b)

(\f b -> (\a -> 0) f b b)

我试图手动找到这些函数的类型并得到

(t1 -> t1 -> t2) -> t1 -> t2

Num t3 => (t1 -> t1 -> t2) -> t1 -> t3

但是当我使用GHCI通过:t获取类型时,会得到以下内容

(\f b -> (\a -> a) f b b) :: (t1 -> t1 -> t2) -> t1 -> t2
(\f b -> (\a -> 0) f b b) :: Num (t1 -> t1 -> t2) => p -> t1 -> t2

我不明白将\a -> a更改为\a -> 0会如何将第一个参数从(t1 -> t1 -> t2)更改为p

1 个答案:

答案 0 :(得分:3)

导出(\f b -> (\a -> a) f b b)的类型

让我们尝试导出表达式的类型:

(\f b -> (\a -> a) f b b)

或更详细:

(\f -> (\b -> (((\a -> a) f) b) b))

我们在这里看到这是一个带有两个参数的函数(从技术上讲,一个函数总是带有一个参数,然后该函数的结果可以带有另一个参数,但是让我们假设如果我们谈论“两个参数” ,我们指的是这样的构造。)

因此,参数分别为fb,最初我们对这些参数了解不多,所以我们给它们分配了一个类型,表达式为:

f :: g
b :: h
(\f b -> (\a -> a) f b b) :: g -> (h -> i)

因此,我们创建了三种类型ghi(在这里我使用了abc以外的其他标识符,因为这可能会导致变量混淆。

但是我们还没有做完,因为表达式本身可以对类型的行为引入更多的约束。例如,我们看到一个lambda表达式:\a -> a,它的类型显然是:

\a -> a :: j -> j

接下来,我们看到一个函数应用程序,其中以\a -> a作为函数,并且以f作为参数,这意味着g ~ jgj是相同的类型),而(\a -> a) f的类型为(\a -> a) f :: g

但是我们还没有完成,因为(\a -> a) f的结果现在在带有b的函数应用程序中充当函数,因此这意味着g实际上是一个函数,输入类型为h,并且某些类型(当前未知的输出类型),因此:

g ~ (h -> k)

因此(\a -> a) f b的类型为k,但我们仍未完成,因为我们执行了另一个以(\a -> a) f b为函数的函数应用程序(类型k),并且以b作为参数,因此实际上意味着k是一个函数,其中h是参数类型,结果是表达式的类型,所以i 。这意味着我们有:

g ~ j
g ~ (h -> k)
k ~ (h -> i)

换句话说,表达式的类型是:

(\f b -> (\a -> a) f b b) :: (h -> (h -> i)) -> (h -> i)

或更简单:

(\f b -> (\a -> a) f b b) :: (h -> h -> i) -> h -> i

导出(\f b -> (\a -> 0) f b b)的类型

推导的第一步大致相同,我们首先介绍一些类型变量:

f :: g
b :: h
(\f b -> (\a -> 0) f b b) :: g -> (h -> i)

,现在我们开始进行推断。我们首先推断(\a -> 0)的类型。这是一个函数,类型为Num l => j -> l,因为0Num ber,但是它可以是任何Num类型,并且与参数的类型无关a

接下来,我们看到有一个函数调用,其中(\a -> 0)作为函数,f作为参数,因此得出结论g ~ j。此函数调用的结果类型为(\a -> 0) f :: Num l => l

现在,我们看到另一个函数调用,其中(\a -> 0) f作为函数,b作为参数。因此,我们得出的结论是l是一个函数(所以l ~ (h -> k))。

最后一个函数调用是使用(\a -> 0) f b :: k作为函数,并再次使用b作为参数。这意味着k是函数k ~ h -> i。因此,我们获得以下类型和相等性:

f :: g
b :: h
(\a -> 0) :: Num l => j -> l
(\f b -> (\a -> 0) f b b) :: g -> (h -> i)
g ~ j
l ~ (h -> k)
k ~ (h -> i)

表达式的类型因此是:

(\f b -> (\a -> 0) f b b) :: g -> (h -> i)

或更具体:

(\f b -> (\a -> 0) f b b) :: Num (h -> (h -> i)) => g -> (h -> i)

或更简单:

(\f b -> (\a -> 0) f b b) :: Num (h -> h -> i) => g -> h -> i

因此,由于我们使用内部(\a -> 0)作为lambda表达式,因此f type value 也不再相关。 (\a -> 0) f将始终返回0,这应该是一个函数,可以考虑b

至少从理论上讲,Num的功能没有什么“奇怪”的(只要它支持应由Num类型实现的功能)。例如,我们可以实现一个函数instance Num (a -> b -> Int),因此将0看作是一个常数函数,该函数始终映射到0,而(+)则是一个构造将两个功能加在一起的新功能的方法。