Moving FParsec code to an F# module does not work

52 Views Asked by At

I'm a newbie in F# and in FParsec and have problems with moving my FParsec code into an F# module using VS 2022 Community Edition, DotNet 6.0.

My use case is this: My console app solution currently consists of a single Program.fs that I can build and run without any problems. Now, I want to move some parts of my code to an F# module so I can reuse it. That causes weird type errors I cannot resolve.

This is a working example to reproduce my problem. Consider a console app solution consisting of a single simple Program.fs file containing

open FParsec

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

printfn "%O" result

I can build and run the code without any problems.

Now, if I create a module MyModule.fs like this:

module MyModule
open FParsec
let p = pstring "foo"

and open it in Program.fs like this:

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

I get the following two build errors:

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'

Why do I get these two errors although the code split with the module seems to be semantically identical to the previous code without the module and how can I resolve these errors?

1

There are 1 best solutions below

1
Tarmil On

As explained by @Gus in the comments, the problem is that parsers have a second type parameter for user data, that was disambiguated by the call to run in the original code but not in the modularized code.

You can fix this by specifying the full type for the parser:

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

Then, when you combine several parsers, you'll only need to specify this once and it will disambiguate for all parsers that use it or that it uses. It's typically done on the final parser.

let p1 = pstring "foo"

let p2 = pstring "bar"

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

Related Questions in F#