为什么具有不同签名的函数的部分应用有效?
举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)
论点,因为它们显然不同?
为什么具有不同签名的函数的部分应用有效?
举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)
论点,因为它们显然不同?
=<<
的类型签名说第一个参数是从a
(任何东西)到 .monad 的函数b
。
好吧,m b
算什么,对吧?所以我们可以替换m b
为 every a
:
(=<<) :: Monad m => (m b -> m b) -> m (m b) -> m b
id
s 类型表示它是从任何事物到相同事物的函数。因此,如果我们加入m b
(不要忘记 monad 约束),我们会得到:
id :: Monad m => m b -> m b
然后你可以看到类型匹配。
一些有用的概念在这里使用:
a
来转换为不同的类型。因此,如果您有 type ,您可以通过分别替换with或with来获取 type或 type 。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'
是 的一个特化t
。例如,a -> a
是a -> b
.现在,有了这些非常有用的概念,您的问题的答案就很简单了:即使函数的“本机”类型不完全匹配,它们也是兼容的,因为它们可以被重写或专门化以获得精确匹配。Matt Fenwick 的回答显示了针对这种情况的专业化。
它试图与 统一a
,m b
并简单地决定它a
必须是m b
,所以(=<<)
(在假设下a ~ m b
)的类型是Monad m => (mb -> m b) -> m (m b) -> m b
,一旦你将它应用到id
,你就剩下 了Monad m => m (m b) -> m b
。