为什么这个Megaparsec解析器在GHCi中工作,但不在源文件中编译?



我是Haskell和Megaparsec库的初学者。在解析一行文本时,需要解析该行中直到行尾的其余文本(LF或CRLF)。我的想法是使用somenoneOf,但即使在GHCi中测试后也无法获得代码编译,如下所示:

λ> 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 "rn")) "" (pack "a line of textrn")
Right "a line of text"
λ> parse (some (noneOf "rn")) "" (pack "a line of textn")
Right "a line of text"

所以解析器(some (noneOf "rn"))编译成功并返回我所期望的:&;一行文本&;不包括行尾字符。但是,我无法在源文件

中编译以下代码
pLineValue :: Parser Text
pLineValue = do
str <- (some (noneOf "rn"))
return (pack str)

编译器给出如下错误:

• 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 "rn")’
In a stmt of a 'do' block: str <- (some (noneOf "rn"))
In the expression:
do str <- (some (noneOf "rn"))
return (pack str)
|
78 |     str <- (some (noneOf "rn"))
|                   ^^^^^^^^^^^^^

我做错了什么?源文件中的正确语法是什么?或者是否有更好的方法来解析剩余的文本,直到但不包括LF或CRLF结尾?我很感激任何帮助,谢谢。

似乎你的一个符号不是来自你期望的地方。然而,很难准确地说出问题在哪里,因为您只给出了编译代码的一部分,而不是一些自包含的代码。

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

正如Silvio Mayolo在评论中提到的,编译器似乎无法看到"rn"String对象,因此是Char的列表,因此是Foldable类的实例。

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

下面的代码非常相似,但编译(和运行)完美:

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 "rn"))
return (pack str)

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

noneOf接受任意Foldable容器:

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

"rn"通常是String,它是Char的列表:

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

但是,如果您启用了OverloadedStrings,"rn"可以是任何IsString实例:

> :set -XOverloadedStrings
> :t "rn"
"rn" :: IsString p => p

因此对noneOf的调用是二义性的,因为容器的类型没有固定:

> :t noneOf "rn"
noneOf "rn"
:: (Foldable f, MonadParsec e s m,
IsString (f (Token s))) =>
m (Token s)
简单的解决方案是添加类型注释:
> :t noneOf ("rn" :: [Char])
noneOf ("rn" :: [Char])
:: (MonadParsec e s m, Token s ~ Char) => m (Token s)

您可以在任何FoldableTraversable多态函数(如maximumsum)中观察到这一点。

或者,您可以使用显式列表:

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

但是请注意,如果您启用了OverloadedLists,这将有相同类型的欠约束类型问题:

> :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)

如果您在源文件和GHCi之间遇到更多奇怪的差异,通常会归结为GHCi为了方便而使用的差异,例如"扩展的默认规则",因此尝试:set -XNoExtendedDefaultRules:set -XExtendedDefaultRules有时会在这种情况下有所帮助。

最新更新