我正在通过Graham Hutton的在Haskell编程,第3章的练习问“什么是类型?”对于函数twice f x = f (f x)
。
我想我理解为什么答案是twice :: (t -> t) -> t -> t
。 (编辑:我做了不了解原因。请参阅我对Paolo答案的评论。)但是,为了实验我写了另一个函数thrice f x = f (f (f x))
。
我绝对不会理解的是thrice
也有thrice :: (t -> t) -> t -> t
类型的原因。
他们按照我期望的方式工作(见下文),但我看不出thrice
的类型是否有意义。
来自ghci
:
>> twice tail [0,1,2,3,4]
[2,3,4]
>> thrice tail [0,1,2,3,4]
[3,4]
答案 0 :(得分:8)
也许更容易看到无点:
twice f = f . f
thrice f = f . f . f
所以你自己组合f
几次。为了能够将f
与自身组合在一起,将f
应用于参数的结果必须具有合适的类型,以便f
可以应用于此。现在,如果你从
f :: a -> r -- argument type -> result type
适用性条件意味着r
必须与a
匹配。对于表示r = a
的类型变量。因此,twice
以及thrice
将函数从某种类型转换为相同类型,并将函数从该类型返回到相同类型,
twice :: (a -> a) -> (a -> a)
thrice :: (a -> a) -> (a -> a)
由于函数类型箭头是右关联的(x -> y -> z = x -> (y -> z)
),因此可以省略类型中的最后一个括号。
答案 1 :(得分:4)
抱歉,也许我不明白你的问题,但是如果你看一下你自己的列表示例,你会看到在两次和三次的情况下,输入是从列表到列表的函数({{1 }}和一个列表(tail
),返回类型是一个列表。
因此[0,1,2,3,4]
和twice
都匹配签名thrice
:从t到t的函数(在您的情况下为(t -> t) -> t -> t
),在(在您的情况下是一个列表)和另一个t(列表)作为回报
答案 2 :(得分:4)
两次的类型表明两次是来自具有域 a 和codomain a 的函数的函数到域 a 和codomain a 的函数。 三次的类型表明三次是来自具有域 a 和codomain a 到函数的函数的函数域 a 和codomain a 。
要了解原因,请考虑推导两次和三次的类型。给定函数 f : a → a 和变量 x ,确定类型的规则f ( fx )声明我们必须首先确定 f 和(fx)的类型,然后应用规则功能应用。确定(fx)类型的规则规定我们必须首先确定 f 和 x 的类型,然后应用规则进行功能应用
首先,因为 f 的类型为 a → a 而 x 的类型为 a ,函数应用程序的规则指出( fx )的类型为 a 。由于 f 的类型为 a → a ,( fx )的类型为 a ,函数应用程序的规则指出 f ( fx )的类型为 a 。函数应用程序规则的另一个应用是 f ( f ( f x ))类型 a 。如您所见,为应用程序重复应用规则, f n x 将为所有<的 a 类型< em> n ∈ℕ。
其次,函数抽象规则指出,如果 x :τ, M :τ'和 x 在 M 中不会自由发生,然后抽象λ x :τ。 M 的类型为τ→τ'。我们有术语 f ( fx )和 f ( f ( fx ))类型为 a 的变量和类型为 a 的变量 x 。因此,抽象λ x : a 。 f ( f x )和λ x : a 。 f ( f ( f x ))都有类型 a → a 。最后,由于 f : a → a ,再次应用函数抽象规则得到λ f :< em> a → a 。 λ x : a 。 f ( f x )和λ f : a → a 。 λ x : a 。 f ( f ( fx ))具有类型( a → a )→ ( a → a )。
正如您所看到的,Haskell的类型系统过于无法表达两次将函数 f 应用于参数 x 两次,而三次将函数 f 应用于参数 x 三次。 可以表达的是两次和三次接受函数作为输入并从术语 x 到术语 y ,类型 a 。此函数是λ x : a 。 f ( f x )两次和λ x : a 。 f ( f ( f x ))三次。
我建议阅读Haskell类型系统所基于的多态性λ演算的简短介绍。这将呈现打字关系,并且可能引导读者证明某些术语具有某些类型。
答案 3 :(得分:1)
试图赢得简洁比赛:
类型签名指定函数的输入(参数)的类型,以及函数返回的值 因此,函数类型签名中的项目数是(n + 1),其中n是函数所用参数的数量。