Haskell:为什么“ id”使该功能不再单调?

时间:2019-05-04 07:04:59

标签: haskell

我试图理解为什么在下面序列的最后一行添加id会消除单子现象:

Prelude> :t id
id :: a -> a
Prelude> :t Control.Monad.liftM2
Control.Monad.liftM2
  :: Monad m => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r
Prelude> :t  (==)
(==) :: Eq a => a -> a -> Bool
Prelude> :t Control.Monad.liftM2 (==)
Control.Monad.liftM2 (==)
  :: (Monad m, Eq a) => m a -> m a -> m Bool
Prelude> :t Control.Monad.liftM2 (==) id
Control.Monad.liftM2 (==) id :: Eq a => (a -> a) -> a -> Bool
Prelude>

添加id :: a -> a如何以最后一行中的方式更改签名?

2 个答案:

答案 0 :(得分:8)

您正在将类型固定为特定的Monad实例,即“函数阅读器”单子(instance Monad ((->) a))。

id :: a -> a,而您试图将其用作类型m a的参数的参数,因此:

m a  ~  a -> a
m a  ~  (->) a a
m a  ~  ((->) a) a
m    ~  (->) a
a    ~  a

签名的其余部分是:

m a -> m Bool

m ~ (->) a开始,结果类型为:

(->) a a -> (->) a Bool
(a -> a) -> (a -> Bool)
(a -> a) -> a -> Bool

(加上使用Eq a的{​​{1}}约束。)

这在无点代码中非常有用,尤其是在使用==实例的情况下,因为您可以将函数的参数隐式地“扩展”到子计算:

Applicative

答案 1 :(得分:3)

3的签名为liftM2 (==)。因此,这意味着如果我们以(Monad m, Eq a) => m a -> m a -> m Bool作为参数调用此函数,则意味着id :: b -> bm a是同一类型。

b -> b成立的事实并不成问题,因为m ~ (->) b(->) r的一个实例,实际上在GHC.Base source code中我们看到:

Monad

这仅在-- | @since 2.01 instance Monad ((->) r) where f >>= k = \ r -> k (f r) r时才有意义。这里的箭头m ~ (->) b是类型构造函数,(->)(->) a b相同。

因此,这意味着如果我们计算a -> b的类型,则会得出以下信息:

liftM2 (==) id

因此,这意味着liftM2 (==) :: m a -> m a -> m Bool id :: (b -> b) ------------------------------------------- m ~ (->) b, a ~ b 的输出类型为liftM2 (==) id,但是我们需要使用我们获得的知识来“专门化”此类型:liftM2 (==) id :: (Monad m, Eq a) => m a -> m Boolm a,并且(->) ba的类型相同,因此:

b

简而言之,该功能仍然是“ monadic”的,尽管通过使用 liftM2 (==) id :: (Monad m, Eq a) => m a -> m Bool -> liftM2 (==) id :: (Monad m, Eq a) => (b -> a) -> (b -> Bool) -> liftM2 (==) id :: Eq b => (b -> b) -> (b -> Bool) -> liftM2 (==) id :: Eq b => (b -> b) -> b -> Bool 您已经选择了一个特定的monad,因此该功能不再适用于所有类型的monad,仅适用于{{1} } monad。