3
votes

I am trying to use the pipe3 function from the FParsec library but I get an error I don't know how to solve.

Given the Record

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

and the following parser

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

What I try to achieve is a parser that receives a string in format "1.1, 3.7" and returns a Point

run plistoffloats' "1.1, 3.7"

Input : "1.1, 3.7"

Desired output : Point = {x = 1.1; y = 3.7;}

Error :

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.

A simpler example with pchar also didn't work.

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.

1

1 Answers

6
votes

This is covered in the FParsec documentation; it will happen with any parser. The reason is because in the .Net type system, functions are allowed to be generic, but values are not — and in FParsec, you're generally defining parsers as values (e.g., you're typically writing let psomething = ... where psomething takes no parameters). Read the linked documentation page for the whole explanation — I won't copy and paste the whole thing — but the short version is that you can do one of two things:

  1. Create a test function that looks like the following, and make sure it's used within the same source file on your parser:

    let test p str =
        match run p str with
        | Success(result, _, _)   -> printfn "Success: %A" result
        | Failure(errorMsg, _, _) -> printfn "Failure: %s" errorMsg
    
  2. Annotate your parser with a type annotation like the following:

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

It sounds like you're trying to do #1, but unless your parser is called with test plistoffloats' in the same source file, the F# type inference won't be able to infer your user state type and will give you that error.

P.S. You can read more about the F# value restriction error here: Understanding F# Value Restriction Errors

P.P.S. The _ in the first position of Parser<_, UserState> does not mean "This type could be anything" the way _ means in other contexts like pattern matching. Instead, _ in a type annotation means "Please infer this type for me so that I don't have to specify it explicitly". In FParsec contexts, this is very useful because all your parsers will have UserState as their second type argument, but will have a varying type for the first type argument. And since the first type argument is the one that the type inference can infer, it means that you can copy and paste the type Parser<_, UserState> to all your parsers and F# will Do The Right Thing™ in each case.