2016-11-20 32 views
5

Mam pewne dziwne efekty podczas próby uruchomienia kodu f # poprzez odbicie.
Biorąc pod uwagę następujące rodzajeDziwny błąd odbicia na wnioskowanym typie

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

i tę funkcję

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

mogę łatwo i poprawnie uruchomić następujący kod

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

Jednak muszę uruchomić tę funkcję na ostrych krawędziach moich granic systemowych i jedynym sposobem, aby to zrobić, jest powrót do użycia refleksji.
Stworzyłem więc tę funkcję, która powinna przyjąć funkcję podobną do powyższej oraz rekord danego typu i zastosować go.

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 

Jeśli teraz uruchomię to tak jak poniżej.

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

otrzymuję następujący błąd

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 

kto próbuje przekształcić coś do Box<obj, obj> i dlaczego?
Każda pomoc jest mile widziana

PS: Niektóre wyjaśnienia ;-)
a) jest to wyraźnie pytanie o użyciu odbicia w kontekście F #
b) Tak, wiem, że mój prawdziwy problem może być rozwiązany bez refleksji i już to zrobiłem. Zwiększa rozmiar mojego kodu o 40% z łatwością.
c) tak, wiem, że odbicie jest wolne od psa. Jestem gotów, aby handlować prędkością (nie potrzebuję) dla czystszego kodu.

Odpowiedz

6

Podpis funkcji convert zawiera jawny ogólny parametr 't, ale nie jest to 't, ale nie jest to 't. Łamie wnioskowanie typu dla argumentu v.

Powinno być:

let convert<'t, 'a> (f:Quotations.Expr) (v:'a) : 't 

Ale jawne parametry są trudne do wykorzystania. Wolę przechowywać informacje o konwersji typu w wyrażeniu:

let convert (f:Quotations.Expr<'a -> 't>) (v:'a) : 't 

Przykłady (http://ideone.com/peLJAR):

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

let r20 = (convert <@ iToS @>) (Box (1.0, 1.0)) 
> val r20 : Box<string,float> = Box ("1.0",1.0) 
+0

cześć, dzięki za odpowiedź - niestety nie działa :-( 'let convert (f: Quotations.Expr <'t -> 'a>) (v:' a): 't' nawet nie skompiluje, podczas gdy' let convert <'t, 'a> (f: Quotations.Expr) (v: 'a):' t' otrzymuje taki sam błąd jak poprzednio. Dodatkowo największym problemem jest to, że nie mogę ustawić typów jako parametrów ogólnych, ponieważ nie mam wtedy (przynajmniej nie w tej części programu) – robkuz

+2

Myślę, że typy są w złej kolejności w odpowiedzi. Czy mógłbyś spróbować 'let convert (f: Quotations.Expr <'a -> 't>) (v:' a): 't ='? –

+0

Dzięki! Naprawię tę literówkę. – gsomix

3

kto próbuje przekształcić coś do puszki i dlaczego?

Ponieważ używasz odbicie, swoje MethodInfo czyta parametry rodzajowe jako obj tutaj

Call (x, mi, y) -> mi 

Następnie można zrobić upcast jak poniżej

let r10 = (convert<Box<string, obj>> <@ iToS @>) (Box((upcast 1 : obj), (upcast 1 : obj)))