将 FParsec 代码移动到 F# 模块不起作用

Moving FParsec code to an F# module does not work

提问人:bookofproofs 提问时间:8/16/2023 更新时间:8/16/2023 访问量:49

问:

我是 F# 和 FParsec 的新手,在使用 VS 2022 社区版 DotNet 6.0 将我的 FParsec 代码移动到 F# 模块时遇到问题。

我的用例是这样的:我的控制台应用程序解决方案目前由一个我可以毫无问题地构建和运行的单个解决方案组成。现在,我想将代码的某些部分移动到 F# 模块,以便可以重用它。这会导致我无法解决的奇怪类型错误。Program.fs

这是重现我的问题的工作示例。考虑一个控制台应用解决方案,该解决方案由一个包含Program.fs

open FParsec

let p = pstring "foo"
let result = run p "foo"

printfn "%O" result

我可以毫无问题地构建和运行代码。

现在,如果我创建一个这样的模块:MyModule.fs

module MyModule
open FParsec
let p = pstring "foo"

并像这样打开它:Program.fs

open FParsec
open MyModule
let result = run p "foo"
printfn "%O" result

我收到以下两个构建错误:

In MyModule.fs, line 3: Error   FS0030  Value restriction. The value 'p' has been inferred to have generic type val p: Parser<string,'_a>    
Either make the arguments to 'p' explicit or, if you do not intend for it to be generic, add a type annotation. 

In Program.fs, line 3: Error    FS0001  Type mismatch. Expecting a 'Parser<'a,unit>' but given a 'Parser<string,obj>'    
The type 'unit' does not match the type 'obj'

为什么我会收到这两个错误,尽管与模块拆分的代码在语义上似乎与没有模块的先前代码相同,我该如何解决这些错误?

模块 f# fparsec

评论

2赞 Gus 8/16/2023
问题在于值限制(您可以在此处搜索它)。在第一种情况下,函数消除了值限制的歧义,这就是它与该设置一起使用的原因。run
0赞 bookofproofs 8/16/2023
那么,它是否是 F# 编译器无法跨模块消除歧义的“功能,而不是错误”?
0赞 Gus 8/16/2023
模块编译与其用途无关,因此在编译模块时不考虑模块外部用途的类型信息。
0赞 bookofproofs 8/16/2023
那么有没有办法将我的语言的解析器语法分离到一个模块中,而不必在模块内运行解析器?我想在主 Program.fs 中运行解析器,但在模块中定义它。谢谢。
0赞 Gus 8/16/2023
是的,您可以指定完整类型,添加显式参数,值限制问题的所有替代解决方案都将在此处应用。

答:

2赞 Tarmil 8/16/2023 #1

正如 @Gus 在注释中解释的那样,问题在于解析器具有用于用户数据的第二种类型参数,该参数在原始代码中被调用消除了歧义,但在模块化代码中没有。run

您可以通过指定解析器的完整类型来解决此问题:

let p: Parser<string, unit> = pstring "foo"

然后,当您组合多个解析器时,只需指定一次,它就会为使用它或它使用的所有解析器消除歧义。它通常在最终解析器上完成。

let p1 = pstring "foo"

let p2 = pstring "bar"

let p: Parser<string, unit> = p1 <|> p2

评论

0赞 bookofproofs 8/16/2023
谢谢,它解决了我的问题。我自己无法弄清楚显式键入 p 变量的正确语法,因为 F# 编译器错误显示“Parser<string,'_a>”而不是“Parser<string,unit>”,这误导了像我这样的新手:-)。