Сглаживание

У меня часто есть код, который имеет шаблон «может быть, ничего, какой-то Функ»:

instance FromJSON SaveSection where
  parseJSON (Object o) =
      SaveSection <$>
      o .:? "eventId" <*>
      (maybe Nothing parseUSDate <$> o .:? "eventDate") <*>
      o .:? "eventRecId" <*>
      o .:? "idxId" <*>
      (maybe Nothing parseUSDate <$> o .:? "idxDate") <*>
      o .:? "idxRecId"

Здесь parseUSDate имеет тип Text -> Maybe Date.

Анализ Aeson, очевидно, возвращает Maybe Text.

Так что мне кажется, что мне нужно поднять 2 слоя Maybe здесь. И я понятия не имею, как это сделать иначе, как с паттерном maybe Nothing someFunc.

Я пропустил какую-то очевидную «сглаживание» или любую другую функцию, которую я мог бы здесь использовать?

EDIT: Спасибо за ответ Алексея.

Это именно то, что я искал. Вот конечный результат:

instance FromJSON SaveSection where
  parseJSON (Object o) =
      SaveSection <$>
      o .:? "eventId" <*>
      ((>>= parseUSDate) <$> o .:? "eventDate") <*>
      o .:? "eventRecId" <*>
      o .:? "idxId" <*>
      ((>>= parseUSDate) <$> o .:? "idxDate") <*>
      o .:? "idxRecId"

person Vagif Verdi    schedule 31.03.2014    source источник
comment
Возможно, пора прочитать Все о монадах :o   -  person Two-Bit Alchemist    schedule 31.03.2014
comment
Проверьте изменения в моем ответе, я думаю, это должно помочь лучше. Моя первоначальная попытка была не совсем правильной, но я считаю, что теперь это исправлено.   -  person bheklilr    schedule 31.03.2014


Ответы (2)


Если у вас есть f' = maybe Nothing f, то типы должны быть f :: a -> Maybe b и f' :: Maybe a -> Maybe b (где a и b могут быть переменными или конкретными типами), иначе проверка типов не будет выполняться. Но это всего лишь тип >>= для монады Maybe: Maybe a -> (a -> Maybe b) -> Maybe b! Таким образом, maybe Nothing f можно записать как (>>= f).

person Alexey Romanov    schedule 31.03.2014
comment
Круто, не знал, что (>>=) = maybe Nothing. - person luqui; 31.03.2014
comment
@luqui Ну, если быть точнее (>>=) = flip (maybe Nothing). - person Alexey Romanov; 31.03.2014
comment
Спасибо. Удивительное понимание для нубов, как я! - person Vagif Verdi; 31.03.2014
comment
@AlexeyRomanov, ну да, мозги. (=<<) = maybe Nothing. (Я предпочитаю (=<<), так он кажется более фундаментальным) - person luqui; 31.03.2014

Есть всегда удобная функция Control.Monad.join:

> join (Just (Just 1))
Just 1
> join (Just Nothing)
Nothing
> join Nothing
Nothing

Я не эксперт по Aeson, но если я это сделаю:

> :m Control.Monad Control.Applicative Data.Aeson Data.Text
> :set -XOverloadedStrings
> :set +m 
> let f :: Text -> Maybe Text
|     f = Just    -- Stand-in for parseUSDate
> :t \o -> join <$> liftM f <$> o .:? "key"
Object -> Parser (Maybe Text)
> -- Has the same type as your expression
> :t \o -> maybe Nothing f <$> o .:? "key"
Object -> Parser (Maybe Text)

Это то, что вы ищете?

РЕДАКТИРОВАТЬ: исправлено, так что это действительно работает... Мой первоначальный универсальный f :: a -> Maybe a все испортил.


Вы можете сделать оператор, чтобы очистить это:

infixl 9
(>>=$) :: (Functor f, Monad m) => f (m a) -> (a -> m b) -> f (m b)
m >>=$ a = join <$> liftM a <$> m

parseJSON (Object o) =
    SaveSection
        <$> o .:? "eventId"
        <*> o .:? "eventDate" >>=$ parseUSDate
        <*> o .:? "eventRecId"
        <*> o .:? "idxId"
        <*> o .:? "idxDate" >>=$ parseUSDate
        <*> o .:? "idxRecId"

(Это должно сработать...)

person bheklilr    schedule 31.03.2014
comment
Спасибо, действительно сработало. Но это решение еще более запутано и трудно читаемо, чем простой шаблон «может быть, ничего». Сравните: (может быть Ничего parseUSDate ‹$› o .:? eventDate) ‹*› ---------- (join ‹$› (parseUSDate ‹$›) ‹$› join ‹$› o .:? Дата события) <*> - person Vagif Verdi; 31.03.2014
comment
@VagifVerdi Я думаю, что мое последнее обновление должно помочь вам в этом, я превратил уродливый шаблон в оператор (не стесняйтесь изменить его на что-то более интуитивно понятное, я просто попытался объединить >>= и <$>), и я бы сказал что результат делает его намного чище. - person bheklilr; 31.03.2014
comment
Хотя вам, возможно, придется повозиться с фиксированностью оператора, чтобы он хорошо работал с <*>, у меня нет всего вашего кода, поэтому его сложно протестировать. - person bheklilr; 31.03.2014
comment
Спасибо, ваш ответ помог понять, как работает объединение. Я принял ответ Алексея, потому что (›== f) просто потрясающий, невероятно лаконичный, но ясный и легко читаемый. Кроме того, это ошеломляющая проницательность для нуба вроде меня. - person Vagif Verdi; 31.03.2014