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