Вопрос по haskell – Как мне заставить Parsec позволить мне вызывать `read` :: Int?
Я получил следующие проверки типов:
p_int = liftA read (many (char ' ') *> many1 digit <* many (char ' '))
Теперь, как следует из названия функции, я хочу, чтобы оно давало мне Int. Но если я сделаю это:
p_int = liftA read (many (char ' ') *> many1 digit <* many (char ' ')) :: Int
Я получаю эту ошибку типа:
Couldn't match expected type `Int' with actual type `f0 b0'
In the return type of a call of `liftA'
In the expression:
liftA read (many (char ' ') *> many1 digit <* many (char ' ')) ::
Int
In an equation for `p_int':
p_int
= liftA read (many (char ' ') *> many1 digit <* many (char ' ')) ::
Int
Есть ли более простой и понятный способ анализа целых чисел, которые могут иметь пробелы? Или способ это исправить?
В конечном итоге я хочу, чтобы это было частью следующего:
betaLine = string "BETA " *> p_int <*> p_int <*> p_int <*>
p_int <*> p_parallel <*> p_exposure <* eol
который должен разобрать строки, которые выглядят так:
BETA 6 11 5 24 -1 oiiio
Таким образом, я могу в конечном итоге вызвать конструктор BetaPair, который будет нуждаться в этих значениях (некоторые как Int, некоторые как другие типы, такие как [Exposure] и Parallel)
(если вам интересно, это парсер для формата файла, который представляет собой, среди прочего, пары бета-цепей с водородными связями в белках. У меня нет контроля над форматом файла!)
Вы можете найти
Text-Megaparsec-Lexer.integer :: MonadParsec s m Char => m Integer
делает то, что вы хотите.
В библиотеке vanilla parsec, по-видимому, отсутствует ряд очевидных синтаксических анализаторов, что привело к увеличению количества «включенных батарей». производные пакеты parsec. Я полагаю, что в конце концов сопровождающие парсек обойдутся с лучшими.
https://hackage.haskell.org/package/megaparsec-4.2.0/docs/Text-Megaparsec-Lexer.html
ОБНОВИТЬ
или с ванильным парсеком:
Prelude Text.Parsec Text.Parsec.Language Text.Parsec.Token> parse ( integer . makeTokenParser $ haskellStyle ) "integer" "-1234"
Right (-1234)
How do I get Parsec to let me call
read :: Int
?
Вторым ответом является «Не используйте чтение».
С помощьюread
эквивалентно повторному анализу данных, которые вы уже проанализировали, поэтому использование его в анализаторе Parsec является запахом кода. Разбор натуральных чисел достаточно безвреден, ноread
имеет семантику сбоя, отличную от Parsec, и она адаптирована к лексическому синтаксису Haskell, поэтому использование его для более сложных числовых форматов проблематично.
Если вы не хотите пытаться определитьLanguageDef
и используя парсекToken
Модуль здесь - это анализатор натуральных чисел, который не использует read:
-- | Needs @foldl'@ from Data.List and
-- @[email protected] from Data.Char.
--
positiveNatural :: Stream s m Char => ParsecT s u m Int
positiveNatural =
foldl' (\a i -> a * 10 + digitToInt i) 0 <$> many1 digit
p_int
это парсер, который производитInt
так что тип будетParser Int
или аналогичный & # xB9 ;.
p_int = liftA read (many (char ' ') *> many1 digit <* many (char ' ')) :: Parser Int
Кроме того, вы можете ввестиread
функция,(read :: String -> Int)
сообщить компилятору, какой тип имеет выражение.
p_int = liftA (read :: String -> Int) (many (char ' ') *> many1 digit <* many (char ' ')) :: Int
Что касается более чистых способов, рассмотрите возможность заменыmany (char ' ')
сspaces
.
& # XB9;ParsecT x y z Int
, например.