4

I am a beginner user of Haskell and the Megaparsec library. While parsing a line of text, I come to a point where I need to parse the remaining text in the line up until the end-of-line (either LF or CRLF). My thought was to use some and noneOf but cannot get the code to compile even after testing in GHCi as follows:

λ> import Data.Text (Text, pack)
λ> import Data.Void
λ> import Text.Megaparsec as M
λ> import Text.Megaparsec.Char as M
λ> import qualified Text.Megaparsec.Char.Lexer as L
λ> type Parser = Parsec Void Text
λ> 
λ> parse (some (noneOf "\r\n")) "" (pack "a line of text\r\n")
Right "a line of text"
λ> parse (some (noneOf "\r\n")) "" (pack "a line of text\n")
Right "a line of text"

So the parser (some (noneOf "\r\n")) compiles successfully and returns what I expected: "a line of text" not including the end-of-line character(s). However, I cannot get the following code to compile in a source file

pLineValue :: Parser Text
pLineValue = do
    str <- (some (noneOf "\r\n"))
    return (pack str)

The compiler gives following error:

    • Ambiguous type variable ‘f0’ arising from a use of ‘noneOf’
      prevents the constraint ‘(Foldable f0)’ from being solved.
      Probable fix: use a type annotation to specify what ‘f0’ should be.
      These potential instances exist:
        instance Foldable (Either a) -- Defined in ‘Data.Foldable’
        instance Foldable Maybe -- Defined in ‘Data.Foldable’
        instance Foldable ((,) a) -- Defined in ‘Data.Foldable’
        ...plus one other
        ...plus 37 instances involving out-of-scope types
        (use -fprint-potential-instances to see them all)
    • In the first argument of ‘some’, namely ‘(noneOf "\r\n")’
      In a stmt of a 'do' block: str <- (some (noneOf "\r\n"))
      In the expression:
        do str <- (some (noneOf "\r\n"))
           return (pack str)
   |
78 |     str <- (some (noneOf "\r\n"))
   |                   ^^^^^^^^^^^^^

What am I doing wrong? What is the correct syntax in the source file? or is there a better approach to parse the remaining text up to but not including the LF or CRLF ending? I'd appreciate any help, Thanks.

Leo U.
  • 43
  • 3
  • 4
    Do you have `OverloadedStrings` on, perhaps? That would make the argument to `noneOf` have ambiguous type. – Silvio Mayolo Feb 23 '21 at 00:14
  • 1
    Congratulations for posting the full text of the error message ! You'd be surprised to learn how many posters just write “This code does not compile” :-( – jpmarinier Feb 23 '21 at 12:29

2 Answers2

2

It seems that one of your symbols does not come from where you expect. However, it is difficult to tell exactly where the problem is, as you give just a part of your compiled code instead of something self-contained.

https://stackoverflow.com/help/minimal-reproducible-example

As mentioned in the comment by Silvio Mayolo, the compiler seems unable to see that "\r\n" is a String object, hence a list of Chars, hence an instance of the Foldable class.

 λ> 
 λ> :type M.noneOf
M.noneOf
  :: (Foldable f, MonadParsec e s m) => f (Token s) -> m (Token s)
 λ> 

This code below is very similar, but compiles (and runs) flawlessly:

import  Data.Text (Text, pack, unpack)
import  Data.Void
import  qualified  Text.Megaparsec  as  M

type MyParser = M.Parsec Void Text

pLineValue :: MyParser Text
pLineValue = do
    str <- (M.some (M.noneOf "\r\n"))
    return (pack str)


main :: IO ()
main = do
    let resT = M.parse pLineValue "-" (pack "a line of text\r\n")
        resS = case resT of
                 Right txt  ->  unpack txt
                 Left  _    ->  "ERROR"
    putStrLn $ "resS = " ++ resS


jpmarinier
  • 4,427
  • 1
  • 10
  • 23
1

noneOf takes an arbitrary Foldable container:

noneOf :: (Foldable f, MonadParsec e s m) => f (Token s) -> m (Token s)

"\r\n" is ordinarily a String, which is a list of Char:

> :t "\r\n"
"\r\n" :: [Char]

> :i String
type String = [Char]    -- Defined in ‘GHC.Base’

However, if you have OverloadedStrings enabled, "\r\n" can be any IsString instance:

> :set -XOverloadedStrings
> :t "\r\n"
"\r\n" :: IsString p => p

Therefore the call to noneOf is ambiguous because the type of container isn’t pinned down:

> :t noneOf "\r\n"
noneOf "\r\n"
  :: (Foldable f, MonadParsec e s m,
      IsString (f (Token s))) =>
     m (Token s)

The simple solution is to add a type annotation:

> :t noneOf ("\r\n" :: [Char])
noneOf ("\r\n" :: [Char])
  :: (MonadParsec e s m, Token s ~ Char) => m (Token s)

You can observe this with any Foldable- or Traversable-polymorphic function like maximum or sum.

Alternatively, you can use an explicit list instead:

> :t noneOf ['\r', '\n']
noneOf ['\r', '\n']
  :: (MonadParsec e s m, Token s ~ Char) => m (Token s)

But be aware that this will have the same sort of underconstrained-type issue if you have OverloadedLists enabled:

> :set -XOverloadedLists
> :t noneOf ['\r', '\n']
noneOf ['\r', '\n']
  :: (Foldable f, MonadParsec e s m,
      IsList (f (Token s)),
      Item (f (Token s)) ~ Char) =>
     m (Token s)

If you encounter further strange differences between a source file and GHCi, it often comes down to differences that GHCi uses for convenience, such as the “extended default rules”, so trying :set -XNoExtendedDefaultRules vs. :set -XExtendedDefaultRules can sometimes help in situations like this.

Jon Purdy
  • 53,300
  • 8
  • 96
  • 166
  • I do have `OverloadedStrings` enabled. Both suggested solutions: a type annotation (`:: [Char]`) or an explicit list `['\r', '\n']` will resolve the issue, code now compiles and runs successfully. – Leo U. Feb 23 '21 at 20:02