Попытка упростить проверку IO Bool в парсере Attoparsec

Я пытаюсь упростить приведенный ниже код, который является частью синтаксического анализатора attoparsec для сетевого пакета, и затрудняюсь найти хороший способ сделать это.

Начинается с звонка atEnd :: IO Bool чтобы определить, есть ли еще что-то для анализа. Я не могу найти лучший способ использовать atEnd чем развернуть его из IO и затем использовать его в операторе if, но, похоже, должен быть более простой способ проверить bool внутри монады. Вот код:

maybePayload :: Parser (Maybe A.Value)
maybePayload = do
    e <- atEnd
    if e then return Nothing
    else do
        payload' <- char ':' *> takeByteString
        maybe mzero (return . Just) (maybeResult $ parse A.json payload') 

Намерение состоит в том, чтобы вернуться Nothing если нет полезной нагрузки, вернуть Just A.Value если есть допустимая полезная нагрузка JSON, и для синтаксического анализатора сбой, если есть недопустимая полезная нагрузка.

Вот запись пакета, которая в итоге создается:

data Packet = Packet
    { pID       :: Integer
    , pEndpoint :: String
    , pPayload  :: Maybe A.Value
    }

2 ответа

Решение

Вы делаете много работы, которую вам не нужно делать. Сначала вы проверяете, находитесь ли вы в конце данных и возвращаете Nothing если это не сработает. Это просто не обязательно, потому что, если вы в конце, любой синтаксический анализатор, который требует содержимого, потерпит неудачу, и использование maybeResult превратит эту неудачу в Nothing,

Единственный раз, когда ваш синтаксический анализатор дает сбой, это случай, когда на входе есть данные, которые не начинаются с символа :в остальное время это удается, даже если это путем возврата Nothing,

Единственный фактический разбор происходит проверка на : затем с помощью A.json, Я думаю, что вы пытаетесь написать всю программу внутри одного парсера, тогда как вы должны просто выполнить синтаксический анализ самостоятельно, а затем вызывать это по мере необходимости. Нет необходимости проверять конец данных или получать весь контент - все это бесплатно встроено в парсер. Как только вы избавитесь от всей этой ненужной проверки, вы получите

payload :: Parser A.Value
payload = char ':' *> A.json

Если вы хотите, вы можете использовать это как maybeResult $ parse payload input чтобы получить Maybe A.Value это не дополнительно завернуто в Parser, Если вы не подаете заявку maybeResultВы можете сопоставить образец с Результатом, возвращаемым, чтобы иметь дело с FailЮр, Partial успех и Success,


Изменить: ОК, яснее, спасибо:

  • (Если есть двоеточие, за которым следует неверный json, произойдет ошибка)
  • Если есть двоеточие, за которым следует допустимый json, преуспейте, обернув его в Just
  • Если есть только конец ввода, преуспеть, ничего не возвращая

Итак, мы получаем:

maybePayload :: Parser (Maybe A.Value)
maybePayload = char ':' *> (Just <$> A.json)
            <|> (Nothing <$ endOfInput)

Я использовал <$> а также <$ от Control.Applicativeили, если хотите, из Data.Functor,

<$> это инфиксная версия fmap, так Just <$> A.json делает A.json и оборачивает любой вывод в Just,

<$ является fmap.const так заменяет () от endOfInput с Nothing,

Зачем вам кодировать сбой в Maybe, если в монаде анализатора уже есть встроенное понятие сбоя? Проблема с использованием Maybe таким образом заключается в том, что парсер не может вернуться назад.

Вы можете попробовать что-то вроде этого (я не пытался проверить это), а затем использовать option в звонилке:

payload :: Parser Value
payload = do
  payload' <- char ':' *> takeByteString
  let res = parse A.json payload'
  case res of
    Error msg -> fail msg
    Success a -> return a
Другие вопросы по тегам