Search code examples
haskellexistential-typeattoparsec

How attoparsec can return values of different types?


I'm stuck with attoparsec where I can't return a value regarding it's "embedded type".

I attempt to parse a file of kind:

type
value
type
value
...

For example:

0 -- code for a string value
hello
70 -- code for an int value
10
0
world
20 -- code for a double value
5.20

My current data types are:

data KeyValue = forall a. (Typeable a, Show a, Eq a) => KeyValue (Int, a)
instance Eq KeyValue where -- as I need to test equality
    KeyValue (code1, value1) == KeyValue (code2, value2) =
      code1 == code2 && case cast value2 of
                               Just value2' -> value1 == value2
                               Nohing -> False

My parser looks like:

parser :: Parser [KeyValue]
parser = many' (keyValue <* endOfLine)

keyValue :: Parser KeyValue
keyValue = do
    key <- decimal <* endOfLine
    value <- case key of
               0  -> takeLine
               20 -> double
               70 -> decimal
               _ -> takeLine
    return $ KeyValue (key, value)

takeLine :: Parser Text
takeLine = takeTill isEndOfLine

But GHC complains that:

Couldn't match type Double with Text
Expected type: Parser Text Text
  Actual type: Parser Double
In the expression: double
In a case alternative: 20 -> double

I understand why but doesn't know how to fix that!

At the moment, I use ExistantialQuantification pragma with Data.Typeable, but I'm not really sure the solution need to be "so complex" with that problem?


Solution

  • Create a sum type and have your parser return that, e.g.:

    data SumType = Str String | Int Int | Double Double
    
    keyvalue :: Parser SumType
    keyvalue = ...
    

    Now keyvalue can end with return (Str "foo") to indicate that a string has been parsed or with return (Int 23), etc. to indicate that an int has been parsed.

    E.g. something like:

    keyValue = do
        key <- decimal <* endOfLine
        case key of
          20 -> do d <- parseDouble; return (Double d)
          70 -> do i <- parseInt; return (Int i)
          ...