Can F # Quotations be used to create a function that applies to arbitrary F # record types?

To write F #:

type R = { X : string ; Y : string }

and two objects:

let  a = { X = null ; Y = "##" }
let  b = { X = "##" ; Y = null }

and the predicate on the lines:

let (!?) : string -> bool = String.IsNullOrWhiteSpace

and function:

let (-?>) : string -> string -> string = fun x y -> if !? x then y else x

There is a way to use F # quotes to determine:

let (><) : R -> R -> R

with behavior:

let c = a >< b // = { X = a.X -?> b.X ; Y = a.Y -?> b.Y }

so that somehow it (><)works for any arbitrary F # notation, not just for R .

Short . Can quotes be used to generate F # code for (><)on-the-fly determination , given an arbitrary record type and padding function (-?>)applicable to its fields?

If quotes cannot be used, what can?

+2
source share
1 answer

F # , , , F # PowerPack. , , F #:

open Microsoft.FSharp.Reflection

let applyOnFields (recd1:'T) (recd2:'T) f =  
  let flds1 = FSharpValue.GetRecordFields(recd1)  
  let flds2 = FSharpValue.GetRecordFields(recd2)  
  let flds = Array.zip flds1 flds2 |> Array.map f
  FSharpValue.MakeRecord(typeof<'T>, flds)

, f . , ( ):

type R = { X : string ; Y : string } 
let  a = { X = null ; Y = "##" } 
let  b = { X = "##" ; Y = null } 

let selectNotNull (x:obj, y) =
  if String.IsNullOrWhiteSpace (unbox x) then y else x

let c = applyOnFields a b selectNotNull 

Reflection , . .NET Reflection , applyOnFields. AST, , , . - :

let applyOnFields (a:R) (b:R) f = { X = f (a.X, b.X); Y = f (a.Y, b.Y) }

, , :

open Microsoft.FSharp.Quotations

// Get information about fields
let flds = FSharpType.GetRecordFields(typeof<R>) |> List.ofSeq

// Generate two variables to represent the arguments
let aVar = Var.Global("a", typeof<R>)
let bVar = Var.Global("b", typeof<R>)

// For all fields, we want to generate 'f (a.Field, b.Field)` expression
let args = flds |> List.map (fun fld ->
  // Create tuple to be used as an argument of 'f'
  let arg = Expr.NewTuple [ Expr.PropertyGet(Expr.Var(aVar), fld)
                            Expr.PropertyGet(Expr.Var(bVar), fld) ]
  // Call the function 'f' (which needs to be passed as an input somehow)
  Expr.App(???, args)

// Create an expression that builds new record
let body = Expr.NewRecord(typeof<R>, args)

, F # PowerPack. . .

+6

Source: https://habr.com/ru/post/1543255/


All Articles