Arnob
Arnob

Reputation: 467

Parsec returns [Char] instead of Text

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

Answers (2)

Cubic
Cubic

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

Toxaris
Toxaris

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

Related Questions