haskell Option/Maybe Monad相对于函子的优势是什么?

0lvr5msh  于 2022-11-14  发布在  其他
关注(0)|答案(1)|浏览(167)

我理解IO单子和List单子相对于Functor的优势,但是,我不理解Option/Maybe单子相对于Functor的优势。
这是简单的语言类型的整合吗?

Option/Maybe Monad在具体应用中比Functor有什么优势?
PS.问具体使用中的优点不是基于观点,因为如果有,它可以被指出,而不带主观方面。
附言,附言,这里有些会员很急,反复催促
Option is both a functor and a monad?
应该是答案,或者QA重复,但实际上不是。
我已经知道了一些基本知识,例如
每个单子都是应用函子,每个应用函子都是函子
这是公认的答案,这不是我想问的
这里有一个很好的答案,这是不包括在以前的问答。
每个问题的方面、细节或解决方案都有很大的不同,因此请避免在这里以粗略的方式“捆绑”不同的东西。

ogq8wdun

ogq8wdun1#

让我们来看看类型。

fmap       :: Functor      f =>   (a ->   b) -> (f a -> f b)
(<*>)      :: Applicative  f => f (a ->   b) -> (f a -> f b)
flip (>>=) :: Monad        f =>   (a -> f b) -> (f a -> f b)

对于一个函子,我们可以将一个普通的函数a -> b应用于f a类型的值,得到一个f b类型的值。这个函数永远不会对f部分发生的事情有发言权,只有内部。把函子看作一种盒子一样的东西,fmap中的函数永远不会看到盒子本身,只有内部。值被取出并放回它开始时所在的盒子中。
应用函子的功能稍微强大一些。现在,我们有一个 * also * in a box的函数。因此,在有限的意义上,函数在f部分有发言权。函数和输入都有f部分(它们彼此独立),这些部分组合成结果。
单子的功能更强大,函数没有f部分,函数取一个值并 * 产生 * 一个新的盒子,而在Applicative的情况下,函数的盒子和值的盒子是独立的,在Monad的情况下,函数的盒子可以 * 依赖于 * 输入值。
这是什么意思?你让我关注Maybe,那么我们来具体讨论一下Maybe

fmap       ::       (a ->       b) -> (Maybe a -> Maybe b)
(<*>)      :: Maybe (a ->       b) -> (Maybe a -> Maybe b)
flip (>>=) ::       (a -> Maybe b) -> (Maybe a -> Maybe b)

提醒一下,Maybe看起来像这样。

data Maybe a = Nothing | Just a

Maybe a是一个可能存在也可能不存在的值,从函子的Angular 来看,我们通常会认为Nothing是某种形式的失败,而Just aa类型的成功结果。
fmap开始,MaybeFunctor示例允许我们在Maybe内部应用一个函数(如果存在的话),该函数对操作的成功或失败没有发言权:一个失败的Maybe(即一个Nothing)必须保持失败,一个成功的Maybe必须保持成功(显然,我们在这里忽略了undefined和其他指称语义问题;我假设函数失败的 * 唯一 * 方式是使用Nothing)。
现在,(<*>),应用运算符,接受Maybe (a -> b)Maybe a。这两个运算符中的任何一个都可能失败。如果它们中的任何一个失败了,那么结果是Nothing,并且 * 只有 * 这两个都成功了,我们才能得到Just作为我们的结果。这允许我们 * 咖喱 * 运算。具体地说,如果我有一个形式为g :: a -> b -> c的函数,并且我有值ma :: Maybe amb :: Maybe b,那么我们可能要把g应用到mamb上,但是当我们开始这样做的时候,我们有一个问题。

fmap g ma :: Maybe (b -> c)

现在我们有了一个可能存在也可能不存在的函数,我们不能用fmap代替mb,因为一个不存在的函数(a Nothing)不能作为fmap的参数。问题是我们有两个 * 独立 * 的Maybe值(在我们的例子中是mamb),在某种意义上,它们在争夺控制权。只有当两个都是Just时,结果才存在。否则,结果应该是Nothing。这是一种布尔“与”运算,因为如果任何中间体失败,那么整个计算失败。(注:如果您要查找布尔值“or”,其中任何单个成功都可以从先前的失败中恢复,则您要查找Alternative
所以我们写

(fmap g ma) <*> mb :: Maybe c

或者,使用Haskell为此目的提供的更方便的同义词,

g <$> ma <*> mb :: Maybe c

现在,上述情况的关键词是 independentmamb对对方的成功或失败没有发言权。这在许多情况下是好的,因为这样的代码通常可以并行化(有非常高效的命令行参数解析库,它们正好利用了Applicative的这个属性)。但是,很明显,这并不总是我们所 * 想要的 *。
输入Monad。在Maybe单子中,提供的函数 * 根据输入a生成 * 一个Maybe b类型的值。aMaybe部分和bMaybe部分不再是独立的:后者可以直接依赖于前者。
例如,以Maybe为例:平方根函数。我们不能取负数的平方根(假设这里不处理复数),所以我们假设的平方根看起来像

sqrt :: Double -> Maybe Double
sqrt x | x < 0 = Nothing
       | otherwise = Just (Prelude.sqrt x)

现在,假设我们有一个数r,但是r不是一个简单的数,它来自我们之前的计算,我们的计算可能失败了,也许它之前做了一个平方根,或者试图除以零,或者其他的什么,但是它做了一些有可能产生Nothing的事情,所以rMaybe Double,我们要求它的平方根。
显然,如果r已经是Nothing,那么它的平方根就是Nothing;如果其他的都算不出来,我们就不可能求平方根了,另一方面,如果r是负数,那么sqrt就会失败,得到Nothing,尽管r本身就是Just,所以我们真正想要的是

case r of
  Nothing -> Nothing
  Just r' -> sqrt r'

而这正是MaybeMonad示例所做的。

r >>= sqrt

整个计算的结果(并且,即它是Nothing还是Just)不仅取决于r是否是Nothing,而且取决于r的实际值。r的两个不同的Just值可以产生成功 * 或 * 失败,这取决于sqrt的操作。单靠Functor无法做到这一点,甚至用Applicative也无法做到这一点。它需要一个Monad

相关问题