Search code examples
parsingparser-combinators

Unpacking nested applicative functors f#


Hi I am attempting to make a combinator parser and I am currently attempting to make it read headers and create parsers based upon what the header which is parsed is. I.e A header of; int, float, string will result in Parser<Parser<int>*Parser<float>*Parser<string>>.

I am wondering however how you would unpack the "inner" parsers which and then end up with a something like; Parser<int*float*string>?

Parser type is: type Parser<'a> = Parser of (string -> Result<'a * string, string>)


Solution

  • I'm not sure that your idea with nested parsers is going to work - if you parse a header dynamically, then you'll need to produce a list of parsers of the same type. The way you wrote this is suggesting that the type of the parser will depend on the input, which is not possible in F#.

    So, I'd expect that you will need to define a value like this:

    type Value = Int of int | String of string | Float of float
    

    And then your parser that parses a header will produce something like:

    let parseHeaders args : Parser<Parser<Value> list> = (...)
    

    The next question is, what do you want to do with the nested parsers? Presumably, you'll need to turn them into a single parser that parses the whole line of data (if this is something like a CSV file). Typically, you'd define a function sequence:

    val sequence : sep:Parser<unit> -> parsers:Parser<'a> list -> Parser<'a list>
    

    This takes a separator (say, parser to recognize a comma) and a list of parsers and produces a single parser that runs all the parsers in a sequence with the separator in between.

    Then you can do:

    parseHeaders input |> map (fun parsers -> sequence (char ',') parsers)
    

    And you get a single parser Parser<Parser<string>>. You now want to run the nested parser on the rest of the that is left after running the outer parser, which recognizes headers. The following function does the trick:

    let unwrap (Parser f:Parser<Parser<'a>>) = Parser (fun s ->
      match f s with
      | Result.Ok(Parser nested, rest) -> nested rest 
      | Result.Error e -> Result.Error e )