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.
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.