Search code examples
f#expression-trees

Parsing expression tree into nested lists


I'm relatively new to F# and am really having a real struggle with parsing an expression tree, which contains nested lists. From bits and pieces on the web I have cobbled together the following.

My standard type is defined:

type Return = 
    | Real of float
    | Func of string * Return list

I make a function call to an external application, which returns something like:

val out : Return =
    Func
        ("List",
             [Func ("List",[Real 1.0; Real 2.0; Real 3.0]);
              Func ("List",[Real 1.0; Real 2.0; Real 3.0]);
              Func ("List",[Real 1.0; Real 2.0; Real 3.0])])

and which I'm needing to parse into

[ [1.0; 2.0; 3.0] ; [1.0; 2.0; 3.0] ; [1.0; 2.0; 3.0] ]

My initial naive thought was along the lines of

let rec parse data =
    match data with
    | Real(y) -> y
    | Func("List", x) -> x |> List.map parse 
    | _ -> failwithf "Unrecognised"

but it's complaining about the type differences, which I now understand.

My second thought is to maybe use some recursive List.fold (as I can fold over the list of Reals and get the inner lists, but can't figure out how to generalise it into a recursive function without the compiler complaining out type). Beyond my current intellectual fire-power.

My third thought is that maybe the return I get from the external application is making this too difficult, as there is no indication in the tuple fst about what is in the tuple snd, apart from the fact it is a "list of something"?

There is this: Convert tree to list but it's as cryptic as the solutions I'm trying tbh.

Any pointers as to which avenue I pursue would be really appreciated.


Solution

  • What is the type of the object you want your parse function to return?

    If your desired output is [ [1.0; 2.0; 3.0] ; [1.0; 2.0; 3.0] ; [1.0; 2.0; 3.0] ], it appears that what you want to return is neither a float list nor a float list list, but rather an arbitrarily nested list of float.

    But that doesn't exist as a standard type, and you'll need to define it yourself as a recursive discriminated union (we'll make it generic for good practice):

    type NestedList<'a> = 
        | Single of 'a
        | Collection of NestedList<'a> list
    

    Well look at that! It's just a thin reskin over your original Return type. That's because the original type is pretty much already a standard nested list implementation, except for the "List" label thing.

    The parse function then has almost no real work left to do:

    let rec parse = function
        | Real y ->  Single y
        | Func ("List", returns) -> Collection (returns |> List.map parse)
        | Func (l, _) -> failwithf "Unrecognised label %s" l