I have some problems building a expression tree. I can do the same thing when using code quotations but I had no luck sofar doing it via expressions.
First have a look at my approach doing it via code quotations
    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
The signature of the above is (Container<'a> -> Container<'b>) -> (FromD<'a> -> ToD<'b>). Exactly what I wanted. The Expression tree generated by f1 is
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, []))))
Now some test code that transforms FromD into a ToD and applies a transformation on the Container as well
    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
Everything is exactly like I wanted and r1 and r2 yield the expected results.
Now I want to recreate f1 using expressions instead of code quotations.
This is my first try (with some helper functions)
//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
and some test code
let ``test F2`` () =
    let r3 = (f2<FromD<bool>, ToD<string>> transform) {a = Container true; b = Container true}
    printfn "R3 %A" r3 
Now the first thing is that in this case the signature of f2 is
(Container<obj> -> Container<string>) -> ('x -> 't)
instead of
(Container<'a> -> Container<'b>) -> (FromD<'a> -> ToD<'b>)
So somehow the type infererrer is a bit to eager on this one.
This the leads then to the following error message
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
So it seems that there is some issue while constructing the expression tree as the type inferer says my function has a bool type param but the actual param is object.
Now I can overcome this by rewriting the function like this
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
In this case I force the ValueWithName to be of a specific type instead of f.GetType().
I have created for this example a very specific type (typeof<Container<bool> -> Container<string>>) also to make the example easier to understand.
This will help me to get past the construction phase and also is working with the cast.
Also the expression tree that got constructed is the same as before.
However now it crashes during evaluation with the following error message
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
Does anybody have an idea whats going on?
 
Aucun commentaire:
Enregistrer un commentaire