Монады и лифтМ - PullRequest
       30

Монады и лифтМ

1 голос
/ 24 января 2020

У меня проблема с использованием liftM. Для (+) он работает нормально, функция madd a b = liftM2 (+) a b дает мне ожидаемый результат Just 5 `madd` Just 7 = Just 12, но теперь, пробуя его с (/), он дает мне странные результаты.

mdiv a b = liftM2 (/) a b теперь операция Just 12 `mdiv` Just 0 дает мне Just Infinity пока я ожидаю Nothing.

Ответы [ 3 ]

9 голосов
/ 24 января 2020

Монады не волхвы c, они просто инкапсулируют определенные вычислительные паттерны.

liftM2 :: Monad m => (a -> b -> c) -> m a -> m b -> m c

и версия c для монады Maybe,

liftM2 :: (a -> b -> c) -> Maybe a -> Maybe b -> Maybe c

Здесь нет ничего о числах и ошибках деления на ноль. Все, что мы здесь знаем, это то, является ли значение monadi c Just something или Nothing.

. Это позволяет us определить функцию безопасного деления, но она не будет этого делать. для нас всех.

Haskell не является интеллектуальным агентом AI для написания кода. Это просто еще один язык программирования, где программист , а не компьютер, пишет программы. И кроме того, почему он должен решить для вас , предпочитаете ли вы, чтобы 1023 * предпочли ошибку деления на ноль во время выполнения или нет!

3 голосов
/ 24 января 2020

Если вы запустите GHCi и попытаетесь выполнить операцию «обнаженного» деления, вы получите Infinity:

Prelude> 12 / 0
Infinity

liftM2, просто позволяющее выполнить операцию внутри контекста monadi c. В случае Just 12 и Just 0 этот контекст равен Maybe. Это не меняет операции; он имеет дело только с изменчивостью, введенной контейнером.

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.

С другой стороны, если существует ровно два значения, он вызывает функцию. Когда вы вызываете его с помощью Just 12 и Just 0, у вас do есть ровно два значения, и вызывается операция /, приводящая к Infinity.

Это, как и ожидалось .

2 голосов
/ 24 января 2020

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

foo :: IO Double
foo = liftM2 (/) (return 12) (return 0)

Что должно вернуть foo? Это не может быть Nothing, потому что его нет в монаде Maybe.

Вы можете использовать fail, который в монаде Maybe оценивается как Nothing и в более общем случае оценивается как некоторый тип значения исключения, иначе вызовите error в зависимости от монады.

Конечно, в конкретном случае 12 / 0 правильный ответ на самом деле - Infinity. Численные мавены, которые определили стандарт IEEE 754, сделали это таким образом по причине.

...