Экспресс Монада - PullRequest
       32

Экспресс Монада

0 голосов
/ 05 февраля 2019

Я получил задание:

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 ..)? </p>

И, может быть, кто-то может написать мне решение с помощью >> =.

Спасибоочень много!

Решение:

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 голосов
/ 06 февраля 2019

Вот подсказка.

Во-первых, вы должны понять, почему результатом оценки является Maybe Int вместо простого Int.Интуитивно понятно, что Maybe Int содержит все возможные значения для Int, а также специальное значение (Nothing).Какое дополнительное значение для?

Вероятно, это дополнительное значение должно использоваться для захвата деления на ноль ошибок.То есть, если нам нужно разделить на ноль в нашем выражении, мы должны вернуть Nothing вместо того, чтобы вызывать ошибку времени выполнения.

Ваша evalM1 функция игнорирует этот факт и просто пытается разделить числа, возвращаяпростой Int.Этот тип возврата Int является наполовину ложным, поскольку деление на ноль на самом деле не обрабатывается.При делении на ноль возникает ошибка времени выполнения вместо более хорошего специального значения Nothing.

Обратите внимание, что мы не можем просто определить evalM :: Exp -> Maybe Int wrapping 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, поскольку мы не можем + two Maybe Int, поскольку они не являютсяномера.Это, однако, не является реальной проблемой.Вместо этого мы можем выполнить два вызова внутри блока do, извлекая их результаты (если они есть, напомним, они могли бы вернуть Nothing), а затем, наконец, + и return сумму.

Делениеаналогично, но не забудьте проверить 0.

В качестве последней заметки позвольте мне ответить на ваш «вопрос как задано».Почему x <- Just number работает только с Just.Это потому, что мы работаем внутри монады Maybe, поэтому каждое выражение, следующее за <-, должно иметь тип вида Maybe something.Поэтому мы не можем написать x <- (42 :: Int).Без Just мы могли бы написать вместо let x = (42 :: Int) внутри блока do, но вам это не понадобится.

...