Search code examples
f#fparsec

FParsec Not Recognizing a Type’s Constructor


The following top-level XML parser definition returns the error The value or constructor ‘TOP_LEVEL_RECORD’ is not defined.

let xTop_Level, xTop_Level_Ref = createParserForwardedToRef<TOP_LEVEL_RECORD, unit>()

do xTop_Level_Ref := 
    pipe4 
        (opt xDeclaration) 
        (opt (many xComment_or_CData))
        xElement
        (opt (many xComment_or_CData))
        (fun decl before_root root after_root
            -> {Declaration = decl
                Before_Root = before_root
                Root = root
                After_Root = after_root}) |>> TOP_LEVEL_RECORD
// This returns the error -------------------→^^^^^^^^^^^^^^^^

TOP_LEVEL_RECORD is defined as …

type TOP_LEVEL_RECORD = {Declaration : XDECLARATION option
                         Before_Root : COMMENTS_OR_CDATA list option
                         Root : XELEMENT
                         After_Root : COMMENTS_OR_CDATA list option
                         }

The parsers xDeclaration, xCommentor_Cdata, and xElement are all correctly defined and return the corresponding types in the TOP_LEVEL_RECORD.

The let xTop_Level, xTop_Level_Ref = createParserForwardedToRef<TOP_LEVEL_RECORD, unit>() is Fparsec’s syntax for recursive parser calls documented here: http://www.quanttec.com/fparsec/tutorial.html#parsing-json.createParserForwardedToRef-example.

If I define the type type TOP_LEVEL = TOP_LEVEL_TYPE of TOP_LEVEL_RECORD and replace TOP_LEVEL_RECORD with TOP_LEVEL and TOP_LEVEL_TYPE as follows …

let xTop_Level, xTop_Level_Ref = createParserForwardedToRef<TOP_LEVEL, unit>()
// Replaced this text ------------------------------------->^^^^^^^^^

do xTop_Level_Ref := 
    pipe4 
        (opt xDeclaration) 
        (opt (many xComment_or_CData))
        xElement
        (opt (many xComment_or_CData))
        (fun decl before_root root after_root
            -> {Declaration = decl
                Before_Root = before_root
                Root = root
                After_Root = after_root}) |>> TOP_LEVEL_TYPE
// Replaced this text ----------------------->^^^^^^^^^^^^^^

... the code compiles without any errors or warnings.

Why does TOP_LEVEL_TYPE have a constructor here and not TOP_LEVEL_RECORD?

Can you point me to the relevant part of the F# or FParsec documentation?


Solution

  • TOP_LEVEL_RECORD (a record type) and TOP_LEVEL (a union type) are type names and cannot be used as constructors.

    To construct a TOP_LEVEL_RECORD you use the syntax as in your code

    { Declaration = decl
      Before_Root = before_root
      Root = root
      After_Root = after_root }
    

    To construct an instance of a union type, you use one of the case names as a constructor function; TOP_LEVEL_TYPE in your case, since there's only one union case.

    Note that in your type definition

    type TOP_LEVEL = TOP_LEVEL_TYPE of TOP_LEVEL_RECORD
    

    TOP_LEVEL is a type, but TOP_LEVEL_TYPE (despite its name) is not a type, but a constructor function for the TOP_LEVEL type.

    So record types do not have named constructor functions, but union types do.

    For your code, you can just skip the |>> TOP_LEVEL_RECORD part.

    You can read about record types and union types in F# language spec, sections 8.4 and 8.5.