Haskell 返回不同类型的函数中单子的Do表示法

Haskell 返回不同类型的函数中单子的Do表示法,haskell,monads,maybe,Haskell,Monads,Maybe,有没有一种方法可以在返回类型不是所述monad的函数中为monad编写do符号 我有一个main函数来完成代码的大部分逻辑,并用另一个函数来补充,在中间进行一些计算。补充函数可能会失败,这就是它返回Maybe值的原因。我希望对main函数中的返回值使用do表示法。举一个一般性的例子: -- does some computation to two Ints which might fail compute :: Int -> Int -> Maybe Int -- actual l

有没有一种方法可以在返回类型不是所述monad的函数中为monad编写
do
符号

我有一个main函数来完成代码的大部分逻辑,并用另一个函数来补充,在中间进行一些计算。补充函数可能会失败,这就是它返回

Maybe
值的原因。我希望对main函数中的返回值使用
do
表示法。举一个一般性的例子:

-- does some computation to two Ints which might fail
compute :: Int -> Int -> Maybe Int

-- actual logic 
main :: Int -> Int -> Int
main x y = do
  first <- compute x y
  second <- compute (x+2) (y+2)
  third <- compute (x+4) (y+4)
  -- does some Int calculation to first, second and third
--对可能失败的两个整数进行一些计算
计算::Int->Int->Maybe Int
--实际逻辑
main::Int->Int->Int
main x y=do

首先,签名本质上是错误的。结果应该是
可能是Int

main :: Int -> Int -> Maybe Int
main x y = do
  first <- compute x y
  second <- compute (x+2) (y+2)
  third <- compute (x+4) (y+4)
  return (first + second + third)
这意味着它确实会从
数据构造函数中“解包”值,但是如果
没有任何内容
,那么这意味着整个
do
块的结果将是
没有任何内容

这或多或少是
Monad提供的便利:您可以将计算作为一系列成功的操作进行,如果其中一个操作失败,结果将是
无任何结果
,否则将是
仅结果

因此,您不能在最后返回一个
Int
而不是
Maybe Int
,因为从类型的角度来看,一个或多个计算完全可能返回一个
Nothing

但是,如果您添加一个“默认”值,该值将在其中一个计算为
Nothing
的情况下使用,则可以“post”处理
do
块的结果,例如:

import Data.Maybe(fromMaybe)

main :: Int -> Int -> Int
main x y = fromMaybe 0 $ do
  first <- compute x y
  second <- compute (x+2) (y+2)
  third <- compute (x+4) (y+4)
  return (first + second + third)

请注意,
asum
仍然只能包含
s,因此您仍然需要进行一些后处理。

Willem的答案基本上是完美的,但为了真正阐明这一点,让我们考虑一下如果您可以编写允许返回int的内容,将会发生什么

因此,您有类型为
Int->Int->Int
main
函数,让我们假设您的
compute
函数的实现如下:

compute :: Int -> Int -> Maybe Int
compute a 0 = Nothing
compute a b = Just (a `div` b)
现在,这基本上是整数除法函数的安全版本
div::Int->Int->Int
,如果除数为
0
,则返回
Nothing

如果可以编写返回
Int
的主函数,则可以编写以下内容:

unsafe :: Int
unsafe = main 10 (-2)

这将使
成为第二个我对你的问题有两种解释,因此要回答这两种解释:

可能是Int
的值相加,得到
Int
要在抛出
Nothing
值时求和
可能是Int
s,可以使用
sum
从列表中抛出
Nothing
值:

sum . catMaybes $ [compute x y, compute (x+2) (y+2), compute (x+4) (y+4)]
获取第一个
可能是Int
值,该值仅为n
,作为
Int
要获取第一个非
Nothing
值,您可以使用
catMaybes
与组合来获取
Just
第一个值(如果有)或
Nothing
第一个值(如果没有),并将
Nothing
转换为默认值:

fromMaybe 0 . listToMaybe . catMaybes $ [compute x y, compute (x+2) (y+2), compute (x+4) (y+4)]
如果保证至少有一个成功,请改用
head

head . catMaybes $ [compute x y, compute (x+2) (y+2), compute (x+4) (y+4)]

关键是,这些计算可能也会产生一个
,如果不是,您可以使用
返回值
。不确定您指的是什么,
compute
main
?无论哪种方式,
compute
已经返回了一个
Maybe
值,而
main
旨在明确返回一个
Int
,因此没有
Maybe
。换句话说,
main
中的一个计算可能失败,但至少应该有一个通过,这保证了
main
具有
Int
返回值。但是
Monad Maybe
的思想是将
Maybe
视为“潜在失败”计算。因此,在这里,您创建了一个潜在失败计算的“链”,如果成功,则返回
Just result
,否则返回
Nothing
。如果你确定
compute
总是返回一个
Just
,那么为什么它的签名是
Int->Int->Maybe Int
?谢谢,你的评论将它称为一系列成功的操作,这让我更清楚,我意识到我实际上走错了方向。我的意图不是让它成为一系列的动作,而是一系列的计算(任何一个都可能失败,但有一个肯定会通过),并返回通过的系列中的第一个。本质上,
main
所做的是抽象出可能的失败,并选择第一个成功的,这样当调用
main
时,一个明确的
Int
值可以在其他地方使用。有什么方法可以做到这一点吗?@lookarpstars:你可以使用
asum
,比如
asum[computexy,compute(x+2)(y+2)]
,它将返回列表中的第一个
,但这仍然是一个
可能
,因为所有计算都可能失败。请参阅:Sweet,这个评论和你在编辑后添加的部分给了我一些工作的依据。感谢您简洁的回答。@lookarpstars对于您的第一条评论,不,主要不是“选择第一条成功的”。只有当所有三个计算都成功时,它才会返回一个Just(三个结果之和)。否则它什么也不返回。因此,它尝试运行所有这些操作(一个接一个,但都是),然后合并所有结果。
fromMaybe 0 . listToMaybe . catMaybes $ [compute x y, compute (x+2) (y+2), compute (x+4) (y+4)]
head . catMaybes $ [compute x y, compute (x+2) (y+2), compute (x+4) (y+4)]