dimanche 20 novembre 2016

Strange reflection error on inferred type

I have some strange effects when trying to run f# code via reflection.
Given the following type

type Box<'a, 'b> = Box of 'a * 'b

and this function

//iToS :: Box<'a,'b> -> Box<string,'b>
let iToS (Box (i, v)) = Box ((sprintf "%A" i), v)

I can easily and correctly run the following code

let r01 = iToS (Box (1, 1))

However I need to run this function at the sharp edges of my system boundaries and the only way to do so is to fall back on reflection usage.
So I created this function that should take a function like the above and a record of the given type and apply it.

let convert<'t> (f:Quotations.Expr) (v:'a) : 't =
    let methi e =
        let rec methi' e =
            match e with
                | Call (x, mi, y) -> mi
                | Lambda (_, body) -> methi' body
                | _ -> failwith <| sprintf "not a function %A" e
        methi' e

    let apply f v =
        let m = methi f
        m.Invoke(null, [|box v|])

    apply f v :?> 't

If I now run this however like below.

let r10 = (convert<Box<string, int>> <@ iToS @>) (Box (1, 1))

I get the following error

System.ArgumentException : Object of type 'Box`2[System.Int32,System.Int32]' cannot be converted to type 'Box`2[System.Object,System.Object]'.
at System.RuntimeType.CheckValue (System.Object value, System.Reflection.Binder binder, System.Globalization.CultureInfo culture, System.Reflection.BindingFlags invokeAttr) [0x0007d] in <8cd55ece525b4760b63de40980e005aa>:0
at System.Reflection.MonoMethod.ConvertValues (System.Reflection.Binder binder, System.Object[] args, System.Reflection.ParameterInfo[] pinfo, System.Globalization.CultureInfo culture, System.Reflection.BindingFlags invokeAttr) [0x0007f] in <8cd55ece525b4760b63de40980e005aa>:0
at System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00014] in <8cd55ece525b4760b63de40980e005aa>:0
at System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) [0x00000] in <8cd55ece525b4760b63de40980e005aa>:0
at convert[t] (Microsoft.FSharp.Quotations.FSharpExpr f, Box`2[a,b] v) [0x00006] in <5831a15618eafa12a745038356a13158>:0
at test convert () [0x000e6] in <5831a15618eafa12a745038356a13158>: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

Who is trying to convert something into a `Box and why? Any help is appreciated

PS: Some clarafications
a) this is explicitly a question about using reflection within the contaxt of F#
b) yeah, I know my real problem could be solved without reflection and I have done so already. It increases my code size by 40% easily.
c) yeah, I know reflection is dog slow. I am willing to trade speed (i dont need) for cleaner code.





Aucun commentaire:

Enregistrer un commentaire