Reputation: 467
I am trying to create a parser for a custom file format. In the format I am working with, some fields have a closing tag like so:
<SOL>
<DATE>0517
<YEAR>86
</SOL>
I am trying to grab the value between the </
and >
and use it as part of the bigger parser.
I have come up with the code below. The trouble is, the parser returns [Char]
instead of Text
. I can pack each Char
by doing fmap pack $ return r
to get a text value out, but I was hoping type inference would save me from having to do this. Could someone give hints as to why I am getting back [Char]
instead of Text
, and how I can get back Text
without having to manually pack the value?
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE OverloadedStrings #-}
import Data.Text
import Text.Parsec
import Text.Parsec.Text
-- |A closing tag is on its own line and is a "</" followed by some uppercase characters
-- followed by some '>'
closingTag = do
_ <- char '\n'
r <- between (string "</") (char '>') (many upper)
return r
Upvotes: 3
Views: 208
Reputation: 15673
string
has the type
string :: Stream s m Char => String -> ParsecT s u m String
(See here for documentation)
So getting a String
back is exactly what's supposed to happen.
Type inference doesn't change types, it only infers them. String
is a concrete type, so there's no way to infer Text
for it.
What you could do, if you need this in a couple of places, is to write a function
text :: Stream s m Char => String -> ParsecT s u m Text
text = fmap pack . string
or even
string' :: (IsString a, Stream s m Char) => String -> ParsecT s u m a
string' = fmap fromString . string
Also, it doesn't matter in this example but you'd probably want to import Text
qualified, names like pack
are used in a number of different modules.
As Ørjan Johansen correctly pointed out, string
isn't actually the problem here, many upper
is. The same principle applies though.
Upvotes: 4
Reputation: 7266
The reason you get [Char]
here is that upper
parses a Char
and many
turns that into a [Char]
. I would write my own combinator along the lines of:
manyPacked = fmap pack . many
You could probably use type-level programming with type classes etc. to automatically choose between many
and manyPack
depending on the expect return type, but I don't think that's worth it. (It would probably look a bit like Scala's CanBuiltFrom).
Upvotes: 3