Haskell 单子与liftM

Haskell 单子与liftM,haskell,functional-programming,monads,Haskell,Functional Programming,Monads,我在使用liftM时遇到问题。对于(+)它工作正常,函数 maddab=liftM2(+)ab给出了预期的结果Just 5`madd`Just 7=Just 12 但是现在用(/)尝试它会给我奇怪的结果 mdiv a b=liftM2(/)a b现在操作Just 12`mdiv`Just 0给了我刚好无穷大 虽然我不期望有任何结果,但如果您启动GHCi并尝试“裸”除法操作,则会得到无穷大: Prelude> 12 / 0 Infinity liftM2只允许您在一元上下文中执行操作。在只

我在使用
liftM
时遇到问题。对于
(+)
它工作正常,函数
maddab=liftM2(+)ab
给出了预期的结果
Just 5`madd`Just 7=Just 12
但是现在用
(/)
尝试它会给我奇怪的结果

mdiv a b=liftM2(/)a b
现在操作
Just 12`mdiv`Just 0
给了我
刚好无穷大

虽然我不期望有任何结果,但如果您启动GHCi并尝试“裸”除法操作,则会得到无穷大:

Prelude> 12 / 0
Infinity
liftM2
只允许您在一元上下文中执行操作。在
只有12个
只有0个
的情况下,该上下文是
可能
。它不会改变操作;它只处理容器引入的可变性

Prelude Control.Monad> liftM2 (/) (Just 12) (Just 0)
Just Infinity
Prelude Control.Monad> liftM2 (/) (Just 12) Nothing
Nothing
Prelude Control.Monad> liftM2 (/) Nothing (Just 0)
Nothing
Prelude Control.Monad> liftM2 (/) (Just 12) (Just 3)
Just 4.0
Prelude Control.Monad> liftM2 (/) Nothing Nothing
Nothing
请注意
liftM2
如何处理一个或两个参数均为
Nothing
的情况。如果没有两个值,则无法调用具有两个参数(如
/
+
)的函数
liftM2
通过返回
Nothing
来处理值少于两个的情况

另一方面,如果正好有两个值,则调用函数。当您使用
仅12
仅0
调用它时,确实有两个值,并且调用
/
操作,产生
无限


这是意料之中的。

如果启动GHCi并尝试“裸”除法操作,则会得到无穷大:

Prelude> 12 / 0
Infinity
liftM2
只允许您在一元上下文中执行操作。在
只有12个
只有0个
的情况下,该上下文是
可能
。它不会改变操作;它只处理容器引入的可变性

Prelude Control.Monad> liftM2 (/) (Just 12) (Just 0)
Just Infinity
Prelude Control.Monad> liftM2 (/) (Just 12) Nothing
Nothing
Prelude Control.Monad> liftM2 (/) Nothing (Just 0)
Nothing
Prelude Control.Monad> liftM2 (/) (Just 12) (Just 3)
Just 4.0
Prelude Control.Monad> liftM2 (/) Nothing Nothing
Nothing
请注意
liftM2
如何处理一个或两个参数均为
Nothing
的情况。如果没有两个值,则无法调用具有两个参数(如
/
+
)的函数
liftM2
通过返回
Nothing
来处理值少于两个的情况

另一方面,如果正好有两个值,则调用函数。当您使用
仅12
仅0
调用它时,确实有两个值,并且调用
/
操作,产生
无限


这是意料之中的。

单子不是魔法,它们只是封装了某些计算模式

liftM2 :: Monad m => (a -> b -> c) -> m a -> m b -> m c
以及monad的具体版本

liftM2 :: (a -> b -> c) -> Maybe a -> Maybe b -> Maybe c
这里没有关于数字和被零除的错误。这里我们只知道一元值是
只是一些
还是
没有

这允许我们定义一个安全的除法函数,但它不能单独为我们所有人定义


Haskell不是一个智能代码编写AI代理。它只是另一种编程语言,程序员而不是计算机编写程序。此外,它为什么要为您决定是否希望运行时发生被零除的错误

单子不是魔法,它们只是封装了某些计算模式

liftM2 :: Monad m => (a -> b -> c) -> m a -> m b -> m c
以及monad的具体版本

liftM2 :: (a -> b -> c) -> Maybe a -> Maybe b -> Maybe c
这里没有关于数字和被零除的错误。这里我们只知道一元值是
只是一些
还是
没有

这允许我们定义一个安全的除法函数,但它不能单独为我们所有人定义


Haskell不是一个智能代码编写AI代理。它只是另一种编程语言,程序员而不是计算机编写程序。此外,它为什么要为您决定是否希望运行时发生被零除的错误

其他答案已经指出了为什么它对
Maybe
monad起作用。当然,
可能不是唯一的单子。您希望它也适用于
IO
、解析器和所有其他monad:

foo :: IO Double
foo = liftM2 (/) (return 12) (return 0)
foo
应该返回什么?它不能是
Nothing
,因为它可能不在
单子中

您可以使用
fail
,它在
中可能会计算为
Nothing
,通常计算为某种异常值,或者根据monad调用
error


当然,在
12/0
的特殊情况下,正确的答案确实是
无限
。定义IEEE754标准的数字专家这样做是有原因的。

其他答案已经指出了为什么它对
可能
monad的工作方式。当然,
可能不是唯一的单子。您希望它也适用于
IO
、解析器和所有其他monad:

foo :: IO Double
foo = liftM2 (/) (return 12) (return 0)
foo
应该返回什么?它不能是
Nothing
,因为它可能不在
单子中

您可以使用
fail
,它在
中可能会计算为
Nothing
,通常计算为某种异常值,或者根据monad调用
error


当然,在
12/0
的特殊情况下,正确的答案确实是
无限
。定义IEEE 754标准的数值专家之所以这样做是有原因的。

为什么你期望
?因为没有定义除以0,最好的awnser应该是
,一个单子只决定一个单子计算如何与一个返回相同类型的单子计算的函数组合。使用
Maybe
时,一元上下文是可能失败的计算。您仍然需要定义失败对您的计算意味着什么。因此,在本例中,我应该添加类似于
mdivab=if(b==0)的内容,而不是liftM2(/)a