首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用代码引号和表达式树构建AST

用代码引号和表达式树构建AST
EN

Stack Overflow用户
提问于 2016-11-09 11:32:51
回答 1查看 379关注 0票数 3

我在构建表达式树时遇到了一些问题。我可以在使用代码引号时做同样的事情,但是到目前为止,我没有运气通过表达式来完成它。

首先,看看我的方法,通过代码引号来实现它。

代码语言:javascript
复制
    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

上面的签名是(Container<'a> -> Container<'b>) -> (FromD<'a> -> ToD<'b>)。这正是我想要的。f1生成的表达式树是

代码语言:javascript
复制
Lambda (x,
        NewRecord (ToD`1,
                Application (ValueWithName (<fun:r1@60>, f),
                                PropertyGet (Some (x), a, [])),
                Application (ValueWithName (<fun:r1@60>, f),
                                PropertyGet (Some (x), b, []))))

现在,一些测试代码将FromD转换为ToD,并在Container上应用转换。

代码语言:javascript
复制
    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

一切都和我想要的完全一样,r1r2产生了预期的结果。

现在,我想使用表达式而不是代码引号重新创建f1

这是我的第一次尝试(带有一些帮助函数)

代码语言:javascript
复制
//fields :: Type -> PropertyInfo []
let fields t = FSharpType.GetRecordFields t

//nameMap :: Type -> Map<string,PropertyInfo>
let nameMap t =
    t
    |> fields
    |> Array.map (fun x -> x.Name, x)
    |> Map.ofArray

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, "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

和一些测试代码

代码语言:javascript
复制
let ``test F2`` () =
    let r3 = (f2<FromD<bool>, ToD<string>> transform) {a = Container true; b = Container true}
    printfn "R3 %A" r3 

首先,在本例中,f2的签名是

(Container<obj> -> Container<string>) -> ('x -> 't)

而不是

(Container<'a> -> Container<'b>) -> (FromD<'a> -> ToD<'b>)

所以不知怎么的,这个类型的下流者有点渴望这个。

这将导致下面的错误消息

代码语言:javascript
复制
System.ArgumentException : Type mismatch when building 'f': function argument type doesn't match. Expected 'tst+Container`1[System.Boolean]', but received type 'tst+Container`1[System.Object]'.
Parameter name: receivedType
at Microsoft.FSharp.Quotations.PatternsModule.checkTypesSR[a] (System.Type expectedType, System.Type receivedType, a name, System.String threeHoleSR) [0x00019] in <57acd2f6dff9fae1a7450383f6d2ac57>:0
at Microsoft.FSharp.Quotations.PatternsModule.checkAppliedLambda (Microsoft.FSharp.Quotations.FSharpExpr f, Microsoft.FSharp.Quotations.FSharpExpr v) [0x00084] in <57acd2f6dff9fae1a7450383f6d2ac57>:0
at Microsoft.FSharp.Quotations.PatternsModule.mkApplication (Microsoft.FSharp.Quotations.FSharpExpr v_0, Microsoft.FSharp.Quotations.FSharpExpr v_1) [0x00001] in <57acd2f6dff9fae1a7450383f6d2ac57>:0
at Microsoft.FSharp.Quotations.FSharpExpr.Application (Microsoft.FSharp.Quotations.FSharpExpr functionExpr, Microsoft.FSharp.Quotations.FSharpExpr argument) [0x00001] in <57acd2f6dff9fae1a7450383f6d2ac57>:0
at tst.f2[x,t] (Microsoft.FSharp.Core.FSharpFunc`2[T,TResult] f) [0x0005f] in <582303e818eafa12a7450383e8032358>:0
at tst.test F2 () [0x00005] in <582303e818eafa12a7450383e8032358>: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

因此,在构造表达式树时似乎存在一些问题,因为类型推断者说,我的函数有一个bool类型param,但是实际的param是object

现在,我可以通过这样重写函数来克服这个问题。

代码语言:javascript
复制
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

在这种情况下,我强迫ValueWithName是特定类型的,而不是f.GetType()

我为这个例子创建了一个非常特定的类型(typeof<Container<bool> -> Container<string>>),也是为了使这个示例更容易理解。

这将帮助我通过建设阶段,也是与演员一起工作。

此外,所构造的表达式树与以前相同。

但是,现在它使用以下错误消息在评估期间崩溃

代码语言:javascript
复制
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

有人知道发生了什么事吗?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-11-09 15:41:48

f2的类型以'x -> 't结尾,因为这正是您在这行中指定它的方式:

代码语言:javascript
复制
let ex2 : Expr<'x -> 't> = ex |> Expr.Cast

f2甚至不知道FromDToD这类东西的存在,所以它不可能把它们作为它的类型。

但是,如果您在测试中查看r3的第一部分的类型,您将看到它是FromD<_> -> ToD<_>,因为它们被指定为f2的类型参数,以分别代表'x't

至于Container<obj>,它实际上比你想象的还要糟糕。如果孤立地查看f2,您将看到它的类型是obj -> 'x -> 't。这是因为f2的主体中没有任何东西可以建议f的类型。因此,它被强迫只有obj作为最终的超级类型的所有。

当您将f2与参数transform一起用于参数f时,编译器将f的类型修正为Container<_> -> Container<string> (因为这是transform的类型),后者后来变成了Container<obj> -> Container<string>,因为程序中没有任何东西可以进一步约束该类型。

从上面看,修复是不言自明的:只需显式声明f的类型。

代码语言:javascript
复制
let f2<'x, 't, 'a, 'b> (f: Container<'a> -> Container<'b>) = 
    ...

这将为您提供正确的类型,甚至在第一个应用程序之前。

,但要小心!

由于您的所有处理都在运行时进行,所以编译器无法保证您在所有地方键入安全。因此,你必须小心,防止他们自己。下面是您的代码所依赖的一些(虽然可能不是全部)不是编译时可执行的东西:

  1. 类型'x必须是一个记录,其字段名为ab,类型为'a
  2. 类型't必须是一个记录,其中有两个字段,分别名为ab,它们都按照特定的顺序声明,并且都具有'b类型。

这样的设计在我看来有点动摇。也许,如果您描述了您最初的问题(最好是一个单独的问题),可能会有人提出一个更优雅的解决方案。

如果你只想“在记录上映射”,我可能会考虑一个不那么雄心勃勃的解决方案,例如:

代码语言:javascript
复制
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}

当然,如果您想要创建一个“泛型”函数来映射任意类型的命名器字段,则此方法将无法工作。但是,我敢说,这样的函数有点过于通用了。

P.S.您的函数transform有一个声明的类型,它比实际的函数更通用。声明的返回类型是Container<'b>,但它实际上返回的是Container<string>。因此,'b被限制为string

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/40506008

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档