Hi,
I think the code looks good as it is (and the general approach sounds fine). Using typed quotations wouldn't work, because you need to process some AST and you don't know in advance what the types are going to be. I think the problem could be the additional Coerce call in the recursive call:

1
2
 
wrap (Expr.Coerce(Expr.Call(methodInfo, args |> List.map (fun arg -> (* here -> *) Expr.Coerce(arg, arg.Type) (* <- here *) |> injectWrapper )), typeof<obj>)) 

The Coerce quotation represents an upcast or downcast (:?> or :> F# operator). The code you wrote would just add type cast of an argument to its actual current type, so that seems unnecessary. I guess it fails because you cannot use casting with primitive types like integer.

I think you could remove the Coerce and just write this:

1
2
 
wrap (Expr.Coerce(Expr.Call(methodInfo, args |> List.map injectWrapper), typeof<obj>)) 
By on 5/31/2009 11:39 AM ()

Hi Tomas,That's actually what I tried first, that gives:System.ArgumentException was unhandled
  Message="type mismatch when building 'args': invalid parameter for a method or indexer property. Expected 'System.Int32', got type 'System.Object'\r\nParameter name: receivedType"
  Source="FSharp.Core"
  ParamName="receivedType"And this exception occurs already in the injectWrapper function, not in the evalUntyped.That's why I added the Coerce. (actually I first left of the other one as well, around the Expr.Call, but that one is apparently necessary and works...). I've sometimes seen an UnboxGeneric call for upcasts, but I can't seem to find how to generate that on the quotation level.

By on 5/31/2009 12:33 PM ()

Hi,
ahh, I see - in this case it complains because when you add your wrapper call around some function call, it changes the result type. For example when "f(1)" returned "int", then when you add "wrap(f(1))" the return type is "obj", because there is also Coerce call around the added "Call".

I'm not quite sure what's the best way to convert object back to any type in quotations. I guess you could generate a call to the F# "unbox" function that takes a type parameter of the expected result type, so you'd essentially generate: "unbox<int>(wrap(f(1)))".

However, there is another option. Your "wrap" function could be an ordinary generic function having a type " 'T -> 'T ". In that case, you'd generate call to it using "Expr.Call" (instead of just composing quotations). The wrap function would simply be:

1
2
3
let wrap x = 
  printf "wrap %A" x
  x

Then, you'd need to get method info of this function (using .NET reflection API such as Assembly.GetType to find the module where "wrap" is defined and GetMethod to find the compiled function). Then you should be able to write something like:

1
2
3
4
let wrap e type = 
  Expr.Call(wrapFunctionInfo.MakeGenericMethod([| type |]), [ e ])

wrap(Expr.Call(methodInfo, args |> List.map injectWrapper )), typeof<obj>)) 
By on 5/31/2009 1:40 PM ()

Tomas,Excellent! For completeness' sake, here is the working code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let wrap x = 
  printfn "wrap %A" x
  x
  
type Marker = Marker of unit

let wrapMethodInfo t = typeof<Marker>.DeclaringType.GetMethod("wrap").MakeGenericMethod([|t|])
let wrapExpr e t = Expr.Call(wrapMethodInfo t, [ e ])

let rec injectWrapper (b:Expr) =
    match b with
    | Call (None,methodInfo,args) as call ->
        wrapExpr (Expr.Call(methodInfo, args |> List.map injectWrapper)) methodInfo.ReturnType
    | ShapeCombination (o, exprlist) -> RebuildShapeCombination(o,exprlist |> List.map (injectWrapper))

let q = <@ 2 * 3 - 1 @>
let qwrapped = injectWrapper q

printfn "%A" <| qwrapped.EvalUntyped()

Console.ReadKey() |> ignore

which prints:wrap 6wrap 55Thanks very much!

By on 5/31/2009 2:51 PM ()
IntelliFactory Offices Copyright (c) 2011-2012 IntelliFactory. All rights reserved.
Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us | Terms of Use | Privacy Policy | Cookie Policy
Built with WebSharper