ap fromMaybe 是如何组成的?

How does ap fromMaybe compose?

我当时写了一个函数,它接受一个值作为输入,在该输入上调用一个函数,如果结果是 Just x,它应该 return x;否则,它应该 return 原始输入。

换句话说,这个函数(我不知道该叫什么):

foo :: (a -> Maybe a) -> a -> a
foo f x = fromMaybe x (f x)

因为它看起来像一个通用函数,我想知道它是否已经定义,所以I asked on Twitter, and Chris Allen replied它是ap fromMaybe

这听起来很有希望,所以我启动了 GHCI 并开始试验:

Prelude Control.Monad Data.Maybe> :type ap
ap :: Monad m => m (a -> b) -> m a -> m b
Prelude Control.Monad Data.Maybe> :type fromMaybe
fromMaybe :: a -> Maybe a -> a
Prelude Control.Monad Data.Maybe> :type ap fromMaybe
ap fromMaybe :: (b -> Maybe b) -> b -> b

ap fromMaybe 的类型当然看起来是正确的,并且一些实验似乎表明它也具有所需的行为。

但是它是如何工作的?

fromMaybe 函数对我来说似乎很清楚,并且孤立地,我认为我理解 ap 的作用 - 至少在 Maybe 的上下文中。当 mMaybe 时,它的类型为 Maybe (a -> b) -> Maybe a -> Maybe b.

我不明白 ap fromMaybe 是如何编译的。对我来说,这个表达式看起来像是部分应用,但我可能理解错了。但是,如果是这种情况,我不明白这些类型是如何匹配的。

ap 的第一个参数是 m (a -> b),但是 fromMaybe 的类型是 a -> Maybe a -> a。那怎么搭配呢?编译器推断 m 是哪个 Monad 实例? fromMaybe 接受两个(柯里化)参数,如何变成接受单个参数的函数?

有人可以帮我把这些点联系起来吗?

但是 apMaybe 的上下文中 而不是 。我们将它与函数 fromMaybe 一起使用,因此它在函数的上下文中,其中

ap f g x = f x (g x)

在各种 Monad 个实例中,我们有

instance Monad ((->) r)

原来如此

ap :: Monad m =>    m  (a       -> b)  ->    m  a  ->    m  b
fromMaybe     ::  r -> (Maybe r -> r)
ap            :: (r -> (a       -> b)) -> (r -> a) -> (r -> b)   
ap                   f                        g        x :: b
ap  fromMaybe ::                          (r -> a) -> (r -> b)  , a ~ Maybe r , b ~ r

因为 -> 在类型中关联到右边:a -> b -> c ~ a -> (b -> c)。尝试将这些类型组合在一起,我们只能得到上面的定义。

还有(<*>) :: Applicative f => f (a -> b) -> f a -> f b,我们可以写成(fromMaybe <*>),如果你喜欢这样的涂鸦:

 #> :t (fromMaybe <*>)
(fromMaybe <*>) :: (r -> Maybe r) -> r -> r

正如在此处的另一个答案中正确指出的那样,当与函数一起使用时,<*> 就是你的好 ole' S combinator。我们不能很好地在 Haskell 中使用名为 S 的函数,因此 <*> 只是 point-free 编码风格的标准指令集的一部分。 Monadic bind(更是如此,翻转),=<<,可能更加神秘,但 pointfree 编码器并不关心,并且会愉快地使用它来编码另一个类似的模式,

(f =<< g) x = f (g x) x 

combinatory 函数调用中,神秘与否(想到 zipWith (-) =<< drop 1)。

为清楚起见,我将重新标记类型参数。

ap :: Monad m => m (a -> b) -> m a -> m b
fromMaybe :: c -> Maybe c -> c

Which Monad instance does the compiler infer that m is?

((->) r) 是一个 Monad。这是所有以类型 r 作为参数的函数,对于某些 specific r.

所以在类型中:

ap :: Monad m => m (a -> b) -> m a -> m b

m ~ (c ->)a ~ Maybe cb ~ c

return 类型 m a -> m b 扩展为 (c -> Maybe c) -> c -> c - 这是 ap fromMaybe 的类型。

对于简洁和机械的回答表示歉意。我不喜欢 cherry-picking Applicative 或 Monad 之类的东西,但我不知道你在哪里。这是 not my usual approach to teaching Haskell.

首先,ap 实际上是 (<*>)

Prelude> import Control.Monad
Prelude> import Data.Maybe
Prelude> import Control.Applicative
Prelude> :t ap
ap :: Monad m => m (a -> b) -> m a -> m b
Prelude> :t (<*>)
(<*>) :: Applicative f => f (a -> b) -> f a -> f b

这是什么意思?这意味着我们不需要像 Monad 这样的 "strong" 来描述我们正在做的事情。适用性就足够了。但是 Functor 没有。

