Search code examples
f#quotations

Dynamic Lookup in F#


Can somebody help me with article of Tomas Petricek: http://tomasp.net/blog/fsharp-dynamic-lookup.aspx/#dynfslinks? The problem is that it is severely outdated. I understand that namespaces

open Microsoft.FSharp.Quotations.Typed
open Microsoft.FSharp.Quotations.Raw

are gone. So I removed the openings. But there are still errors. "Typed" is not defined. "RecdGet" is not defined. And I suspect they are not the last. I'm trying to prove to my boss that F# is good to use for database normalization. Dynamic lookup of fields would really helped me to deal with similarly named fields having different prefixes.

There is also post of Tomas on fpish: https://fpish.net/topic/None/57493, which I understand predates the article


Solution

  • Here's a rough equivalent:

    open Microsoft.FSharp.Quotations
    open Microsoft.FSharp.Quotations.Patterns
    
    type DynamicMember<'t,'u> = Expr<'t -> 'u>
    
    let getValueReader (expr:DynamicMember<'recdT, 'fieldT>) = 
      // Match the quotation representing the symbol
      match expr with
      | Lambda(v, PropertyGet (Some (Var v'), pi, [])) when v = v' ->
          // It represents reading of the F# record field..
          // .. get a function that reads the record field using F# reflection
          let rdr = Reflection.FSharpValue.PreComputeRecordFieldReader pi
    
          // we're not adding any additional processing, so we just
          // simply add type conversion to the correct types & return it
          ((box >> rdr >> unbox) : 'recdT -> 'fieldT)
      | _ -> 
          // Quotation doesn't represent symbol - this is an error
          failwith "Invalid expression - not reading record field!"
    
    type SampleRec = { Str : string; Num : int }
    
    let readStrField = getValueReader <@ fun (r : SampleRec) -> r.Str @>
    let readNumField = getValueReader <@ fun (r : SampleRec) -> r.Num @>   
    
    let rc = { Str = "Hello world!"; Num = 42 }
    let s, n = readStrField rc, readNumField rc
    
    printfn "Extracted: %s, %d" s n