Попытка упростить проверку 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