haskell 为什么允许在一个接受两个monad值的函数上使用ap,而不是在一个封装在monad中的函数上?

juzqafwq  于 12个月前  发布在  其他
关注(0)|答案(2)|浏览(91)

下面是Haskell代码:

zipWith compare `ap` tail

字符串
哪里

zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
compare :: a -> a -> Ordering 
ap :: Monad m => m (a -> b) -> m a -> m b


因此,根据我的理解,ap在这种情况下需要一个 Package 在列表monad中的函数,也就是说它需要m (a -> b),这应该是由zipWith compare完成的,但我不知道如何实现,因为zipWith compare将采用[a] -> [a] -> [Ordering]类型,这是不完全正确的。
代码工作,并不是我自己的工作,我只是想了解它是如何被允许的。

i34xakig

i34xakig1#

这里的诀窍是a ->是一个Monad!
事实上,我们可以在一个普通的ghci会话中列出Monad的示例:

ghci> :i Monad
type Monad :: (* -> *) -> Constraint
class Applicative m => Monad m where
  (>>=) :: m a -> (a -> m b) -> m b
  (>>) :: m a -> m b -> m b
  return :: a -> m a
  {-# MINIMAL (>>=) #-}
    -- Defined in ‘GHC.Base’
instance Monoid a => Monad ((,) a) -- Defined in ‘GHC.Base’
instance (Monoid a, Monoid b) => Monad ((,,) a b)
  -- Defined in ‘GHC.Base’
instance (Monoid a, Monoid b, Monoid c) => Monad ((,,,) a b c)
  -- Defined in ‘GHC.Base’
instance Monad ((->) r) -- Defined in ‘GHC.Base’ <- This is the one!
instance Monad IO -- Defined in ‘GHC.Base’
instance Monad Maybe -- Defined in ‘GHC.Base’
instance Monad Solo -- Defined in ‘GHC.Base’
instance Monad [] -- Defined in ‘GHC.Base’
instance Monad (Either e) -- Defined in ‘Data.Either’

字符串
因此,zipWith compare :: m ([a] -> Ordering)tail :: m [a],其中m((->) [a]),或([a] ->)
这里的技巧是,函数arrow ->实际上是一个构建新类型的类型构造函数。一旦解决了这个问题,你可以看看它的Monad示例的实际实现(那里没有什么出乎意料的)。
事实上,对于ap(我没有仔细检查过,但我很有信心),它看起来像这样:

ap :: (r -> (a -> b)) -- this is m (a -> b)
   -> (r -> a)        -- this is m a
   -> (r -> b)        -- this is m b
ap rab ra
  = \r ->             -- the result must be a function starting from r
      (rab r)         -- gives (a -> b)
      (ra r)          -- gives a, thus the body of the lambda gives b


这也是SKI中的S-combinator。

z4bn682m

z4bn682m2#

但我不知道怎么做,因为zipWith compare将采用[a] -> [a] -> [Ordering]类型,这是不完全正确的。
不,该类型有正确的形式。一开始并不明显,但该类型确实匹配m (t -> u),因此可以在这里使用ap
为了理解为什么,我们需要回忆一下A -> B(->) A B的语法糖。这是类型构造函数(->)应用于A,然后再次应用于B。我们也可以将其写为((->) A) B
那么,让我们匹配类型:

[a] -> [a] -> [Ordering]
= (->) [a] ([a] -> [Ordering])
= ((->) [a]) ([a] -> [Ordering])
  ---------m  --t    ---------u

字符串
因此,我们可以取m = (->) [a]t = [a]u = [Ordering]来满足将其传递给ap的要求。

ap :: Monad m => m (t -> u) -> m t -> m u


最后,请注意m = (->) [a]确实是一个monad,它本质上与Reader [a] monad相同,只是没有newtype Package 器。

相关问题