Reflection 使用代码引用和表达式树构建AST
我在构建表达式树时遇到一些问题。当使用代码引用时,我也可以做同样的事情,但到目前为止,我还没有通过表达式来做这件事 首先看一下我通过代码引用来实现这一点的方法Reflection 使用代码引用和表达式树构建AST,reflection,f#,Reflection,F#,我在构建表达式树时遇到一些问题。当使用代码引用时,我也可以做同样的事情,但到目前为止,我还没有通过表达式来做这件事 首先看一下我通过代码引用来实现这一点的方法 open Microsoft.FSharp.Quotations open Microsoft.FSharp.Quotations.Patterns open Microsoft.FSharp.Quotations.DerivedPatterns type Container<'a> = Co
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.Patterns
open Microsoft.FSharp.Quotations.DerivedPatterns
type Container<'a> = Container of 'a
type FromD<'a> = {a: Container<'a>; b: Container<'a>}
type ToD<'a> = {a: Container<'a>; b: Container<'a>}
let private eval e = QuotationEvaluator.Evaluate e
let f1 f =
let ex =
<@
fun (x:FromD<'a>) ->
{
a = f x.a;
b = f x.b
}
: ToD<'b>
@>
eval ex
现在,一些测试代码将从d
转换为ToD
,并在容器上应用转换
let transform (Container (v:'a)) : Container<'b> = Container (sprintf "%A" v)
[<Test>]
let ``test F1`` () =
let r1 = f1 transform {a = Container true; b = Container true}
let r2 = f1 transform {a = Container 1; b = Container 2}
printfn "F1: %A, F1: %A" r1 r2
还有一些测试代码
let ``test F2`` () =
let r3 = (f2<FromD<bool>, ToD<string>> transform) {a = Container true; b = Container true}
printfn "R3 %A" r3
因此,在构造表达式树时似乎存在一些问题,因为类型推断器说我的函数有一个bool
type参数,但实际参数是object
现在我可以通过这样重写函数来克服这个问题
let f2<'x, 't> f =
let xt = typeof<'x>
let tt = typeof<'t>
let ps = nameMap xt
let x = Var("x", xt)
let vx = Expr.Var(x)
let fnv = Expr.ValueWithName(f, typeof<Container<bool> -> Container<string>>, "f")
let ex =
Expr.Lambda(x,
Expr.NewRecord(tt,
[
Expr.Application(fnv, Expr.PropertyGet(vx, ps.Item "a", []))
Expr.Application(fnv, Expr.PropertyGet(vx, ps.Item "b", []))
]))
let ex2 : Expr<'x -> 't> = ex |> Expr.Cast
let ex3 = eval ex2
ex3
有人知道发生了什么吗?f2
的类型以'x->'t
结尾,因为这正是您在这一行中指定它的方式:
let ex2 : Expr<'x -> 't> = ex |> Expr.Cast
这将在第一次应用程序之前为您提供正确的类型
但要小心由于您的所有处理都是在运行时进行的,因此编译器无法保证您在所有位置的类型安全。因此,你必须自己小心防范它们。下面是代码所依赖的一些(尽管可能不是全部)编译时不可执行的东西:
'x
必须是一条记录,其字段名为a
和b
类型为'a
't
必须是一个记录,它有两个字段,分别命名为a
和b
,按特定顺序声明,并且都有类型'b
let fromDMap f (fromD: FromD<_>) : ToD<_> = { a = f fromD.a; b = f fromD.b }
// Usage:
let r3 = fromDMap transform {a = Container true; b = Container true}
let fromDMap f(fromD:fromD):ToD={a=f fromD.a;b=f fromD.b}
//用法:
设r3=fromDMap变换{a=Container true;b=Container true}
当然,如果您想要创建一个“通用”函数来映射任意类型的同名字段,那么这种方法将不起作用。但是,我敢说这样的函数有点过于通用
p.S.您的函数
transform
的声明类型比函数的实际类型更通用。声明的返回类型是ContainerIt,它的类型是'x->'t
,因为这正是您强制转换它的方式。编译器完全按照您的要求执行。至于将f
的类型固定为obj
,那是因为代码中没有任何东西可以将编译器指向f
的类型。@FyodorSoikin是的,当然-但这只是bc,否则我甚至无法编译它。因此,只要我能够用实际工作的签名(Container)->(FromD)
来表示函数,我就不受此实现的约束,因此我愿意接受其他建议。;-)不,那对我不起作用。我不能这样定义它,因为f2
实际上比您在示例中看到的要灵活得多(因此也需要通过表达式而不是引用来构造它)。我想要的是一个函数f2::g[j[a]->(j[a]->j[b])->h[j[b]
(Haskell语法),但这不能用F的签名来表达。因此,放置(容器)
不是一个选项,从你那里得到一个提示,我就解决了问题。函数签名必须是这样的let f2 transform
您不能要求类型参数本身就是具有自己类型参数的类型构造函数。这被称为“高级类型”(与“高阶函数”相比),而F#没有这些类型。
let f2<'x, 't> f =
let xt = typeof<'x>
let tt = typeof<'t>
let ps = nameMap xt
let x = Var("x", xt)
let vx = Expr.Var(x)
let fnv = Expr.ValueWithName(f, typeof<Container<bool> -> Container<string>>, "f")
let ex =
Expr.Lambda(x,
Expr.NewRecord(tt,
[
Expr.Application(fnv, Expr.PropertyGet(vx, ps.Item "a", []))
Expr.Application(fnv, Expr.PropertyGet(vx, ps.Item "b", []))
]))
let ex2 : Expr<'x -> 't> = ex |> Expr.Cast
let ex3 = eval ex2
ex3
System.ArgumentException : Argument types do not match
at System.Linq.Expressions.Expression.Constant (System.Object value, System.Type type) [0x00049] in <4a648327db854c86ab0ece073e38f4b3>:0
at FSharp.Quotations.Evaluator.QuotationEvaluationTypes.LetRecConvExpr (FSharp.Quotations.Evaluator.QuotationEvaluationTypes+ConvEnv env, Microsoft.FSharp.Core.FSharpOption`1[T] letrec, Microsoft.FSharp.Quotations.FSharpExpr inp) [0x00185] in <56703c1ea378c767a74503831e3c7056>:0
at FSharp.Quotations.Evaluator.QuotationEvaluationTypes.ConvExpr (FSharp.Quotations.Evaluator.QuotationEvaluationTypes+ConvEnv env, Microsoft.FSharp.Quotations.FSharpExpr inp) [0x00001] in <56703c1ea378c767a74503831e3c7056>:0
at FSharp.Quotations.Evaluator.QuotationEvaluationTypes.LetRecConvExpr (FSharp.Quotations.Evaluator.QuotationEvaluationTypes+ConvEnv env, Microsoft.FSharp.Core.FSharpOption`1[T] letrec, Microsoft.FSharp.Quotations.FSharpExpr inp) [0x02065] in <56703c1ea378c767a74503831e3c7056>:0
at FSharp.Quotations.Evaluator.QuotationEvaluationTypes.ConvExpr (FSharp.Quotations.Evaluator.QuotationEvaluationTypes+ConvEnv env, Microsoft.FSharp.Quotations.FSharpExpr inp) [0x00001] in <56703c1ea378c767a74503831e3c7056>:0
at FSharp.Quotations.Evaluator.QuotationEvaluationTypes+ConvExprs@703.Invoke (Microsoft.FSharp.Quotations.FSharpExpr inp) [0x00001] in <56703c1ea378c767a74503831e3c7056>:0
at Microsoft.FSharp.Primitives.Basics.List.map[T,TResult] (Microsoft.FSharp.Core.FSharpFunc`2[T,TResult] mapping, Microsoft.FSharp.Collections.FSharpList`1[T] x) [0x0003f] in <57acd2f6dff9fae1a7450383f6d2ac57>:0
at Microsoft.FSharp.Collections.ListModule.Map[T,TResult] (Microsoft.FSharp.Core.FSharpFunc`2[T,TResult] mapping, Microsoft.FSharp.Collections.FSharpList`1[T] list) [0x00001] in <57acd2f6dff9fae1a7450383f6d2ac57>:0
at FSharp.Quotations.Evaluator.QuotationEvaluationTypes.ConvExprs (FSharp.Quotations.Evaluator.QuotationEvaluationTypes+ConvEnv env, Microsoft.FSharp.Collections.FSharpList`1[T] es) [0x00007] in <56703c1ea378c767a74503831e3c7056>:0
at FSharp.Quotations.Evaluator.QuotationEvaluationTypes.LetRecConvExpr (FSharp.Quotations.Evaluator.QuotationEvaluationTypes+ConvEnv env, Microsoft.FSharp.Core.FSharpOption`1[T] letrec, Microsoft.FSharp.Quotations.FSharpExpr inp) [0x020e6] in <56703c1ea378c767a74503831e3c7056>:0
at FSharp.Quotations.Evaluator.QuotationEvaluationTypes.ConvExpr (FSharp.Quotations.Evaluator.QuotationEvaluationTypes+ConvEnv env, Microsoft.FSharp.Quotations.FSharpExpr inp) [0x00001] in <56703c1ea378c767a74503831e3c7056>:0
at FSharp.Quotations.Evaluator.QuotationEvaluationTypes.LetRecConvExpr (FSharp.Quotations.Evaluator.QuotationEvaluationTypes+ConvEnv env, Microsoft.FSharp.Core.FSharpOption`1[T] letrec, Microsoft.FSharp.Quotations.FSharpExpr inp) [0x027f0] in <56703c1ea378c767a74503831e3c7056>:0
at FSharp.Quotations.Evaluator.QuotationEvaluationTypes.ConvExpr (FSharp.Quotations.Evaluator.QuotationEvaluationTypes+ConvEnv env, Microsoft.FSharp.Quotations.FSharpExpr inp) [0x00001] in <56703c1ea378c767a74503831e3c7056>:0
at FSharp.Quotations.Evaluator.QuotationEvaluationTypes.Conv[a] (a e, System.Boolean eraseEquality) [0x0001d] in <56703c1ea378c767a74503831e3c7056>:0
at FSharp.Quotations.Evaluator.QuotationEvaluationTypes.CompileImpl[a] (a e, System.Boolean eraseEquality) [0x00001] in <56703c1ea378c767a74503831e3c7056>:0
at FSharp.Quotations.Evaluator.QuotationEvaluationTypes.Compile[a] (a e) [0x00001] in <56703c1ea378c767a74503831e3c7056>:0
at FSharp.Quotations.Evaluator.QuotationEvaluator.Evaluate[T] (Microsoft.FSharp.Quotations.FSharpExpr`1[T] e) [0x00001] in <56703c1ea378c767a74503831e3c7056>:0
at tst.f2[x,t] (Microsoft.FSharp.Core.FSharpFunc`2[T,TResult] f) [0x000f5] in <5823081418eafa12a745038314082358>:0
at tst.test F2 () [0x00005] in <5823081418eafa12a745038314082358>:0
at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (System.Reflection.MonoMethod,object,object[],System.Exception&)
at System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00038] in <8cd55ece525b4760b63de40980e005aa>:0
let ex2 : Expr<'x -> 't> = ex |> Expr.Cast
let f2<'x, 't, 'a, 'b> (f: Container<'a> -> Container<'b>) =
...
let fromDMap f (fromD: FromD<_>) : ToD<_> = { a = f fromD.a; b = f fromD.b }
// Usage:
let r3 = fromDMap transform {a = Container true; b = Container true}