简体   繁体   中英

Can't find PropertyGet expression in F# quotation

My goal is to create a function which accepts a generic 'property accesser' function and returns the name of the property it is accessing. For example:

type Person = { Name: string; Age: int }
let blake = { Name = "Blake"; Age = 30 }

let requestedProperty = propertyAccess (fun p -> p.Name) // should return "Name"

I want propertyAccess to be generic, and to be usable with any type. It might have the signature:

'T -> 'TProperty -> string

So far, I have tried creating a quotation out of the delegate function and pattern matching the expression tree looking for a PropertyGet . Unfortunately the quotation expression tree has the shape:

NewDelegate (Func`2, delegateArg0,
             Application (ValueWithName (<fun:result@88>, f), delegateArg0))

and I'm not able to find the PropertyGet. How can I achieve this?

The entire code looks like:


let propertyAccess (f: 'T -> 'TProperty) =
    let expression = <@ f @>

    let rec findProperty expr =
        match expr with
        | Application(ex1, ex2) -> findProperty ex1
        | PropertyGet(o, info, lst) -> sprintf "Property: %s" info.Name 
        | Lambda(param, body) -> findProperty body
        | NewDelegate(t, lst, ex) -> findProperty ex
        | ValueWithName(ob, t, s) -> sprintf "ValueName: %s" s
        | _ -> "Couldn't find property"

type Person = { Name: string; Age: int }
let blake = { Name = "Blake"; Age = 30 }

let requestedProperty = propertyAccess (fun p -> p.Name) // should return "Name"

Thanks

The problem is that you're simply wrapping a (function) value in a quotation, and the AST ends up being just a ValueWithName .

You want the compiler to automatically quote your argument expression.

type Props =
    static member propertyAccess ([<ReflectedDefinition>] expr: Expr<'a -> 'b>) =
      match expr with
      | Patterns.Lambda(_, Patterns.PropertyGet(_, prop, _)) -> prop.Name
      | _ -> failwith "Invalid property"

Note that this will only work on a static member, and not on vals.

let requestedProperty = Props.propertyAccess (fun p -> p.Name) // returns "Name"

You can also consider Expression<Func<T, TValue>> , which works almost the same.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM