<*> 在 addRecip x y = fmap (+) (recipMay x) <*> recipMay y 中做了什么?

What does <*> do in addRecip x y = fmap (+) (recipMay x) <*> recipMay y?

addRecip :: Double -> Double -> Maybe Double
addRecip x y = fmap (+) (recipMay x) <*> recipMay y
  where
    recipMay a | a == 0 = Nothing
               | otherwise = Just (1 / a)

我查找 <*> 的一些解释。

<*> 接受一个函子,其中包含一个接受 a 并返回 b 的函数,以及一个包含 a 的函子,它 returns 一个函子包含 a b。所以 <*> 从仿函数中提取函数并将其应用于仿函数内部的参数,最后 returns 将结果放入仿函数

这是一个例子:

fs <*> xs = [f x | f <- fs, x <- xs]

但就我而言,似乎有点不同。 recipMay x 中的元素不是函数。

这里的函子是Maybe<*> 将 return Nothing 如果任一参数是 Nothing (即,它涉及除以零)

Nothing <*> _       = Nothing
_       <*> Nothing = Nothing

在其余情况下,它只应用包装函数:

Just f <*> Just x = Just (f x)

另请注意

fmap (+) (recipMay x) <*> recipMay y

是一个有点不寻常的符号。通常写成

(+) <$> recipMay x <*> recipMay y

完全等价,因为 fmap 写成中缀 <$>,但可以说更具可读性。

这里,fmap (+) (recipMay x)(或(+) <$> recipMay x)表示

if x == 0
then Nothing 
else Just (\a -> 1/x + a)

<*> 将应用值应用于另一个。它是常规功能应用程序的更丰富的对应物。应用值以某种方式修饰,例如,是否有任何您认为的值(对于 Maybe,这是您的情况)可以是可选的,或者可以有很多值(对于 List)。

因此,将一个应用值应用到另一个应用值具有一些特殊行为。对于列表,a <*> b 将 a 的每个成员应用于 b 的每个成员,从而形成所有组合的巨大列表,而对于 Maybe(这是你的情况)a <*> b 给出 Just (a' b') 如果 a 和b 是 (Just a') 和 (Just b'),如果 a 和 b 中的一个或两者都不是 Nothing,则给出 Nothing - 对于 Maybe,总而言之,它是可选值的函数应用程序,如果涉及的任何值不存在,则结果不存在。

关于如何实施 <*> 有一些规则,这意味着您始终可以将其视为 [将 "contained function" 应用于 "contained value"],只要您完成所有工作在包含的域中(使用 <$>、<*>、pure、>>=、<|> 等)那么你可以认为它与常规函数应用程序相同,但是当你来到 "extract"价值,你会看到增加的丰富性。

(<*>) :: Applicative f => f (a -> b) -> f a -> f b 来自 Applicative 类型类。 Applicative 是(引用文档)“A functor with application.”。您可以将 Functor 视为一个集合(尽管还有其他类型不是函子的集合,例如函数)。

如果我们将函子视为一个集合,那么 (<*>) 运算符将采用其中两个集合。第一个集合存储 a -> b 类型的函数,后者是 b 类型的集合。通过将第二个集合中的每个元素应用于第一个集合中的每个函数,结果是 bs 的集合(相同类型的集合)。

所以对于列表来说,它看起来像:

(<*>) :: [a -> b] -> [a] -> [b]
(<*>) fs xs = [fi xj | fi <- fs, xj <- xs]

A Maybe 也是某种集合:它要么包含 no 元素(Nothing 的情况),要么包含 one 元素(具有 x 元素的 Just x 情况)。因此,您可以将 Maybe 视为具有“multiplicity” 0..1.

的集合

如果两个操作数之一是 Nothing(或两者),则结果也是 Nothing,因为如果没有函数或元素,则有没有"result"的一个函数应用。只有在两个操作数都是Just的情况下(所以Just fJust x),我们才能执行函数应用(所以Just (f x)):

(<*>) :: Maybe (a -> b) -> Maybe a -> Maybe b
(<*>) (Just f) (Just x) = Just (f x)
(<*>) _ _ = Nothing

在这个具体案例中,我们可以分析一下用途:

addRecip :: Double -> Double -> Maybe Double
addRecip x y = (fmap (+) (recipMay x)) <*> recipMay y
  where
    recipMay a | a == 0 = Nothing
               | otherwise = Just (1 / a)

因此我们看到两个操作数:fmap (+) (RecipMay x)recipMay y。如果xand/ory0,那么操作数分别是Nothing。因为在那种情况下对应的 recipMayNothing.

因此我们可以这样写:

addRecip :: Double -> Double -> Maybe Double
addRecip x y | x == 0 = Nothing
             | y == 0 = Nothing
             | otherwise = Just ((1/x) + (1/y))

但是在上面我们重复了== 01/逻辑两次。