Search code examples
f#type-inferencefparsec

Avoiding value restriction error for generic use of function that returns two functions


I want to use the FParsec createParserForwardedToRef function with a generic Expr union, like this:

type Expr<'Term> =
        | Unary of Operator * Expr<'Term>
        | Binary of Operator * Expr<'Term> * Expr<'Term>
        | Ternary of Operator * Expr<'Term> * Expr<'Term> * Expr<'Term>
        | Term of 'Term
let expr, exprR = createParserForwardedToRef<Expr<'T>,unit>()

I can't get rid of the value restriction error. I can't turn expr into a CLR function, much less exprR.

How would I normally deal with this situation?


Solution

  • The tricky bit is the generic Expr<'T>. You can make a function that returns expr parser, and let subsequent use of parsers determine a specific type of Expr<'T>:

    let expr() =
        // initially exprRef holds a reference to a dummy parser 
        let expr, exprRef = createParserForwardedToRef()
    
        // create other union-case parsers as inner or outer functions
        let unary() = ...
        let binary = ...
        let ternary() = ...
        let term() = ...
    
        // replace dummy parser reference in exprRef
        do exprRef := choice [unary(); binary(); ternary(); term()]
        expr
    

    You could also pass primitive parsers such as term as arguments to expr function. In this case, type of expr depends on different kinds of parsers passed on e.g. pstring, pfloat.

    FParsec tutorial has a section on F# value restriction which might also be helpful.