2017-04-17 1 views
2

Wie kann ich die Ausdrücke aus einer Liste in ein Zitat spleißen, wenn ich die Anzahl und Arten dieser Ausdrücke zur Entwurfszeit nicht kenne?F # Typ Provider-Entwicklung: Bei der Bereitstellung einer Methode, wie auf Parameter der Variablen Nummer und Typ zugreifen?

Unten habe ich den vollständigen Code für einen Typ Provider enthalten. (. Ich das Konzept ausgezogen habe nach unten um das Problem zu demonstrieren) tritt Mein Problem bei diesen Zeilen:

let func = ProvidedMethod((*...*), InvokeCode = fun args -> 
     <@@ let stringParts = 
       args 
       |> List.mapi (fun i arg -> 
        if paramTypes.[i] = typeof&lt;int&gt; then 
         sprintf "%i" (%%arg: int)... 

Auf dem Lambda-Parameter arg, erhalte ich folgende Fehlermeldung:

error FS0446: The variable 'arg' is bound in a quotation but is used as part of a spliced expression. This is not permitted since it may escape its scope.`` 

ich kann‘ Ich finde heraus, wie man Code schreibt, so dass ich die Parameterwerte "extrahiere", wenn die Zahlen und Typen der Werte zur Zeit des Providerdesigns nicht bekannt sind (obwohl sie zur Kompilierungszeit bekannt sind).

Als ich einen Parameters Existenz und Art zur Entwurfszeit nicht bekannt ist, kann ich dies tun:

printfn "%A" (%%args.[0]: int)

Aber ich kann nicht herausfinden, wie aus dem Expr list Eingang zu einem obj list bekommen innerhalb Das Zitat.

Hier ist die vollständige Typ Provider-Code:

[<TypeProvider>] 
type SillyProviderDefinition(config: TypeProviderConfig) as self = 
    inherit TypeProviderForNamespaces() 

let sillyType = ProvidedTypeDefinition(THIS_ASSEMBLY, NAMESPACE, "SillyProvider", Some typeof<obj>) 
    do sillyType.DefineStaticParameters([ProvidedStaticParameter("argTypes", typeof<string>)], fun typeName args -> 
     let retType = ProvidedTypeDefinition(typeName, Some typeof<obj>)   

     let paramTypes = 
      (args.[0] :?> string).Split([|'|'|]) 
      |> Array.map (function 
       | "int" -> typeof<int> 
       | "string" -> typeof<string> 
       | x -> failwithf "Invalid argType %A. Only string or int permitted" x) 
     let parameters = 
      paramTypes 
      |> Array.mapi (fun i p -> ProvidedParameter(sprintf "arg%i" i, p)) 
      |> Array.toList 

     let func = ProvidedMethod("Stringify", parameters, typeof<string>, IsStaticMethod = true, InvokeCode = fun args -> 
      <@@ let stringParts = 
        args 
        |> List.mapi (fun i arg -> 
         if paramTypes.[i] = typeof<int> then 
          sprintf "%i" (%%arg: int) 
         elif paramTypes.[i] = typeof<string> then 
          (%%arg: string) 
         else 
          failwith "Unexpected arg type") 
       //printfn "%A" (%%args.[0]: int) 
       String.Join("", stringParts) @@>) 

     do retType.AddMember func 
     do sillyType.AddMember retType 
     retType) 

    do self.AddNamespace(NAMESPACE, [sillyType]) 

Antwort

3

Als minimales Beispiel, lassen Sie uns sagen, dass wir die Liste mit Typen und eine Liste mit einigen Zitaten (im Rahmen des Typs Provider haben Sie die Liste der Typen und args ist die Liste der Angebote, möglicherweise auch die this Instanz enthält):

open Microsoft.FSharp.Quotations 

let tys = [ typeof<int>; typeof<string> ] 
let args = [ Expr.Value(42); Expr.Value("test"); ] 

Wir wollen einen Ausdruck konstruieren, die formatInt oder formatString Anrufe je und von der Art verkettet dann alle formatierten Strings:

let formatInt (n:int) = string n 
let formatString (s:string) = s 

Jetzt ist es wichtig, zu unterscheiden, was in der mitgelieferten zitierte Code geschieht (quote-Ebene) und im normalen Code, der ausgeführt wird, das Zitat zu generieren (Code -Niveau). Bei Code-Ebene, iterieren wir über alle Arten und zitierte Argumente und erzeugen eine Liste von Angeboten mit Aufrufen an formatInt oder formatString - diejenigen Expr<string> eingegeben werden können, weil sie die gleiche Art haben:

let formattedArgList = 
    [ for t, e in List.zip tys args -> 
     if t = typeof<int> then <@ formatInt %%e @> 
     elif t = typeof<string> then <@ formatString %%e @> 
     else failwith "!" ] 

Jetzt können Sie bauen eine Liste Ausdruck von fold auf Code-Ebene und mit Hilfe der Liste :: Operator an der quote-Ebene Aufruf:

let listArgExpr = 
    formattedArgList 
    |> List.fold (fun state e -> <@ %e::%state @>) <@ [] @> 

und jetzt können Sie ein Angebot mit zitierte String.concat Aufruf konstruieren:

<@ String.concat "," %listArgExpr @> 
+0

Können Sie mir zeigen, wo dieser Code im Zusammenhang mit der Art Provider-Klasse geht?Ich habe versucht, es zu integrieren, und es kompiliert, aber ich habe die folgenden Fehler, wenn ich versucht, auszuführen: '' Fehler FS0193: Das Modul/Namespace '. $ Library1' von der Übersetzungseinheit 'TypeProviderPain' nicht enthält den Namespace, das Modul oder den Typ 'formatInt @ 47''' '' scripty.fsx (7,9): Fehler FS1109: Ein Verweis auf den Typ' . $ [email protected] 'im Assembly 'TypeProviderPain' wurde gefunden, aber der Typ konnte in dieser Assembly nicht gefunden werden'' –

+1

Ich konnte diesen Laufzeitfehler eliminieren, indem ich "formatInt" und "formatString" in ihr eigenes Modul verschoben habe. Ich musste auch "String.concat" durch "String.Join" ersetzen, um einen ähnlichen Fehler zu vermeiden: '' Fehler FS0193: Das Modul/Namespace 'Microsoft.FSharp.Collections' von der Kompilierungseinheit 'FSharp.Core' enthielt nicht das Namensraum, Modul oder Typ 'StringModule'''. Nachdem ich diese Schritte unternommen hatte, war ich in der Lage, den Typ-Provider zu betreiben. –

+1

Diese Art von Fehlern sind leider die üblichen Typ Provider Schmerzen ... Froh, dass Sie es zur Arbeit gebracht haben! –

Verwandte Themen