Prelude> :info Applicative
class Functor f => Applicative (f :: * -> *) where
  pure :: a -> f a
  (<*>) :: f (a -> b) -> f a -> f b
Prelude> :info Functor
class Functor (f :: * -> *) where
  fmap :: (a -> b) -> f a -> f b

这里是 ap/(<*>) 可能 Monad/Applicative:

Prelude> ap (Just (+1)) (Just 1)
Just 2
Prelude> (<*>) (Just (+1)) (Just 1)
Just 2

首先要弄清楚的是,我们在谈论 Applicative 类型类的哪个实例?

Prelude> :t fromMaybe
fromMaybe :: a -> Maybe a -> a

从 Maybe 的类型中稍微脱糖可以得到:

(->) a (Maybe a -> a)

所以我们在这里关注的类型构造函数是(->)。关于 (->) 也称为函数类型,GHCi 告诉我们什么?

Prelude> :info (->)
data (->) a b   -- Defined in ‘GHC.Prim’
instance Monad ((->) r) -- Defined in ‘GHC.Base’
instance Functor ((->) r) -- Defined in ‘GHC.Base’
instance Applicative ((->) a) -- Defined in ‘GHC.Base’

嗯。 Maybe 呢?

Prelude> :info Maybe
data Maybe a = Nothing | Just a     -- Defined in ‘GHC.Base’
instance Monad Maybe -- Defined in ‘GHC.Base’
instance Functor Maybe -- Defined in ‘GHC.Base’
instance Applicative Maybe -- Defined in ‘GHC.Base’

对 Maybe 使用 (<*>) 发生的事情是这样的:

Prelude> (+1) 1
2
Prelude> (+1) `fmap` Just 1
Just 2
Prelude> Just (+1) <*> Just 1
Just 2
Prelude> :t fmap
fmap :: Functor f => (a -> b) -> f a -> f b
Prelude> let mFmap = fmap :: (a -> b) -> Maybe a -> Maybe b
Prelude> (+1) `mFmap` Just 1
Just 2
Prelude> :t (<*>)
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
Prelude> let mAp = (<*>) :: Maybe (a -> b) -> Maybe a -> Maybe b
Prelude> :t (+1)
(+1) :: Num a => a -> a
Prelude> :t Just (+1)
Just (+1) :: Num a => Maybe (a -> a)
Prelude> Just (+1) `mAp` Just 1
Just 2

好的,函数类型的 Functor 和 Applicative 呢?这里棘手的部分之一是 (->) 必须在类型 中部分应用 才能成为 Functor/Applicative/Monad。因此,您的 f 成为整体 (->) a b(->) a,其中 a 是参数类型,b 是结果。

Prelude> (fmap (+1) (+2)) 0
3
Prelude> (fmap (+1) (+2)) 0
3
Prelude> :t fmap
fmap :: Functor f => (a -> b) -> f a -> f b
Prelude> let funcMap = fmap :: (a -> b) -> (c -> a) -> c -> b
Prelude> -- f ~ (->) c 
Prelude> (funcMap (+1) (+2)) 0
3

Prelude> :t (<*>)
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
Prelude> let funcAp = (<*>) :: (c -> a -> b) -> (c -> a) -> (c -> b)
Prelude> :t fromMaybe
fromMaybe :: a -> Maybe a -> a
Prelude> :t funcAp fromMaybe
funcAp fromMaybe :: (b -> Maybe b) -> b -> b
Prelude> :t const
const :: a -> b -> a
Prelude> :t funcAp const
funcAp const :: (b -> b1) -> b -> b

不保证有用。您可以仅从类型和了解参数化的工作原理来判断 funcAp const 并不有趣。

编辑:说到撰写,(->) a 的 Functor 就是 (.)。适用的是,但有一个额外的论点。 Monad 是 Applicative,但参数翻转了。

更进一步:适用于(->) a) 的<*> 是S,pure 是SKI 组合子演算的K。 (你可以从K和S推导出I。其实你可以从K和S推导出任何程序。)

Prelude> :t pure
pure :: Applicative f => a -> f a
Prelude> :t const
const :: a -> b -> a
Prelude> :t const
const :: a -> b -> a
Prelude> let k = pure :: a -> b -> a
Prelude> k 1 2
1
Prelude> const 1 2
1

如果您更喜欢中缀语法,您正在寻找的 monad 是 (->) rr -> _

然后 ap 的签名扩展为:

 m (a -> b) -> m a -> m b =
 (r -> (a -> b)) -> (r -> a) -> r -> b = -- now we use the signature of fromMaybe 
 (b -> (Maybe b -> b)) -> (b -> Maybe b) -> b -> b

现在,如果您将 ap fromMaybe 视为部分应用函数,瞧,您将获得所需的结果。