我有两个功能
(\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
答案 0 :(得分:3)
(\f b -> (\a -> a) f b b)
的类型让我们尝试导出表达式的类型:
(\f b -> (\a -> a) f b b)
或更详细:
(\f -> (\b -> (((\a -> a) f) b) b))
我们在这里看到这是一个带有两个参数的函数(从技术上讲,一个函数总是带有一个参数,然后该函数的结果可以带有另一个参数,但是让我们假设如果我们谈论“两个参数” ,我们指的是这样的构造。)
因此,参数分别为f
和b
,最初我们对这些参数了解不多,所以我们给它们分配了一个类型,表达式为:
f :: g
b :: h
(\f b -> (\a -> a) f b b) :: g -> (h -> i)
因此,我们创建了三种类型g
,h
和i
(在这里我使用了a
,b
和c
以外的其他标识符,因为这可能会导致变量混淆。
但是我们还没有做完,因为表达式本身可以对类型的行为引入更多的约束。例如,我们看到一个lambda表达式:\a -> a
,它的类型显然是:
\a -> a :: j -> j
接下来,我们看到一个函数应用程序,其中以\a -> a
作为函数,并且以f
作为参数,这意味着g ~ j
(g
和j
是相同的类型),而(\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
,因为0
是Num
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
,而(+)
则是一个构造将两个功能加在一起的新功能的方法。