我编写了以下函数(使用 这些 库):
import Control.Applicative (Alternative ((<|>)), optional)
import Data.These (These(..))
asumThese :: (Alternative f, Monad f) => f a -> f b -> f (These a b)
asumThese x y = optional x >>= \case
Just x' -> maybe (This x') (These x') <$> optional y
Nothing -> That <$> y
有没有办法可以删除
Monad
约束,或者没有办法摆脱使用绑定?
我的直觉告诉我我无法避免这里的
Monad
约束,但是任何人都可以告诉我为什么会这样吗?这样,将来我可以更好地了解我可能需要什么样的功能 Monad
以及哪些功能可以推广到 Applicative
和/或 Alternative
?
我感觉这与需要
Monad
进行排序有关,但我不能完全确定它,因为 f <$> x <*> y
以与 f <$> y <*> x
不同的顺序解析事物,所以 Applicative
中仍然有排序
.
当一个步骤的
action取决于上一步的
return value时,您需要
Monad m
而不是 Applicative m
。控制对数据的依赖性被认为是捕捉“排序”的力量。如果你写 (,) <$> x <*> y
,那么 x
在 y
之前执行/解析/无论什么都只是一个约定。对于右边的Applicative
,y
可以在之前x
发挥作用,或者y
和x
可以并行。在 x >>= maybe (return Nothing) (Just <$> y)
中,在 y
完成之前,不知道 x
是否会执行。对于>>=
,排序是强制的,而不仅仅是常规的。
在这种情况下,控制确实依赖于数据:如果
optional x
返回 Nothing
,则下一个操作是运行 y
,但如果它返回 Just x'
,则下一个操作是 optional y
。也就是说,您只引入一个分支,以便在 y
成功时允许 x
失败。因此,我认为没有一种(好的)方法可以纯粹用 Alternative
来编写此操作。
当然还有最简单的
asumThese x y = (These <$> x <*> y) <|> This <$> x <|> That <$> y
但这 i) 以不同的顺序产生结果(即,它以不同的方式引入
Alternative
效果,因为效果不再依赖于数据)并且 ii) 效率低下。