我试图理解函数的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
我的问题是
我们如何阅读/理解这些功能的作用和回归?
这是applyTwice
。如果他们说->
左边的任何东西是函数需要的东西,右手边是它返回的东西那么它应该不是
applyTwice :: ((t -> t) -> t) -> t
? (t -> t)
为(f x)
,((t -> t) -> t)
为f (f x)
,返回类型为t
。
applyOnce
。为什么函数的类型被解释为applyOnce :: (t1 -> t) -> t1 -> t
?因为我们只接受一个函数并返回它的值。它不应该是applyOnce :: (t1 -> t) -> t1
吗?作为Haskell的初学者,我想在此提出任何建议。
答案 0 :(得分:4)
我们有:
applyTwice f x = f(f x)
仅当x
和f x
的类型相同时才有意义(否则,您如何将f x
传递给需要x
的函数? )。因此,您可以说applyTwice
需要2个输入参数:
现在这个输入可以是任何类型,比如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]
的列表,每个元素都为xs
每f
转换一次。但是map
也可以看作是一个函数,它接受一个简单的函数f
并返回另一个函数,该函数作用于列表并返回列表。
我做的是帮助我推断出参数,首先将顶级->
分隔的所有类型分开。例如map
:
map :: (a -> b) -> [a] -> [b]
-- gives us
-- (a -> b) and [a] and [b]
最右边的类型是传统上称为"返回类型"当调用map并提供其所有参数时。其他类型是我必须使用的参数,例如在函数定义中。
请注意这一点,因为您将看到许多函数定义的示例,这些函数定义不会明确地使用所有参数来定义函数。
来到你的第二个问题,你显然混淆了参数和函数体。考虑applyTwice
:
applyTwice f x = f (f x)
f
和x
是applyTwice
的参数,f (f x)
是函数体。现在f
必须是一个函数,因为它应用于参数x
。 x
的类型可以是任何内容,我们称之为t
。现在f必须从t
,x
的类型转到其他内容,称之为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 接受从t
到t
和值t
的函数,并返回t
}。实际上,如果我们查看定义,我们会看到它将函数f
应用于值x
并再次将结果提供给f
。这意味着 - 如类型签名所示 - f
和f x
必须具有相同类型t
(因为f
仅接受t
作为参数)。
相比之下,applyOnce :: (t1 -> t) -> t1 -> t
允许f x
和x
分别拥有不同的类型(t
和t1
),因为此功能并非如此。将f
(类型为t
)的输出反馈回f
。
我认为这种混淆源于你无法区分f
(函数)和f x
(函数的结果)这一事实。
最后,您为applyOnce
建议的类型(即(t1 -> t) -> t
)暗示(nay 肯定)您可以在不提供参数的情况下获得函数的结果;请记住(t1 -> t)
是输入类型(函数),t
是输出类型。如何在没有f(x)
的情况下计算x
?