lundi 9 décembre 2019

F# How to extract keys from a boxed Map object

This is a follow-up of this post and that post.

I need to write a function which takes an object (obj type) and a key (also an obj type), and if the object happens to be a Map, that is any Map<'k,'v> then extracts its keys and values.

The difficulty is that I cannot parametrize the function with generic types and that we cannot pattern-match objects on generic types.

I am not familiar with F# Reflection, but I found a way to get the Map's values, once I know its keys. With this example code :

module TestItem = 
    open System
    open Microsoft.FSharp.Reflection

    // some uninteresting types for this example, could be anything arbitrary
    type Foo = {argF1 : string; argF2 : double; argF3 : bool[]}
    type Bar = {argB1 : string; argB2 : double; argB3 : Foo[]}

    // and their instances
    let foo1 = {argF1 = "foo1"; argF2 = 1.0; argF3 = [| true  |]}
    let foo2 = {argF1 = "foo2"; argF2 = 2.0; argF3 = [| false |]}

    let bar1 = {argB1 = "bar1"; argB2 = 10.0; argB3 = [| foo1 |]}
    let bar2 = {argB1 = "bar2"; argB2 = 20.0; argB3 = [| foo2 |]}

    // a Map type
    type Baz = Map<String,Bar>    
    let baz : Baz = [| ("bar1", bar1); ("bar2", bar2) |] |> Map.ofArray

    let item (oMap : obj) (key : obj) : unit =
        let otype = oMap.GetType()

        match otype.Name with
        | "FSharpMap`2" -> 
            printfn "  -Map object identified"
            let prop  = otype.GetProperty("Item")

            try
                let value = prop.GetValue(oMap, [| key |]) 
                printfn "  -Value associated to key:\n %s" (value.ToString())
            with
            | _             ->  
                printfn "  -Key missing from oMap"
        | _             ->  
            printfn "  -Not a Map object"

    [<EntryPoint>]
    let main argv =
        printfn "#test with correct key"
        let test = item baz "bar1"

        printfn "\n#test with incorrect key"
        let test = item baz "bar1X"

        Console.ReadKey() |> ignore
        0 // return exit code 0

Running the code above ouputs the following to the Console :

#test with correct key
  -Map object identified
  -Value associated to key:
 {argB1 = "bar1";
 argB2 = 10.0;
 argB3 = [|{argF1 = "foo1";
            argF2 = 1.0;
            argF3 = [|true|];}|];}

#test with incorrect key
  -Map object identified
  -Key missing from oMap

Now, to solve my problem, I would just need to find a way to extract the keys from the oMap object.

My question : how to complete the code below to return the oMap keys, of type obj[], if oMap is indeed a boxed Map object?

module CompleteThis =
    open System
    open Microsoft.FSharp.Reflection

    let keys (oMap : obj) (key : obj) : obj[] =
        let otype = oMap.GetType()

        match otype.Name with
        | "FSharpMap`2" -> 
            printfn "  -Map object identified"

            (* COMPLETE HERE *)
            Array.empty // dummy
        | _             ->  
            printfn "  -Not a Map object"
            Array.empty // return empty array




Aucun commentaire:

Enregistrer un commentaire