在最近的工作表中,我被要求解释为什么f
中的函数f g x = g (g x) x
没有类型。
我对Haskell很陌生,我很不清楚如何在不知道任何有关函数的细节的情况下计算出左右表达式的关联顺序。似乎g
应定义为:
g :: a -> b
,假设x
的类型为a
- 但这似乎会立即导致RHS问题,g (g x) x
似乎意味着{{1}需要2个参数,一个类型为g
,另一个类型为b
。此外,我还坚持如何阅读LHS,即a
接受2个参数:函数f
和变量g
还是简单地接受1个参数,x
?
我想知道是否有人可以告诉我应该如何阅读这些表达方式?
答案 0 :(得分:7)
为了能够回答这样的问题,你需要像Haskell编译器一样思考。我们有一个功能。
f g x = g (g x) x
为了使其输入良好,我们需要找到f
,g
和x
的类型。现在,f
有两个参数,所以
f :: a -> b -> c
g :: a
x :: b
我们也知道g x
必须有意义作为表达式(我们会说g x
是"格式良好"),所以g
必须是一个函数可以应用x
。
g :: b -> t0
目前,t0
是一个类型变量。我们不知道g x
的结果;我们只知道它有意义并且#34;。现在,外部g (g x) x
也必须有意义。因此,g
必须是我们可以应用的类型g x
(其类型为t0
,如前所述)和x
(其类型为b
)能够获得类型c
的结果(函数的返回类型)。所以
g :: t0 -> b -> c
现在,问题发生了。我们看到g
有三个声明:a
,b -> t0
和t0 -> b -> c
。为了让g
拥有所有这三种类型,他们必须统一。也就是说,通过插入某些变量的值,它们必须能够变得相同。 a
没有任何问题,因为它是一个自由变量并且不依赖于任何其他变量,所以我们可以设置"它到我们想要的任何东西。因此b -> t0
和t0 -> b -> c
必须相同。在Haskell中,我们将其写为
(b -> t0) ~ (t0 -> b -> c)
圆括号(在类型中)是右关联的,因此这相当于
(b -> t0) ~ (t0 -> (b -> c))
为了使两个函数类型相同,它们的参数必须相同,所以
b ~ t0
t0 ~ (b -> c)
通过传递属性,这意味着
b ~ (b -> c)
所以b
是一种将自己作为参数的类型。这就是Haskell所说的无限类型,并且当前标准不允许这样做。因此,为了使您编写的函数可以接受,b
必须是Haskell中不存在的类型。因此,f
不是有效的Haskell函数。
答案 1 :(得分:6)
相关规则是:
应用程序被写为并列,即JsPath.read
将f x
应用于f
。
括号用于分隔表达式,而不是用于函数应用程序,即x
将f (g x)
应用于f
。 (g x
本身就是g x
对g
的应用。)
应用程序关联到左侧,即x
。
将这些放在一起,我们可以看到f x y = (f x) y
,即g (g x) x = (g (g x)) x
到g (g x)
的应用,其中x
本身就是g (g x)
的应用到g
。
我还应该提到Haskell中的所有函数都只有一个参数。在一个函数看起来有多个参数的地方,确实存在一些问题:
g x
换句话说,f x y z = ((f x) y) z
是一个函数,它接受一个参数并返回一个接受参数的函数,并返回一个接受参数并返回一个值的函数。你可能会看到为什么我们有时候更喜欢撒谎并说一个函数需要多个参数,但从技术上讲它并不正确。 “接受多个参数”的函数实际上是一个返回函数的函数,它可以返回一个函数,依此类推。
答案 2 :(得分:5)
package stack;
public class info {
private String namn;
public void setTitel(String film) {
namn=film;
}
public String getTitel() {
return namn;
}
public void filmnamn() {
System.out.printf("Movie title: %s", getTitel());
}
}
解析为a b c
(a b) c
表示f x y = ...
f = \x y -> ...
表示\x y -> ...
因此
\x -> (\y -> ...)
装置
f g x = g (g x) x
现在让我们尝试派生f = \g -> (\x -> (g (g x)) x)
的类型。让我们给它一个名字:
f
但究竟是什么f :: ta
? ta
被定义为lambda,因此其类型涉及f
(它是一个函数):
->
即。对于某些类型\g -> (\x -> (g (g x)) x) :: tb -> tc
g :: tb
\x -> (g (g x)) x :: tc
和\g -> ...
,tb -> tc
的类型为tb
,tc
(参数)的类型为g
,并且结果的类型(函数体)是tb
。
由于整个事情都与tc
绑定,我们有
f
我们还没有完成ta = tb -> tc
:
tc
与
\x -> (g (g x)) x :: td -> te
x :: td
(g (g x)) x :: te
函数体(我们称之为tc = td -> te
的类型)包含一个函数(必须是)对变量te
的应用。由此得出:
x
,因为
g (g x) :: td -> te
再次向下钻,我们有
x :: td
(g (g x)) x :: te
因为将g :: tf -> (td -> te)
g x :: tf
应用于g
必须包含g x
类型。最后,
td -> te
,因为
g :: td -> tf
现在我们有x :: td
g x :: tf
的两个方程式:
g
因此
g :: tf -> (td -> te)
g :: td -> tf
这里我们遇到了一个问题:tf = td
td -> te = tf
tf -> te = tf
是根据自身定义的,给出类似
tf
即。一个无限大的类型。这是不允许的,这就是tf = (((((... -> te) -> te) -> te) -> te) -> te) -> te
没有有效类型的原因。