Вот подсказка.
Во-первых, вы должны понять, почему результатом оценки является 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
, но вам это не понадобится.