dfe*_*uer 16 haskell alternative-functor monadplus
我最近写的
do
e <- (Left <$> m) <|> (Right <$> n)
more actions
case e of
Left x -> ...
Right y -> ...
Run Code Online (Sandbox Code Playgroud)
这看起来很尴尬。我知道protolude(和其他一些包)定义
-- Called eitherP in parser combinator libraries
eitherA :: Alternative f => f a -> f b -> f (Either a b)
Run Code Online (Sandbox Code Playgroud)
但即便如此,这一切还是感觉有点手动。有没有一些我没见过的漂亮模式可以收紧它?
DDu*_*Dub 15
我刚刚注意到OP在评论中表达了同样的想法表达了同样的想法。无论如何我都会发表我的想法。
\nCoyoneda 是一个巧妙的技巧,但对于这个特定问题来说有点过分了。我认为你所需要的只是常规的旧延续。
\n让我们命名这些...:
do\n e <- (Left <$> m) <|> (Right <$> n)\n more actions\n case e of\n Left x -> fx x\n Right y -> fy y\nRun Code Online (Sandbox Code Playgroud)\n然后,我们可以将其写为:
\ndo\n e <- (fx <$> m) <|> (fy <$> n)\n more actions\n e\nRun Code Online (Sandbox Code Playgroud)\n这有点微妙 \xe2\x80\x94 在那里使用很重要,<$>即使它看起来你可能想要使用=<<,以便第一行的结果实际上是稍后执行的单子操作,而不是得到的东西立即执行。
dan*_*iaz 10
这个问题想太多了,但是……
在您的代码中,每个分支的类型Either可能不同,但它们不会逃脱 do 块,因为它们被LeftandRight延续“擦除”。
这看起来有点像存在主义类型。也许我们可以声明一个包含初始操作及其延续的类型,并为该类型提供一个Alternative实例。
实际上,我们不必声明它,因为 Hackage 中已经存在这样的类型:它Coyoneda来自kan-extensions。
data Coyoneda f a where
Coyoneda :: (b -> a) -> f b -> Coyoneda f a
Run Code Online (Sandbox Code Playgroud)
其中有有用的实例
Alternative f => Alternative (Coyoneda f)
MonadPlus f => MonadPlus (Coyoneda f)
Run Code Online (Sandbox Code Playgroud)
在我们的例子中,“返回值”本身就是一个单子动作m,所以我们想要处理类型的值Coyoneda m (m a),其中m a是整个 do 块的类型。
知道了所有这些,我们可以定义以下函数:
sandwich :: (Foldable f, MonadPlus m, Monad m)
=> m x
-> f (Coyoneda m (m a))
-> m a
sandwich more = join . lowerCoyoneda . hoistCoyoneda (<* more) . asum
Run Code Online (Sandbox Code Playgroud)
重新实现原来的例子:
sandwich more [Coyoneda m xCont, Coyoneda n yCont]
Run Code Online (Sandbox Code Playgroud)
你也许可以这样做:
do
let acts = do more actions
(do x <- m; acts; ...) <|> (do y <- n; acts; ...)
Run Code Online (Sandbox Code Playgroud)
我不知道这对你来说是否更好。
more actions(当然,如果这些绑定了许多变量,那么效果就不好)