Cai*_*inG 25 haskell types type-signature
我正在自学Haskell,我想知道以下类型的签名:
Prelude> :t ($)
($) :: (a -> b) -> a -> b
Prelude>
Run Code Online (Sandbox Code Playgroud)
我应该如何解释(没有双关语意)?
一个半相似的结果也证明是令人费解的:
Prelude> :t map
map :: (a -> b) -> [a] -> [b]
Prelude>
Run Code Online (Sandbox Code Playgroud)
bhe*_*ilr 31
我会先说map
.该map
函数将操作应用于列表中的每个元素.如果我有
add3 :: Int -> Int
add3 x = x + 3
Run Code Online (Sandbox Code Playgroud)
然后我可以使用以下方法将其应用于整个Int
s 列表map
:
> map add3 [1, 2, 3, 4]
[4, 5, 6, 7]
Run Code Online (Sandbox Code Playgroud)
所以,如果你看一下类型签名
map :: (a -> b) -> [a] -> [b]
Run Code Online (Sandbox Code Playgroud)
你会看到第一个参数是(a -> b)
,它只是一个函数,它接受一个a
并返回一个b
.第二个参数是[a]
类型值的列表,a
返回类型[b]
是类型值的列表b
.因此,在简单的英语中,该map
函数将函数应用于值列表中的每个元素,然后将这些值作为列表返回.
这是什么使map
一个高阶函数,它接受一个函数作为参数,并做东西吧.另一种方法map
是在类型签名中添加一些括号来实现它
map :: (a -> b) -> ([a] -> [b])
Run Code Online (Sandbox Code Playgroud)
所以,你也可以把它看成是从转换功能的功能a
来b
为从功能[a]
到[b]
.
该函数($)
具有类型
($) :: (a -> b) -> a -> b
Run Code Online (Sandbox Code Playgroud)
并使用像
> add3 $ 1 + 1
5
Run Code Online (Sandbox Code Playgroud)
它所做的是采取什么是对正确的,在这种情况下1 + 1
,并把它传递给函数在左边,在这里add3
.为什么这很重要?它具有方便的固定性或运算符优先级,使其等效于
> add3 (1 + 1)
Run Code Online (Sandbox Code Playgroud)
因此,无论如何,在传递给左边之前,基本上将其包裹在括号中.这使得将多个函数链接在一起非常有用:
> add3 $ add3 $ add3 $ add3 $ 1 + 1
Run Code Online (Sandbox Code Playgroud)
比...更好
> add3 (add3 (add3 (add3 (1 + 1))))
Run Code Online (Sandbox Code Playgroud)
因为你不必关闭括号.
好吧,正如已经说过的那样,$
如果你只是忘记了currying并且在C++中看到它就可以很容易理解
template<typename A, typename B>
B dollar(std::function<B(A)> f, A x) {
return f(x);
}
Run Code Online (Sandbox Code Playgroud)
但实际上,除了将函数应用于值之外,还有更多内容!的签名之间的明显的相似性$
,并map
具有实际上是一个相当深类别的理论意义:两者都是函子的同态作用的例子!
在我们一直使用的Hask类别中,对象是类型.(这有点混乱,但不要担心).态射是函数.
最着名的(endo-)仿函数是那些具有同名类型实例的仿函数.但实际上,在数学上,仿函数只是将对象映射到对象和态射到态射1的东西.map
(双关语,我想!)是一个例子:它接受一个对象(即类型)A
并将其映射到一个类型[A]
.而且,对于任何两种类型的A
和B
,它需要一个态射(即功能)A -> B
,并将其映射到类型的对应列表的功能[A] -> [B]
.
这只是仿函数类签名操作的一个特例:
fmap :: Functor f => (a->b) -> (f a->f b)
Run Code Online (Sandbox Code Playgroud)
数学并不要求这个fmap
有名字.所以也可以有身份函子,它只是为自己分配任何类型.并且,每个态度对自己:
($) :: (a->b) -> (a->b)
Run Code Online (Sandbox Code Playgroud)
"身份"显然更为普遍存在,您也可以将任何类型的值映射到自己.
id :: a -> a
id x = x
Run Code Online (Sandbox Code Playgroud)
当然,可能的实施就是这样
($) = id
Run Code Online (Sandbox Code Playgroud)
1心灵,不是映射物体和态射的任何东西都是一个算子......它确实需要满足算子定律.
($)
只是功能应用.它获取类型的函数,类型a->b
的参数a
,应用函数并返回类型的值b
.
map
是阅读函数类型签名有助于理解它的一个很好的例子.map
第一个参数是一个获取a
和返回的函数,b
它的第二个参数是一个类型列表[a]
.因此map
将类型函数应用于值a->b
列表a
.结果类型确实是类型[b]
- b
值列表!
(a->b)->[a]->[b]
可以解释为"接受一个函数和一个列表并返回另一个列表",还可以解释为"接受一个类型的函数a->b
并返回另一个类型的函数[a]->[b]
".当你这样看时,map
"升级"f(在这种情况下通常使用术语"提升")来处理列表:if double
是一个加倍整数map double
的函数,那么是一个加倍列表中每个整数的函数.
归档时间: |
|
查看次数: |
7154 次 |
最近记录: |