Вопрос о аппликативном и вложенном - PullRequest
0 голосов
/ 20 октября 2018

Я написал эту функцию:

appFunc :: Integer -> Integer -> Bool -> Maybe (Integer,Integer)
appFunc i1 i2 b = if b then Just (i1,i2) else Nothing

И затем я использую ее как таковую в GHCi:

> appFunc <$> Just 3 <*> Nothing <*> Just True
Nothing

Это здорово, потому что если хотя бы один из параметров равен Nothing тогда все выражение оценивается как Nothing.Однако, когда все параметры равны Just, тогда я получаю вложенный Maybe:

> appFunc <$> Just 3 <*> Just 1 <*> Just False
Just Nothing

В идеале, я хотел бы, чтобы он оценивался как обычный Nothing.Поэтому я решил использовать join:

> join $ appFunc <$> Just 3 <*> Just 1 <*> Just True
Just (3,1) 

Есть ли лучшее решение или более чистый стиль?Я экспериментировал с функцией монады >>=, но безуспешно.Например, я попытался написать:

> Just True >>= appFunc <$> Just 3 <*> Just 1
* Couldn't match expected type `Bool -> Maybe b'
              with actual type `Maybe (Bool -> Maybe (Integer, Integer))'
* Possible cause: `(<*>)' is applied to too many arguments
  In the second argument of `(>>=)', namely
    `appFunc <$> Just 5 <*> Just 4'
  In the expression: Just True >>= appFunc <$> Just 5 <*> Just 4
  In an equation for `it':
      it = Just True >>= appFunc <$> Just 5 <*> Just 4
* Relevant bindings include
    it :: Maybe b (bound at <interactive>:51:1)

Эта ошибка имеет смысл для меня, потому что:

 appFunc <$> Just 3 <*> Just 1 :: m (a -> m b) 

, тогда как >>= :: m a -> (a -> m b) -> m b

Есть ли решение монады или я долженпросто придерживайтесь аппликативного стиля с join?

1 Ответ

0 голосов
/ 20 октября 2018

Почему бы просто

module Main where

import Data.Bool

appFunc :: Integer -> Integer -> Bool -> Maybe (Integer, Integer)
appFunc i1 i2 what = bool Nothing (Just (i1,i2)) what

result = do
  i1 <- Just 1
  i2 <- Just 2
  test <- Just True
  appFunc i1 i2 test

result2 = Just 1 >>= \i1 -> Just 2 >>= \i2 -> Just True >>= appFunc i1 i2 

main = do
  print result
  print result2

Ваш appFunc больше похож на типичный monadFunc.Как уже упоминалось, использование join - это просто монадное решение, я просто перефразировал его в более идиоматический стиль.

Чтобы лучше понять эти вещи, давайте посмотрим на сигнатуру центральной аппликативной операции.

(<*>) :: Applicative f => f (a -> b) -> f a -> f b

Все три параметра для (<*>) являются аппликативно упакованными значениями, и (<*>) знает "обтекание", прежде чем оно должно достигнуть пика, чтобы что-то с ними сделать.Например,

Just (+1) <*> Just 5

здесь, вычисление, включающее «обернутую» функцию (+1) и «обернутое» значение 5, не может изменить «обтекание» Just в этом случае.

Ваш appFunc, с другой стороны, нуждается в чистых значениях, чтобы произвести что-то в «обертке».Это не аппликативно.Здесь нужно сделать некоторые вычисления со значениями, чтобы узнать, какой составной частью будет «обтекание».

Давайте посмотрим на центральную монадическую операцию:

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

Здесьвторой параметр делает именно это.Это функция, принимающая чистое значение и возвращающая что-то в обертке.Так же, как appFunc i1 i2.

...