2012-05-23 1 views
2

J'ai ce qui suit, qui vérifie de type-:Comment puis-je obtenir Parsec pour me laisser appeler `read` :: Int?

p_int = liftA read (many (char ' ') *> many1 digit <* many (char ' ')) 

Maintenant, comme le nom de la fonction implique, je le veux me donner un Int. Mais si je fais ceci:

p_int = liftA read (many (char ' ') *> many1 digit <* many (char ' ')) :: Int 

Je reçois cette erreur de type:

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 

est-il un plus simple, plus propre moyen d'utiliser des entiers qui peuvent avoir des espaces? Ou un moyen de résoudre ce problème?

En fin de compte, je veux que cela fasse partie des éléments suivants:

betaLine = string "BETA " *> p_int <*> p_int <*> p_int <*> 
      p_int <*> p_parallel <*> p_exposure <* eol 

qui est d'analyser les lignes qui ressemblent à ceci:

BETA 6 11 5 24 -1 oiiio 

Je peux donc éventuellement appeler un constructeur BetaPair qui sera besoin de ces valeurs (certaines comme Int, d'autres comme d'autres types comme [Exposure] et Parallel)

(si vous êtes curieux, ceci est un analyseur pour un format de fichier qui représente, entre autres, choses, des paires de brins bêta liés à l'hydrogène dans les protéines. Je n'ai aucun contrôle sur le format de fichier!)

Répondre

5

p_int est un analyseur qui produit un Int, donc le type serait Parser Int ou similaire¹.

p_int = liftA read (many (char ' ') *> many1 digit <* many (char ' ')) :: Parser Int 

Vous pouvez saisir la fonction read, (read :: String -> Int) pour indiquer au compilateur qui tapez l'expression a.

p_int = liftA (read :: String -> Int) (many (char ' ') *> many1 digit <* many (char ' ')) :: Int 

En ce qui concerne les moyens propres, envisager de remplacer many (char ' ') avec spaces.

¹ ParsecT x y z Int, par exemple.

+0

parfait, merci! taper la fonction de lecture est exactement ce dont j'avais besoin; Je ne faisais que fausser la syntaxe. –

5

How do I get Parsec to let me call read :: Int ?

Une deuxième réponse est "Ne pas utiliser en lecture". L'utilisation de read équivaut à ré-analyser les données que vous avez déjà analysées. Par conséquent, l'utiliser dans un analyseur Parsec est une odeur de code. L'analyse syntaxique des nombres naturels est assez inoffensive, mais read a une sémantique de défaillance différente de celle de Parsec et est adaptée à la syntaxe lexicale de Haskell. L'utiliser pour des formats numériques plus compliqués est donc problématique.

Si vous ne voulez pas aller à la peine de définir un LanguageDef et en utilisant le module Token de parsec est ici un analyseur de nombre naturel qui n'utilise pas la lecture:

-- | 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 
+1

Merci, c'est très utile. En effet, ce que je suis l'analyse est simplement un format de fichier de données, donc un LanguageDef complet ne semble pas utile. En effet, je dois aussi analyser les valeurs de negativeLogProbability, qui (en tant que logs de probabilités) sont soit '*' (probabilité nulle) ou Double (probabilité non nulle). Comment proposeriez-vous d'analyser Double sans utiliser read? –

+0

Salut @Noah - pour l'analyse double, je déléguerais au maître et je verrais Daan Leijen l'a fait dans le code source du module Parsec.Token. –

1

Vous pouvez trouver

Text-Megaparsec-Lexer.integer :: MonadParsec s m Char => m Integer 

fait ce que vous voulez.La bibliothèque parsec de vanilla semble manquer un certain nombre de parseurs évidents, ce qui a conduit à l'augmentation des paquets dérivés de batteries parsec "batteries incluses". Je suppose que les mainteneurs de parsec finiront par se tourner vers des betteries par la suite.

https://hackage.haskell.org/package/megaparsec-4.2.0/docs/Text-Megaparsec-Lexer.html

MISE À JOUR

ou parsec vanille:

Prelude Text.Parsec Text.Parsec.Language Text.Parsec.Token> parse (integer . makeTokenParser $ haskellStyle) "integer" "-1234" 
Right (-1234) 
Questions connexes