【问题标题】:Explanation of partial application - join部分应用说明-join
【发布时间】:2012-08-13 16:24:18
【问题描述】:
为什么不同签名的函数的部分应用有效?
以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) 参数,因为它们明显不同?
【问题讨论】:
标签:
haskell
types
partial-application
【解决方案1】:
=<< 的类型签名表明第一个参数是从 a(任何东西)到 b 的单子的函数。
嗯,m b 算什么,对吧?所以我们可以用m b 替换每个a:
(=<<) :: Monad m => (m b -> m b) -> m (m b) -> m b
ids 类型表示它是从任何事物到相同事物的函数。因此,如果我们加入 m b(不要忘记 monad 约束),我们会得到:
id :: Monad m => m b -> m b
然后你就可以看到类型匹配了。
【解决方案2】:
一些有用的概念在这里使用:
- 具有变量
a 的任何类型都可以通过将a 的每个实例替换为任何其他类型t 来转换为不同的类型。因此,如果您有a -> b -> c 类型,则可以通过将b 替换为d 或c 分别替换为Int 来获得a -> d -> c 类型或a -> b -> Int 类型。
- 可以通过替换相互转换的任何两种类型都是等价的。例如,
a -> b 和 c -> d 是等价的(a ~ c、b ~ d)。
- 如果
t 类型可以转换为t' 类型,但t' 不能转换回t,那么我们说t' 是一个特化t。例如,a -> a 是 a -> b 的特化。
现在,有了这些非常有用的概念,您的问题的答案就很简单了:即使函数的“本机”类型不完全匹配,它们也是兼容的,因为它们可以被重写或专门化以获得精确匹配。 Matt Fenwick 的回答显示了针对这种情况的专业化。
【解决方案3】:
它试图将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。