理解此表达式的Haskell类型

理解此表达式的Haskell类型,haskell,Haskell,我在做哈斯凯尔式的练习,这一次让我很难堪。提供的表达式是: f2 f g h = h.g.f f2的类型显然是: f2 :: (a -> b1) -> (b1 -> b) -> (b -> c) -> a -> c 对于这样一个简短的表达来说,这似乎过于复杂了。有人能解释一下为什么这作为类型是有意义的吗?我发现这些“确定某某表达式的类型”通常有点倒退。类型应该始终放在第一位:如果您想为某项任务编写解决方案,您可以将问题描述表述为类型签名。然后您继续实

我在做哈斯凯尔式的练习,这一次让我很难堪。提供的表达式是:

f2 f g h = h.g.f
f2的类型显然是:

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

对于这样一个简短的表达来说,这似乎过于复杂了。有人能解释一下为什么这作为类型是有意义的吗?

我发现这些“确定某某表达式的类型”通常有点倒退。类型应该始终放在第一位:如果您想为某项任务编写解决方案,您可以将问题描述表述为类型签名。然后您继续实际编写一个实现

在本例中,您将从问题开始:我有三个函数
f
g
h
,以及一个类型的值,我可以传递给
f
。此外,函数具有成对匹配的结果/参数类型。因此签字

f2 :: (α -> β) -> (β -> γ) -> (γ -> δ) -> α -> δ
您现在可以继续并以显式的指向形式实现它,即

f2 f g h x = h (g (f x))
这仍然很简短。毕竟,这是一项相当简单的任务


但是在Haskell中,您可以使用标准合成操作符
,将其缩短。最终实现非常短的事实基本上可以归结为这样一个事实:
f2
基本上做了与
相同的事情,只做了两次。因此,如果您有一个具有复杂类型签名的非常复杂的任务,但发现某个库包含一个几乎可以完成该任务的函数,那么这并不比这更令人惊讶。显然,调用ready build函数将使您的实现比任务复杂度所建议的要短得多,但复杂度仅取决于库函数。

我发现这些“确定某某表达式的类型”通常有点落后。类型应该始终放在第一位:如果您想为某项任务编写解决方案,您可以将问题描述表述为类型签名。然后您继续实际编写一个实现

在本例中,您将从问题开始:我有三个函数
f
g
h
,以及一个类型的值,我可以传递给
f
。此外,函数具有成对匹配的结果/参数类型。因此签字

f2 :: (α -> β) -> (β -> γ) -> (γ -> δ) -> α -> δ
您现在可以继续并以显式的指向形式实现它,即

f2 f g h x = h (g (f x))
这仍然很简短。毕竟,这是一项相当简单的任务


但是在Haskell中,您可以使用标准合成操作符
,将其缩短。最终实现非常短的事实基本上可以归结为这样一个事实:
f2
基本上做了与
相同的事情,只做了两次。因此,如果您有一个具有复杂类型签名的非常复杂的任务,但发现某个库包含一个几乎可以完成该任务的函数,那么这并不比这更令人惊讶。显然,调用现成的构建函数将使您的实现比任务复杂度所建议的要短得多,但复杂度仅仅取决于库函数。

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

为了保持一致性,我已将
b1
重命名为
b
,并更新了其他名称。下面是一个略加编辑的版本:

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

  • 这里,
    f2
    接受类型为
    a->b
    f
    。也就是说,它是一个函数,具有某种类型的输入
    a
    a
    可以是任何东西,这里没有限制)和某种类型的返回值
    b
  • 然后
    f2
    获取
    g
    哪个输入的类型必须匹配
    f
    的输出,因此它是
    b->c
    。它不能像
    e->c
    (其中
    e
    b
    不同),或者代码没有意义,因为它不可能组成
    f。g
  • 对于第三个参数,
    h
    ,类型为
    c->d
    ,情况也完全相同
  • 函数合成的结果(f2返回的内容)是一个新函数,它接受f所做的任何事情,并返回h返回的任何事情,因此它是
    a->d
    。通过这个,我们已经涵盖了整个类型定义

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

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

为了保持一致性,我已将
b1
重命名为
b
,并更新了其他名称。下面是一个略加编辑的版本:

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

  • 这里,
    f2
    接受类型为
    a->b
    f
    。也就是说,它是一个函数,具有某种类型的输入
    a
    a
    可以是任何东西,这里没有限制)和某种类型的返回值
    b
  • 然后
    f2
    获取
    g
    哪个输入的类型必须匹配
    f
    的输出,因此它是
    b->c
    。它不能像
    e->c
    (其中
    e
    b
    不同),或者代码没有意义,因为它不可能组成
    f。g
  • 对于第三个参数,
    h
    ,类型为
    c->d
    ,情况也完全相同
  • 函数合成的结果(f2返回的内容)是一个新函数,它接受f所做的任何事情,并返回h返回的任何事情,因此它是
    a->d
    。通过这个,我们已经涵盖了整个类型定义

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

我同意你的观点,在我的问题中,它与(b->c)等价,但我不明白为什么类型表达式