Haskell函数类型澄清

时间:2017-02-15 08:15:15

标签: haskell

我试图理解函数的Haskell类型变量。我写了一个函数:

applyTwice f x = f(f x)

我试着理解这个函数的类型变量,所以我做了一个:t applyTwice。这就是Haskell解释类型的方式:

applyTwice :: (t -> t) -> t -> t

然后我创建了另一个函数:

applyOnce f x = f x

这次:t Haskell返回

applyOnce :: (t1 -> t) -> t1 -> t

我的问题是

  1. 我们如何阅读/理解这些功能的作用和回归?

  2. 这是applyTwice。如果他们说->左边的任何东西是函数需要的东西,右手边是它返回的东西那么它应该不是 applyTwice :: ((t -> t) -> t) -> t(t -> t)(f x)((t -> t) -> t)f (f x),返回类型为t

  3. 这是applyOnce。为什么函数的类型被解释为applyOnce :: (t1 -> t) -> t1 -> t?因为我们只接受一个函数并返回它的值。它不应该是applyOnce :: (t1 -> t) -> t1吗?
  4. 作为Haskell的初学者,我想在此提出任何建议。

3 个答案:

答案 0 :(得分:4)

我们有:

applyTwice f x = f(f x)

仅当xf x的类型相同时才有意义(否则,您如何将f x传递给需要x的函数? )。因此,您可以说applyTwice需要2个输入参数:

  • 采用类型t并返回相同类型t
  • 的函数
  • 某种类型的输入

现在这个输入可以是任何类型,比如t1。但是您还必须能够将该函数应用于此输入,因此输入的类型也必须为t。放在一起,我们得到签名:

applyTwice :: (t -> t) -> t -> t

请记住,只有最后一个术语是返回类型,而所有其他术语都是输入类型。

现在考虑:

applyOnce f x = f x

简单地说,关于函数f的所有内容都说它应该能够接受任何类型的输入x。除了,applyOnce还应该返回相同的类型,函数应该返回什么类型。因此,我们最终得到您看到的签名:

applyOnce :: (t1 -> t) -> t1 -> t

其中t1可以是任何类型,t可以是任何类型(与t1相同或不同),但要求返回类型f匹配applyOnce的返回类型,由t表示

TL; DR:具有n个输入的函数的函数签名将始终具有(n + 1)个项,最后一个项将是函数的返回类型。

但是,你应该注意到,事实并非如此。 Haskell中的所有函数实际上都采用一个参数,即它们是 curried 。您可以阅读有关此here的更多信息。

答案 1 :(得分:1)

返回类型

函数采用什么参数以及函数返回的内容在Haskell中是一个非常棘手的问题,因为部分应用程序之类的特殊内容。

map函数看起来需要f :: a -> b,转换函数和列表xs :: [a],并返回ys :: [b]的列表,每个元素都为xsf转换一次。但是map也可以看作是一个函数,它接受一个简单的函数f并返回另一个函数,该函数作用于列表并返回列表。

我做的是帮助我推断出参数,首先将顶级->分隔的所有类型分开。例如map

map :: (a -> b) -> [a] -> [b]
-- gives us
-- (a -> b) and [a] and [b]

最右边的类型是传统上称为"返回类型"当调用map并提供其所有参数时。其他类型是我必须使用的参数,例如在函数定义中。

请注意这一点,因为您将看到许多函数定义的示例,这些函数定义不会明确地使用所有参数来定义函数。

来到你的第二个问题,你显然混淆了参数和函数体。考虑applyTwice

applyTwice f x = f (f x)

fxapplyTwice的参数,f (f x)是函数体。现在f必须是一个函数,因为它应用于参数xx的类型可以是任何内容,我们称之为t。现在f必须从tx的类型转到其他内容,称之为s。但现在f x s类型再次成为f的参数,因此编译器可以推导出s ~ t。现在f (f x)再次属于s类型,必须与t相同,这意味着您最终得到:

applyTwice :: (t -> t) -> t -> t

类似地,您可以针对applyOnce尝试此操作,并查看其类型为(t -> s) -> t -> s$只有Prelude

答案 2 :(得分:1)

让我们将Haskell翻译成英文。

applyTwice :: (t -> t) -> t -> t表示 applyTwice 接受从tt和值t的函数,并返回t }。实际上,如果我们查看定义,我们会看到它将函数f应用于值x并再次将结果提供给f。这意味着 - 如类型签名所示 - ff x必须具有相同类型t(因为f仅接受t作为参数)。

相比之下,applyOnce :: (t1 -> t) -> t1 -> t允许f xx分别拥有不同的类型(tt1),因为此功能并非如此。将f(类型为t)的输出反馈回f

我认为这种混淆源于你无法区分f(函数)和f x(函数的结果)这一事实。

最后,您为applyOnce建议的类型(即(t1 -> t) -> t)暗示(nay 肯定)您可以在不提供参数的情况下获得函数的结果;请记住(t1 -> t)是输入类型(函数),t是输出类型。如何在没有f(x)的情况下计算x