Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 为什么这两个函数有不同的类型?_Haskell_Types - Fatal编程技术网

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

Haskell 为什么这两个函数有不同的类型?,haskell,types,Haskell,Types,我有两个功能 (\f b -> (\a -> a) f b b) 及 我试图手动找到这些函数的类型,得到了 (t1 -> t1 -> t2) -> t1 -> t2 及 但是当我使用GHCI使用:t获取类型时,我得到了以下结果 (\f b -> (\a -> a) f b b) :: (t1 -> t1 -> t2) -> t1 -> t2 (\f b -> (\a -> 0) f b b) :: Num (

我有两个功能

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

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

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

但是当我使用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

派生
(\f b->(\a->a)f 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)
(\f b -> (\a -> a) f b b) :: (h -> (h -> i)) -> (h -> i)
因此,我们创建了三种类型
g
h
i
(我在这里使用了
a
b
c
以外的其他标识符,因为这可能会导致变量混淆)

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

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

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

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

g ~ j
g ~ (h -> k)
k ~ (h -> i)
换句话说,表达式的类型是:

f :: g
b :: h
(\f b -> (\a -> a) f b b) :: g -> (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) :: Num (h -> h -> i) => g -> 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 -> a) f b b) :: (h -> h -> i) -> h -> i
(\f b -> (\a -> 0) f b b) :: Num (h -> h -> i) => g -> h -> i
因此,由于我们使用内部lambda表达式
(\a->0)
,因此
f
的类型或值不再相关
(\a->0)f
将始终返回
0
,这应该是一个函数,可以考虑a
b


至少从理论的角度来看,对于一个
Num
(只要它支持应该由
Num
类型实现的函数)的函数没有什么“奇怪”的地方。例如,我们可以实现一个函数
实例Num(a->b->Int)
,从而将
0
视为一个常量函数,始终映射到
0
,并将
(+)
视为构造一个新函数的方法,将两个函数相加。

因为您的函数
\a->0
返回一个数字,而对于
\a->a
,它返回参数,因此输出与输入类型相同。您希望发生什么?您正在指定一个返回类型,即一个数字,因此类型当然会改变!什么特别让你困惑?当然,但是为什么第一个参数现在是
p
而不是
(t1->t1->t2)
@joelfischer:因为第一个参数是
f
(在
\f b->…
),而
f
因此是一个自由参数,因为我们使用它作为
(\a->0)
的参数,这意味着无论我们写什么,它都是“被忽略的”。
fbb
不是代码中的子表达式。函数应用程序是左关联的,因此它解析为
((\a->0)f)b)b