f g x 的类型 = g . g x

6

我不清楚为什么定义的函数是这样的:

f g x = g . g x

具有类型

f :: (b -> a -> b) -> b -> a -> a -> b

我原本以为它应该是某种类型。
f :: (t -> t) -> t -> t

有人能向我解释一下这个表达式是如何分解的吗?谢谢!

2个回答

11

注意,函数应用具有最高优先级; 操作符稍后执行。

因此,术语 g . g x 首先将 g 应用于 x,然后组合结果和 g 本身。如果 x 的类型为 b,那么 g 必须具有类型 b -> c。由于我们将 gg x(后者类型为 c)组合 (将其返回类型设为 b 的函数类型),因此 c 必须是返回类型为 b 的函数类型,因此 c = a -> b。现在,g 的类型为 b -> a -> bg . g x 的类型为 a -> (a -> b)f 的类型恰好为 (b -> a -> b) -> b -> a -> a -> b

如果您想要类似于 (a -> a) -> a -> a 的东西,您可以尝试其中一个。

f g x = g (g x)
f g x = (g . g) x
f g x = g . g $ x
f g = g . g

我明白 g 是类型 (b -> a -> b),但是如何推断出 g x 的类型? - SuspiciousPineapple
@SendMeMemes,请查看组合运算符(y -> z) -> (x -> y) -> x -> z的签名,并将其与其参数的类型“g”和“g x”进行匹配。 - lisyarus

2
理解点运算符 ((.) 操作符),它的类型是关键。
(.) :: (b -> c) -> (a -> b) -> a -> c

需要两个带有一个参数的函数,并将它们组合在一起。在应用 g x 后,编译器会假设 g 实际上是 g :: a -> b -> c,以满足 (.) :: (b -> c) -> (a -> b) -> a -> c 的签名要求,该函数接受一个带有一个参数的函数作为第一个参数和一个带有一个参数的函数作为第二个参数。否则,代码将无法编译。

最后,如果您想要签名 f :: (t -> t) -> t -> t,您需要像这样:

λ> let applyTwice g = g.g
λ> :t applyTwice
applyTwice :: (a -> a) -> a -> a
λ> applyTwice (*2) 3
12

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接