简体   繁体   English

生成参数化F#报价

[英]Generating parameterized F# quotations

Let's say we have a simple F# quotation: 假设我们有一个简单的F#报价:

type Pet = {  Name : string }
let exprNonGeneric = <@@ System.Func(fun (x : Pet) -> x.Name) @@>

The resulting quotation is like: 结果引用如下:

val exprNonGeneri : Expr =
  NewDelegate (System.Func`2[[FSI_0152+Pet, FSI-ASSEMBLY, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]],
             x, PropertyGet (Some (x), System.String Name, []))

Now I want to generalize it, so I instead of type "Pet" and property "Name" I could use an arbitrary type and method/property defined on it. 现在我想概括它,所以我不是键入“Pet”和属性“Name”,而是可以使用在其上定义的任意类型和方法/属性。 Here is what I am trying to do: 这是我想要做的:

let exprGeneric<'T, 'R> f = <@@ System.Func<'T, 'R>( %f ) @@>
let exprSpecialized = exprGeneric<Pet, string> <@ (fun (x : Pet) -> x.Name) @>

The resulting expression is now different: 结果表达式现在不同了:

val exprSpecialized : Expr =
  NewDelegate (System.Func`2[[FSI_0152+Pet, FSI-ASSEMBLY, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]],
             delegateArg,
             Application (Lambda (x,
                                  PropertyGet (Some (x), System.String Name, [])),
                          delegateArg))

As you can see, the difference between the first and the second expression is that in first case the top level NewDelegate expression contains PropertyGet, while the second expression wraps PropertyGet in a Application/Lambda expression. 如您所见,第一个和第二个表达式之间的区别在于,在第一种情况下,顶级NewDelegate表达式包含PropertyGet,而第二个表达式在Property / Lambda表达式中包装PropertyGet。 And when I pass this expression to an external code it does not expect such expression structure and fails. 当我将这个表达式传递给外部代码时,它不会期望这样的表达式结构并且失败。

So I need some way to build a generalized version of quotation, so when it gets specialized, the resulting quotation is an exact match of <@@ System.Func(fun (x : Pet) -> x.Name) @@>. 所以我需要一些方法来构建一个通用版本的引用,所以当它被专门化时,结果引用与<@@ System.Func(fun(x:Pet) - > x.Name)@@>完全匹配。 Is this possible? 这可能吗? Or it there only choice to manually apply pattern matching to a generated quotation and transform it to what I need? 或者它只有选择手动将模式匹配应用于生成的报价并将其转换为我需要的?

UPDATE . 更新 As a workaround I implemented the following adapter: 作为一种解决方法,我实现了以下适配器:

let convertExpr (expr : Expr) =
    match expr with
    | NewDelegate(t, darg, appl) ->
        match (darg, appl) with
        | (delegateArg, appl) ->
            match appl with 
            | Application(l, ldarg) ->
                match (l, ldarg) with
                | (Lambda(x, f), delegateArg) ->
                    Expr.NewDelegate(t, [x], f)
                | _ -> expr
            | _ -> expr
    | _ -> expr

It does the job - I can now convert expression from 1st to 2nd form. 它完成了工作 - 我现在可以将表达式从第一种形式转换为第二种形式。 But I am interested in finding out if this can be achieved in a simple way, without traversing expression trees. 但我有兴趣了解是否可以通过简单的方式实现这一点,而无需遍历表达式树。

I don't think it will be possible to do this; 我认为不可能做到这一点; in the second case, you are plugging in the expression <@ (fun (x : Pet) -> x.Name) @> , which is represented using a Lambda node, into the hole in the other expression. 在第二种情况下,您将表达式<@ (fun (x : Pet) -> x.Name) @> (使用Lambda节点表示)插入到另一个表达式的孔中。 The compiler does not simplify expressions during this plugging process, so the Lambda node won't be removed no matter what you do. 在此插入过程中,编译器不会简化表达式,因此无论您执行什么操作,都不会删除Lambda节点。

However your pattern matching workaround can be greatly simplified: 但是,您的模式匹配解决方法可以大大简化:

let convertExpr = function
| NewDelegate(t, [darg], Application(Lambda(x,f), Var(arg))) 
    when darg = arg -> Expr.NewDelegate(t, [x], f)
| expr -> expr

In fact, your more complicated version is incorrect. 事实上,您更复杂的版本是不正确的。 This is because the delegateArg in your innermost pattern is not matching against the value of the previously bound delegateArg identifier from the outer pattern; 这是因为delegateArg在最里面的图案不是针对先前绑定的值匹配delegateArg从外模式标识符; it is a new, freshly bound identifier which also happens to be called delegateArg . 它是一个新的,新绑定的标识符,恰好也称为delegateArg In fact, the outer delegateArg identifier has type Var list while the inner one has type Expr ! 实际上,外部delegateArg标识符具有类型Var list而内部类型具有类型Expr However, given the limited range of expression forms generated by the compiler your broken version may not be problematic in practice. 但是,鉴于编译器生成的表达形式范围有限,您的破解版本在实践中可能不会有问题。

EDIT 编辑

Regarding your followup questions, if I understand you correctly it may not be possible to achieve what you want. 关于你的后续问题,如果我理解正确,可能无法实现你想要的。 Unlike C#, where x => x + 1 could be interpreted as having a type of either Func<int,int> or Expression<Func<int,int>> , in F# fun x -> x + 1 is always of type int->int . 与C#不同,其中x => x + 1可以解释为具有Func<int,int>Expression<Func<int,int>> ,在F# fun x -> x + 1中始终为int->int类型int->int If you want to get a value of type Expr<int->int> then you generally need to use the quotation operator (<@ @>) . 如果要获取Expr<int->int>类型的值,则通常需要使用引号运算符(<@ @>)

There is one alternative that may be of use, however. 但是,有一种替代方案可能有用。 You can use the [<ReflectedDefinition>] attribute on let bound functions to make their quotations available as well. 您可以在let绑定函数上使用[<ReflectedDefinition>]属性来使其引用也可用。 Here's an example: 这是一个例子:

open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.ExprShape
open Microsoft.FSharp.Quotations.Patterns
open Microsoft.FSharp.Quotations.DerivedPatterns

let rec exprMap (|P|_|) = function
| P(e) -> e
| ShapeVar(v) -> Expr.Var v
| ShapeLambda(v,e) -> Expr.Lambda(v, exprMap (|P|_|) e)
| ShapeCombination(o,l) -> RebuildShapeCombination(o, l |> List.map (exprMap (|P|_|)))


let replaceDefn = function
| Call(None,MethodWithReflectedDefinition(e),args) 
    -> Some(Expr.Applications(e, [args]))
| _ -> None


(* plugs all definitions into an expression *)
let plugDefs e = exprMap replaceDefn e

[<ReflectedDefinition>]
let f x = x + 1

(* inlines f into the quotation since it uses the [<ReflectedDefinition>] attribute *)
let example = plugDefs <@ fun y z -> (f y) - (f 2) @>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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