WedX - журнал о программировании и компьютерных науках

Вопрос об аппликативных и вложенных возможностях

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

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?


  • Я не могу придумать ничего проще. Я думал об определении собственного <$>-подобного комбинатора, но это кажется неосуществимым, потому что исходное выражение анализируется как (((appFunc <$> Just 3) <*> Just 1) <*> Just True), поэтому даже переопределение <$> не помогает (если только мы не хотим что-то, что работает только с арностью 3). 20.10.2018
  • Возможно, стоит упомянуть, что join является решением монады: join :: Monad m => m (m a) -> m a, join mm = mm >>= id и m >>= f = join (f <$> m). 20.10.2018

Ответы:


1

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

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. Как уже упоминалось duplode, использование 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.

20.10.2018
  • Спасибо, использование do было большим идиоматичным моментом AHA! 20.10.2018
  • Новые материалы

    Я хотел выучить язык программирования MVC4, но не мог выучить его раньше, потому что это выглядит сложно…
    Просто начните и учитесь самостоятельно Я хотел выучить язык программирования MVC4, но не мог выучить его раньше, потому что он кажется мне сложным, и я бросил его. Это в основном инструмент..

    Лицензии с открытым исходным кодом: руководство для разработчиков и создателей
    В динамичном мире разработки программного обеспечения открытый исходный код стал мощной парадигмой, способствующей сотрудничеству, инновациям и прогрессу, движимому сообществом. В основе..

    Объяснение документов 02: BERT
    BERT представил двухступенчатую структуру обучения: предварительное обучение и тонкая настройка. Во время предварительного обучения модель обучается на неразмеченных данных с помощью..

    Как проанализировать работу вашего классификатора?
    Не всегда просто знать, какие показатели использовать С развитием глубокого обучения все больше и больше людей учатся обучать свой первый классификатор. Но как только вы закончите..

    Работа с цепями Маркова, часть 4 (Машинное обучение)
    Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

    Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
    Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..

    Использование машинного обучения и Python для классификации 1000 сезонов новичков MLB Hitter
    Чему может научиться машина, глядя на сезоны новичков 1000 игроков MLB? Это то, что исследует это приложение. В этом процессе мы будем использовать неконтролируемое обучение, чтобы..


    Для любых предложений по сайту: [email protected]