Как вычесть значения Maybe в Haskell? Например, если я хочу вычесть Just 8 - Just 5, чтобы получить Just 3, как бы я это сделал? - PullRequest
1 голос
/ 07 марта 2020

Как я могу сделать следующее. Например, если я хочу вычесть Just 8 - Just 5, чтобы получить Just 3, как бы я это сделал?

Just 8 - Just 5 = Just 3

Just 15 - Just 9 = Just 6

Ответы [ 4 ]

6 голосов
/ 07 марта 2020

Функция liftA2 (импортированная из Control.Applicative) "поднимает" обычную функцию в контекст Applicative. Например, liftA2 (-) - это функция, которая принимает не два значения Num a => a, а два значения (Applicative f, Num a) => f a и выдает результат в том же контексте. Это работает не только для Maybe:

>>> liftA2 (-) (Just 8) (Just 5)
Just 3

, но и для списков

>>> liftA2 (-) [4,5,6] [1,2,3] -- difference of every pair with one number from each list
[3,2,1,4,3,2,5,4,3]    

Either:

>>> liftA2 (-) (Right 8) (Right 5)
Right 3

функций

>>> liftA2 (-) (+3) (*2) 9 -- (\x -> (x + 3) - (x * 2)) 9 == 12 - 18
-6

* 1022 и др. * * * 1021

3 голосов
/ 07 марта 2020
import Control.Applicative

main = print $ liftA2 (-) (Just 8) (Just 5)
2 голосов
/ 07 марта 2020

Как правило, ожидается, что вы будете использовать подход монади c:

Just 8 >>= \n -> Just 5 >>= \m -> return (n-m)

Однако, не входя в монады, мы все равно можем выполнять эту работу, используя функтор и аппликативные экземпляры типа Maybe.

Сначала давайте fmap оператор (-) над значением Just 8, например (-) <$> Just 8. Это приведет к использованию аппликативного типа Maybe, который равен Just (8-) с сигнатурой типа Just (8-) :: Num a => Maybe (a -> a). Теперь для выполнения задания требуется всего одна аппликативная операция:

λ> (-) <$> (Just 8) <*> (Just 5) 
Just 3

Это иногда делается с помощью LiftA2, как показано в некоторых других ответах, но я думаю, что лучше знать этот шаблон в первую очередь, так как LiftA2 жестко связан с бинарными операторами / функциями, иными словами, если вам нужно поднять троичный оператор, такой как (,,), вам нужно вместо этого использовать LiftA3. Однако вышеприведенный шаблон просто делает это таким же образом, как

λ> (,,) <$> Just 1 <*> Just 't' <*> Just "yeah"
Just (1,'t',"yeah")
2 голосов
/ 07 марта 2020

Использовать понимание монады:

{-# LANGUAGE MonadComprehensions #-}

foo :: (Num b, Monad m) => m b -> m b -> m b
foo a b = [ x - y | x <- a, y <- b ]

Красиво, визуально, недооценено.

foo (Just 8) (Just 5) возвращает Just 3.

Конечно, в таком простом случае это это синтаксическое излишество, вам гораздо лучше с liftA2 (-) как в семантическом, так и в практическом плане.

Где на самом деле сияет понимание монад, когда у вас есть некоторые условные вычисления. Пример classi c - безопасное деление, исключающее ошибки деление на ноль :

safediv :: (MonadPlus m, Fractional b, Eq b) 
        => m b -> m b -> m b
safediv a b = [ x / y | x <- a, y <- b, y /= 0 ]

-- safediv (Just 8) (Just 0)  returns  Nothing

Это операция по существу monadi c, его нельзя кодировать аппликативами, и любой другой альтернативный способ записать его должен быть более подробным.

...