Search code examples
haskellmegaparsec

Using parseTest in megaparsec 7.0.4


Advent of Code means I'm using megaparsec for the first time in a while. And things have changed around a fair bit since last time. Assuming

import qualified Text.Megaparsec as MP
import qualified Text.Megaparsec.Char as C

I tried

MP.parseTest (C.char '+') "+"

in GHCI, which gives the following rather unhelpful error message:

interactive>:121:1: error:
* Ambiguous type variable `e0' arising from a use of `MP.parseTest'
  prevents the constraint `(MP.ShowErrorComponent
                              e0)' from being solved.
  Probable fix: use a type annotation to specify what `e0' should be.
  These potential instance exist:
    one instance involving out-of-scope types
    (use -fprint-potential-instances to see them all)
* In the expression: MP.parseTest (C.char '+') "+"
  In an equation for `it': it = MP.parseTest (C.char '+') "+"

<interactive>:121:15: error:
* Ambiguous type variable `e0' arising from a use of `C.char'
  prevents the constraint `(Ord e0)' from being solved.
  Probable fix: use a type annotation to specify what `e0' should be.
  These potential instances exist:
    instance (Ord a, Ord b) => Ord (Either a b)
      -- Defined in `Data.Either'
    instance Ord Ordering -- Defined in `ghc-prim-0.5.3:GHC.Classes'
    instance Ord Integer
      -- Defined in `integer-gmp-1.0.2.0:GHC.Integer.Type'
    ...plus 25 others
    ...plus 131 instances involving out-of-scope types
    (use -fprint-potential-instances to see them all)
* In the first argument of `MP.parseTest', namely `(C.char '+')'
  In the expression: MP.parseTest (C.char '+') "+"
  In an equation for `it': it = MP.parseTest (C.char '+') "+"

Am I using this correctly? What do I need to do to fix this?


Solution

  • The latest versions of "megaparsec" support custom error types defined by the user. The ParsecT type is parameterized by the type of the custom errors. To support pretty-printing (like in parseTest) such custom errors must be instances of the Ord and ShowErrorComponent typeclasses.

    If we never throw any custom errors, the error type remains polymorphic. But at the end, when printing the results, we must provide a concrete type. We can use the uninhabited type Void from the "void" package, and inform the type checker by using an explicit type signature.

    The documentation recommends defining a type synonym like type Parser = Parsec Void Text and using it in your signatures.

    Another way of avoiding the ambiguity is by using visible type application:

    Prelude Text.Megaparsec Data.Void> :t parseTest
    parseTest
      :: (ShowErrorComponent e, Show a, Stream s) =>
         Parsec e s a -> s -> IO ()
    Prelude Text.Megaparsec Data.Void> :set -XTypeApplications
    Prelude Text.Megaparsec Data.Void> :t parseTest @Void
    parseTest @Void
      :: (Show a, Stream s) => Parsec Void s a -> s -> IO ()