FParsec 和 pipe3 使参数显式或添加类型符号

FParsec and pipe3 make the arguments explicit or add a type notation

我正在尝试使用 FParsec 库中的 pipe3 函数,但出现错误,我不知道如何解决。

鉴于记录

type Point = { x: float; y: float }

和以下解析器

let plistoffloats' =
    pipe3 pfloat (pchar ',' .>> spaces) pfloat 
        (fun first z second -> { x = first; y = second })

我试图实现的是一个解析器,它接收格式为 "1.1, 3.7" 和 returns a Point

的字符串
run plistoffloats' "1.1, 3.7"

输入 : "1.1, 3.7"

期望输出Point = {x = 1.1; y = 3.7;}

错误

error FS0030: Value restriction. The value 'plistoffloats'' has been inferred to have generic type val plistoffloats' : Parser <Point,'__a>
Either make the arguments to 'plistoffloats'' explicit or, if you do not intend for it to be generic, add a type annotation.

使用 pchar 的更简单的示例也不起作用。

let parsesA = pchar 'a'

error FS0030: Value restriction. The value 'parsesA' has been inferred to have generic type val parsesA : Parser<char,'_a> Either make the arguments to 'parsesA' explicit or, if you do not intend for it to be generic, add a type annotation.

这在FParsec documentation中有介绍;它会发生在任何解析器上。原因是因为在 .Net 类型系统中,允许 functions 是通用的,但 values 不是——而在 FParsec 中,你是通常将解析器定义为值(例如,您通常编写 let psomething = ...,其中 psomething 不带参数)。阅读链接的文档页面以获得完整的解释——我不会复制和粘贴整个内容——但简短的版本是你可以做以下两件事之一:

  1. 创建一个如下所示的 test 函数,并确保它在您的解析器的同一个源文件中使用:

    let test p str =
        match run p str with
        | Success(result, _, _)   -> printfn "Success: %A" result
        | Failure(errorMsg, _, _) -> printfn "Failure: %s" errorMsg
    
  2. 使用如下类型注释来注释您的解析器:

    type UserState = unit   // You might change this later
    let plistoffloats' : Parser<_, UserState> =
        // ...
    

听起来您正在尝试执行 #1,但除非您的解析器在同一个源文件中使用 test plistoffloats' 调用,否则 F# 类型推断将无法推断您的用户状态类型并会给你那个错误。

P.S。您可以在此处阅读有关 F# 值限制错误的更多信息:Understanding F# Value Restriction Errors

P.P.S。 Parser<_, UserState> 的第一个位置的 _ 并不像 _ 在模式匹配等其他上下文中的意思那样表示 "This type could be anything"。相反,类型注释中的 _ 表示 "Please infer this type for me so that I don't have to specify it explicitly"。在 FParsec 上下文中,这非常有用,因为您的所有解析器都将 UserState 作为其 second 类型参数,但 first[ 将具有不同的类型=45=] 类型参数。由于 first 类型参数是类型推断可以推断的参数,这意味着您可以将类型 Parser<_, UserState> 复制并粘贴到所有解析器,F# 将执行在每种情况下都是正确的事™。