在返回不同类型的函数中对 monad 进行标记
Do notation for monad in function returning a different type
有没有办法在 return 类型不是所述 monad 的函数中为 monad 编写 do
符号?
我有一个主要功能来处理代码的大部分逻辑,并辅以另一个在中间为它进行一些计算的功能。补充功能可能会失败,这就是为什么它 returning 一个 Maybe
值。我希望在 main 函数中对 returned 值使用 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
我打算让 first
、second
和 third
具有实际的 Int
值,从 Maybe
上下文中取出,但是按照上面的方式做 Haskell 抱怨无法将 Maybe Int
的类型与 Int
.
匹配
有办法吗?还是我走错方向了?
请原谅我是否错误地使用了某些术语,我是 Haskell 的新手,并且仍在努力了解所有内容。
编辑
main
必须 return 一个 Int
,而不用包裹在 Maybe
中,因为代码的另一部分使用 [=21= 的结果]作为Int
。单个 compute
的结果可能会失败,但它们应该在 main
中共同通过(即至少一个会通过),而我正在寻找的是一种使用 do
的方法符号将它们从 Maybe
中取出,对它们进行一些简单的 Int
计算(例如,可能将任何 Nothing
returned 视为 0
),以及 return 最终值只是 Int
.
好吧,签名本质上是错误的。结果应该是 Maybe Int
:
main :: Int -> Int -> <b>Maybe Int</b>
main x y = do
first <- compute x y
second <- compute (x+2) (y+2)
third <- compute (x+4) (y+4)
<b>return</b> (first + second + third)
例如这里我们 return (first + second + third)
,return
将把它们包装在 Just
数据构造函数中。
这是因为您的 do
块隐式使用了 Monad Maybe
的 >>=
,定义为:
instance Monad Maybe where
Nothing >>=_ = Nothing
(Just x) >>= f = f x
return = Just
所以这意味着它确实会从 Just
数据构造函数中 "unpack" 值,但是如果 Nothing
从它出来,那么这意味着结果整个 do
块将是 Nothing
.
这或多或少是 Monad Maybe
提供的便利:您可以将计算作为一系列 成功的 操作,如果其中一个失败,结果将为 Nothing
,否则将为 Just result
.
因此你不能在最后 return 一个 Int
而不是 Maybe Int
,因为从类型的角度来看,一个或多个计算绝对是可能的可以 return一个Nothing
.
然而,您可以 "post" 处理 do
块的结果,例如,如果您添加一个 "default" 值,该值将在其中一个计算是 Nothing
,比如:
import Data.Maybe(fromMaybe)
main :: Int -> Int -> <b>Int</b>
main x y = <b>fromMaybe 0</b> $ do
first <- compute x y
second <- compute (x+2) (y+2)
third <- compute (x+4) (y+4)
return (first + second + third)
如果 do
-block 因此 return 是 Nothing
,我们将其替换为 0
(您当然可以在 fromMaybe :: a -> Maybe a -> a
作为计算值 "fails").
如果你想return Maybe
列表中的第一个元素即Just
,那么你可以使用asum :: (Foldable t, Alternative f) => t (f a) -> f a
,这样你就可以写你的 main
喜欢:
-- first <i>non</i>-failing computation
import Data.Foldable(asum)
import Data.Maybe(fromMaybe)
main :: Int -> Int -> Int
main x y = fromMaybe 0 $ <b>asum</b> [
compute x y
compute (x+2) (y+2)
compute (x+4) (y+4)
]
请注意,asum
仍然只能包含 Nothing
,因此您仍然需要进行一些 post 处理。
Willem 的回答基本上是完美的,但为了真正说明问题,让我们考虑一下如果您可以编写允许您 return 一个 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
。
returns a Nothing
如果你能随心所欲地编写一个 main 函数 returns 和 Int
,你将能够编写以下内容:
unsafe :: Int
unsafe = main 10 (-2)
这会使 second <- compute ...
失败并且 return 成为 Nothing
但现在您必须将 Nothing
解释为一个不好的数字。它破坏了使用 Maybe
monad 安全捕获失败的全部目的。当然,您可以按照 Willem 的描述为 Nothing
提供默认值,但这并不总是合适的。
更一般地说,当你在 do
块内时,你应该只在 "the box" 内思考那是 monad,不要试图逃脱。在某些情况下,例如 Maybe
,您可以使用 fromMaybe
或 maybe
等函数来执行 unMaybe
,但一般情况下不行。
我对你的问题有两种解释,所以同时回答:
对Just n
的Maybe Int
值求和得到Int
要在抛出 Nothing
值的同时对 Maybe Int
求和,您可以使用 sum
和 Data.Maybe.catMaybes :: [Maybe a] -> [a]
从列表中抛出 Nothing
值:
sum . catMaybes $ [compute x y, compute (x+2) (y+2), compute (x+4) (y+4)]
获取第一个 Maybe Int
值 Just n
作为 Int
要获取第一个非Nothing
值,可以使用catMaybes
结合listToMaybe :: [a] -> Maybe a
to get Just
the first value if there is one or Nothing
if there isn't and fromMaybe :: a -> Maybe a -> a
将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)]
有没有办法在 return 类型不是所述 monad 的函数中为 monad 编写 do
符号?
我有一个主要功能来处理代码的大部分逻辑,并辅以另一个在中间为它进行一些计算的功能。补充功能可能会失败,这就是为什么它 returning 一个 Maybe
值。我希望在 main 函数中对 returned 值使用 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
我打算让 first
、second
和 third
具有实际的 Int
值,从 Maybe
上下文中取出,但是按照上面的方式做 Haskell 抱怨无法将 Maybe Int
的类型与 Int
.
有办法吗?还是我走错方向了?
请原谅我是否错误地使用了某些术语,我是 Haskell 的新手,并且仍在努力了解所有内容。
编辑
main
必须 return 一个 Int
,而不用包裹在 Maybe
中,因为代码的另一部分使用 [=21= 的结果]作为Int
。单个 compute
的结果可能会失败,但它们应该在 main
中共同通过(即至少一个会通过),而我正在寻找的是一种使用 do
的方法符号将它们从 Maybe
中取出,对它们进行一些简单的 Int
计算(例如,可能将任何 Nothing
returned 视为 0
),以及 return 最终值只是 Int
.
好吧,签名本质上是错误的。结果应该是 Maybe Int
:
main :: Int -> Int -> <b>Maybe Int</b>
main x y = do
first <- compute x y
second <- compute (x+2) (y+2)
third <- compute (x+4) (y+4)
<b>return</b> (first + second + third)
例如这里我们 return (first + second + third)
,return
将把它们包装在 Just
数据构造函数中。
这是因为您的 do
块隐式使用了 Monad Maybe
的 >>=
,定义为:
instance Monad Maybe where
Nothing >>=_ = Nothing
(Just x) >>= f = f x
return = Just
所以这意味着它确实会从 Just
数据构造函数中 "unpack" 值,但是如果 Nothing
从它出来,那么这意味着结果整个 do
块将是 Nothing
.
这或多或少是 Monad Maybe
提供的便利:您可以将计算作为一系列 成功的 操作,如果其中一个失败,结果将为 Nothing
,否则将为 Just result
.
因此你不能在最后 return 一个 Int
而不是 Maybe Int
,因为从类型的角度来看,一个或多个计算绝对是可能的可以 return一个Nothing
.
然而,您可以 "post" 处理 do
块的结果,例如,如果您添加一个 "default" 值,该值将在其中一个计算是 Nothing
,比如:
import Data.Maybe(fromMaybe)
main :: Int -> Int -> <b>Int</b>
main x y = <b>fromMaybe 0</b> $ do
first <- compute x y
second <- compute (x+2) (y+2)
third <- compute (x+4) (y+4)
return (first + second + third)
如果 do
-block 因此 return 是 Nothing
,我们将其替换为 0
(您当然可以在 fromMaybe :: a -> Maybe a -> a
作为计算值 "fails").
如果你想return Maybe
列表中的第一个元素即Just
,那么你可以使用asum :: (Foldable t, Alternative f) => t (f a) -> f a
,这样你就可以写你的 main
喜欢:
-- first <i>non</i>-failing computation
import Data.Foldable(asum)
import Data.Maybe(fromMaybe)
main :: Int -> Int -> Int
main x y = fromMaybe 0 $ <b>asum</b> [
compute x y
compute (x+2) (y+2)
compute (x+4) (y+4)
]
请注意,asum
仍然只能包含 Nothing
,因此您仍然需要进行一些 post 处理。
Willem 的回答基本上是完美的,但为了真正说明问题,让我们考虑一下如果您可以编写允许您 return 一个 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
如果你能随心所欲地编写一个 main 函数 returns 和 Int
,你将能够编写以下内容:
unsafe :: Int
unsafe = main 10 (-2)
这会使 second <- compute ...
失败并且 return 成为 Nothing
但现在您必须将 Nothing
解释为一个不好的数字。它破坏了使用 Maybe
monad 安全捕获失败的全部目的。当然,您可以按照 Willem 的描述为 Nothing
提供默认值,但这并不总是合适的。
更一般地说,当你在 do
块内时,你应该只在 "the box" 内思考那是 monad,不要试图逃脱。在某些情况下,例如 Maybe
,您可以使用 fromMaybe
或 maybe
等函数来执行 unMaybe
,但一般情况下不行。
我对你的问题有两种解释,所以同时回答:
对Just n
的Maybe Int
值求和得到Int
要在抛出 Nothing
值的同时对 Maybe Int
求和,您可以使用 sum
和 Data.Maybe.catMaybes :: [Maybe a] -> [a]
从列表中抛出 Nothing
值:
sum . catMaybes $ [compute x y, compute (x+2) (y+2), compute (x+4) (y+4)]
获取第一个 Maybe Int
值 Just n
作为 Int
要获取第一个非Nothing
值,可以使用catMaybes
结合listToMaybe :: [a] -> Maybe a
to get Just
the first value if there is one or Nothing
if there isn't and fromMaybe :: a -> Maybe a -> a
将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)]