2017-05-07 31 views
0

我正在做一个Haskell类型的练习,这个难题让我难堪。所提供的表达式为:了解这个表达式的Haskell类型

f2 f g h = h.g.f 

而对于f2的类型是显然:

f2 :: (a -> b1) -> (b1 -> b) -> (b -> c) -> a -> c 

这似乎对于这样的短表达过于复杂。有人可以解释为什么这种类型有意义吗?

回答

3

你为什么觉得这很复杂?它只是一个接受三个函数(匹配类型)并返回一个新函数的函数。

我已将b1更名为b并更新了其他名称以保持一致性。因此,这里是一个稍微修改版本:

f2 :: (a -> b) -> (b -> c) -> (c -> d) -> a -> d

  • 这里,f2需要f其类型a -> b。意思是说,它是一个函数,用一些类型的输入aa可以是任何东西,这里没有限制)和一些返回值类型b
  • 然后f2需要g哪个输入的类型必须匹配f的输出,所以它是b -> c。它不可能像e -> c(其中eb不同),或者代码无效,因为它不可能组成f . g
  • 对于第三个参数hc -> d类型完全相同。
  • 函数组成的结果(f2返回的结果)是一个新函数,它接受任何f并返回任何h返回值,因此它是a -> d。有了这个,我们已经涵盖了整个类型定义。

基本上,这是一个相对较长的定义,但我认为它是一个非常简单的本质。

+0

第四点是什么让我困惑,我理解到那里。具体而言,我不会得到“接受任何回报,并返回任何回报”, –

+0

好吧,我盯着它看了一会儿,我现在明白了为什么我给出的例子中的d或c会在那里,但我是仍然有些困惑,为什么必须在那里。 –

+1

@KaiD:考虑一个稍微简单的例子'f3 f g = f。 g',其类型为'f3 ::(a - > b) - >(b - > c) - > a - > c'。它有两个函数('f'和'g'),并产生一个新函数。在'a - > c'部分,它表示生成的函数的输入类型将是'a' - 与'f'相同的类型。返回类型是'd',与'g '回报。例如,如果'f'接受一个字符串并返回一个整数,'g'接受一个整数并返回一个布尔值,那么'f3'的结果必须接受一个字符串并返回一个布尔值 - 这就是类型声明所说的。说得通? – drdaeman

3

我发现这些“确定这样和那样的表达的类型”通常有点倒退。类型应该总是优先:你想为某个任务编写一个解决方案,你将问题描述作为一个类型签名。 然后你继续前进,实际编写一个实现。

在这种情况下,你的问题开始:我有三个功能fgh,和类型的值,我可以传递给f。此外,函数具有成对匹配的结果/参数类型。因此,签名

f2 :: (α -> β) -> (β -> γ) -> (γ -> δ) -> α -> δ 

现在你可以去和明确的尖形式实现这一点,即

f2 f g h x = h (g (f x)) 

这仍然是相当短暂的。毕竟,这是一项非常简单的任务!

但是在Haskell中,通过使用标准组合运算符.可以使它更短。最终实施非常短的事实基本上是由于f2.基本相同,只是两次。所以这并不比使用复杂类型签名的非常复杂的任务更令人惊讶,但是发现一些包含几乎完成任务的函数的库。很显然,调用这个ready-build函数会比任务复杂度提供的实现要短得多,但复杂性只能推迟到库函数。

+0

我和你在同一个问题上等同于(b - > c),但我不明白为什么类型表达式不仅仅在那里结束。为什么我需要接下来的两个,就我而言 - > a - > c? –

+1

@KaiD因为类型表达式覆盖了返回值。 'a - > c'('α - >δ')是'f2'返回的一种类型。 – drdaeman