为什么部分应用具有不同签名的功能有效?
以Control.Monad.join
为例:
GHCi> :t (=<<)
(=<<) :: Monad m => (a -> m b) -> m a -> m b
GHCi> :t id
id :: a -> a
GHCi> :t (=<<) id
(=<<) id :: Monad m => m (m b) -> m b
为什么它接受id :: a -> a
代替(a -> m b)
参数,因为它们明显不同?
答案 0 :(得分:10)
=<<
的类型签名表示第一个参数是从a
(任何)到b
的monad的函数。
嗯,m b
算什么了,对吗?因此,我们可以在m b
中替换a
:
(=<<) :: Monad m => (m b -> m b) -> m (m b) -> m b
id
类型表示它是从任何东西到同一个东西的函数。因此,如果我们在m b
(不忘记monad约束),我们得到:
id :: Monad m => m b -> m b
然后你可以看到类型匹配。
答案 1 :(得分:3)
这里使用的一些有用的概念:
a
的每个实例替换为任何其他类型a
,可以将具有变量t
的任何类型转换为其他类型。因此,如果您的类型为a -> b -> c
,则可以将a -> d -> c
替换为a -> b -> Int
或b
来获取类型d
或类型c
分别为Int
。a -> b
和c -> d
是等效的(a
〜c
,b
〜d
)。t
可以转换为t'
类型,但t'
无法转换回t
,那么我们会说t'
1}}是t
的特化。例如,a -> a
是a -> b
。现在,通过这些非常有用的概念,您的问题的答案非常简单:即使函数&#34;原生&#34;类型不完全匹配,它们是兼容的,因为它们可以被重写或专门用于获得完全匹配。 Matt Fenwick的回答显示了针对这种情况的专业化。
答案 2 :(得分:2)
它尝试将a
与m b
统一起来,并简单地决定a
必须是m b
,因此(=<<)
的类型(假设{ {1}})为a ~ m b
,一旦您将其应用于Monad m => (mb -> m b) -> m (m b) -> m b
,您就会被id
留下。