表达单子

时间:2019-02-05 20:11:05

标签: haskell expression monads maybe

我得到了任务:

data Exp = Con Int | Sum Exp Exp | Division Exp Exp

编写函数evalM,该函数评估Expression Exp并将其放在Maybe中。 不允许使用Just构造函数,而必须使用DO或>> =来解决。

所以我的代码是:

evalM1 :: Exp -> Int
evalM1 e = evalM2 e where
           evalM2 :: Exp -> Int
           evalM2 (Con i) = (i)
           evalM2 (Sum e1 e2) = (evalM2 e1)+(evalM2 e2)
           evalM2 (Division e1 e2) = div (evalM2 e1) (evalM2 e2)

evalM :: Exp -> Maybe Int
evalM e = do
          return (evalM1 e)

-- Second try
evalM e = do
          y <- Just (evalM1 e)
          return y

所以我的问题是,为什么第一个evalM在没有Just的情况下起作用,而第二个evalM仅在我使用Just(y <-Just ..)的情况下起作用?

也许有人可以用>> =给我写解决方案。

非常感谢您!

解决方案:

evalM :: Exp -> Maybe Int
evalM (Con i) = return i 
evalM (Neg x) = evalM x >>= \v -> return (-v)
evalM (Sum e1 e2) = evalM e1 >>= \a -> evalM e2 >>= \b -> (return (a+b))
evalM (Division e1 e2) = evalM e1 >>= \a -> evalM e2 >>= (\b -> guard(b/=0) >> (return (div a b)))

> *Test> evalM (Division (Con 1) (Division (Con 0) (Con 1))) ~> Nothing
> *Test> evalM (Division (Con 1) (Sum (Con 2) (Neg (Con 2)))) ~> Nothing

1 个答案:

答案 0 :(得分:1)

这是一个提示。

首先,您必须了解为什么评估结果是Maybe Int而不是简单的Int。直观地讲,Maybe Int包含Int的所有可能值,以及一个特殊的值(Nothing)。额外的价值是什么?

同样,该附加值将用于捕获零误差除法。也就是说,如果需要在表达式中除以零,则应返回Nothing而不是导致运行时错误。

您的evalM1函数将忽略此事实,仅尝试对数字进行除法,返回简单的Int。不过,此Int返回类型是一个半谎言,因为实际上并没有处理被零除的问题。除以零会导致运行时错误,而不是更好的特殊值Nothing

请注意,我们不能简单地定义evalM :: Exp -> Maybe Int包装evalM1。像

这样的代码
evalM e = return (evalM1 e)

会输入,但在特殊情况下不会处理零除。

我建议您忽略evalM1,而从头开始构建evalM。您可以遵循类似的递归模式:

evalM :: Exp -> Maybe Int
evalM (Con i) = ...
evalM (Sum e1 e2) = ...
evalM (Division e1 e2) = ...

对于总和情况:请注意,我们不能编写递归调用evalM e1 + evalM e2,因为我们不能+两个Maybe Int,因为它们不是数字。但是,这不是一个真正的问题。相反,我们可以在do块中执行两次调用,以获取它们的结果(如果有,请回想它们可以返回Nothing),然后最后是+return

除法类似,但请记住检查0

最后,请允许我解决您的“所问问题”。为什么x <- Just number仅适用于Just。好吧,这是因为我们在这里工作于Maybe monad中,因此<-之后的每个表达式都必须具有Maybe something形式的类型。所以我们不能写x <- (42 :: Int)。如果没有Just,我们可以在let x = (42 :: Int)块中编写do,但是您不需要。