我正在自学Haskell,我想知道以下类型的签名:
Prelude> :t ($)
($) :: (a -> b) -> a -> b
Prelude>
我应该如何解释(没有双关语)?
一个半相似的结果也证明是令人费解的:
Prelude> :t map
map :: (a -> b) -> [a] -> [b]
Prelude>
答案 0 :(得分:30)
我将以map
开头。 map
函数将操作应用于列表中的每个元素。如果我有
add3 :: Int -> Int
add3 x = x + 3
然后我可以使用Int
将其应用于map
的整个列表:
> map add3 [1, 2, 3, 4]
[4, 5, 6, 7]
所以,如果你看一下类型签名
map :: (a -> b) -> [a] -> [b]
您会看到第一个参数是(a -> b)
,它只是一个带a
并返回b
的函数。第二个参数是[a]
,它是类型a
的值列表,以及返回类型[b]
,类型b
的值列表。所以用简单的英语,map
函数将一个函数应用于值列表中的每个元素,然后将这些值作为列表返回。
这是使map
成为高阶函数的原因,它将函数作为参数并对其进行处理。查看map
的另一种方法是在类型签名中添加一些括号以使其成为
map :: (a -> b) -> ([a] -> [b])
因此,您也可以将其视为将函数从a
转换为b
到从[a]
到[b]
的函数的函数。
函数($)
的类型为
($) :: (a -> b) -> a -> b
和
一样使用> add3 $ 1 + 1
5
所有这一切都取决于正确的内容,在本例中为1 + 1
,并将其传递给 left 上的函数,这里add3
。为什么这很重要?它有一个方便的 fixity 或运算符优先级,使其等同于
> add3 (1 + 1)
因此无论如何,在传递给左边之前,基本上包含在括号中。这使得将多个函数链接在一起非常有用:
> add3 $ add3 $ add3 $ add3 $ 1 + 1
比
更好> add3 (add3 (add3 (add3 (1 + 1))))
因为你不必关闭括号。
答案 1 :(得分:7)
好吧,正如已经说过的那样,$
可以很容易地被理解,如果你只是忘记了currying并且看起来就像在C ++中那样
template<typename A, typename B>
B dollar(std::function<B(A)> f, A x) {
return f(x);
}
但实际上,除了将函数应用于某个值之外,还有更多内容! $
和map
的签名之间的明显相似性实际上是一个非常深刻的类别理论意义:两者都是仿函数的变形行为的例子!
在我们一直使用的 Hask 类别中,对象是类型。 (That is a bit confusionsome,但不要担心)。态射是函数。
最着名的(endo - )仿函数是那些具有the eponymous type class实例的仿函数。但实际上,在数学上,仿函数只是将对象映射到对象和态射到映射 1 的东西。 map
(双关语,我想!)是一个例子:它接受一个对象(即类型)A
并将其映射到类型[A]
。并且,对于任何两种类型A
和B
,它需要一个态射(即函数)A -> B
,并将其映射到类型为[A] -> [B]
的相应列表函数。< / p>
这只是仿函数类签名操作的一个特例:
fmap :: Functor f => (a->b) -> (f a->f b)
数学并不要求这个fmap
有一个名字。因此,还可以使用身份仿函数,它只是为自己分配任何类型。并且,每个态度都是自己的:
($) :: (a->b) -> (a->b)
&#34;标识&#34;显然更普遍存在,你也可以将任何类型的值映射到自己。
id :: a -> a
id x = x
当然,可能的实施是
($) = id
1 介意,不是任何映射对象和态射是一个仿函数......它确实需要满足functor laws。
答案 2 :(得分:6)
($)
只是功能应用。它获得类型a->b
的函数,类型为a
的参数,应用函数并返回类型为b
的值。
map
是阅读函数类型签名有助于理解它的一个很好的例子。 map
的第一个参数是一个带a
并返回b
的函数,它的第二个参数是[a]
类型的列表。
因此map
将a->b
类型的函数应用于a
值列表。结果类型确实是[b]
类型 - b
值的列表!
(a->b)->[a]->[b]
可以解释为&#34;接受一个函数和一个列表并返回另一个列表&#34;,并且还作为&#34;接受类型a->b
的函数并返回另一个[a]->[b]
&#34;类型的函数。
当你以这种方式看待它时,map
&#34;升级&#34; f(术语&#34; lift&#34;经常在这个上下文中使用)来处理列表:如果double
是一个加倍整数的函数,那么map double
是一个加倍的函数列表中的整数。