2017-04-17 12 views
2

私の質問はこれです:F#タイププロバイダー開発:メソッドを提供するとき、変数番号とタイプのパラメーターにアクセスする方法は?

どのように私は、設計時にこれらの式の数と種類がわからないとき、私は二重引用符にリストから式をスプライスすることができますか?

下段にはタイププロバイダの完全なコードが含まれています。 (私はこの問題を示すために、ダウン概念を取り除いてきました。)私の問題は、これらの行で発生します。ラムダパラメータarg

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)... 

、私は次のエラーを取得する:

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.`` 

私ができます」プロバイダの設計時に値の数や型がわからない場合(パラメータはコンパイル時にはわかりますが)、パラメータ値を「抽出」するようなコードの記述方法を理解してください。

私は、設計時にパラメータの有無や種類を知っていたとき、私はこれを行うことができます。

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

しかし、私は内obj listExpr list入力から取得する方法を見つけ出すことはできません引用。ここで

がフルタイププロバイダコードです:最小限の例として

[<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]) 

答えて

3

、我々は種類のリストを持っているし、(タイププロバイダの文脈では、あなたがのリストをいくつか引用してリストを持っているとしましょう種類とargsは、おそらくもthisインスタンスを含む、引用のリストです):

open Microsoft.FSharp.Quotations 

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

我々は応じてformatIntまたはformatStringを呼び出す式を構築したいですその後、タイプおよびすべてのフォーマットされた文字列を連結上:

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

、それが提供引用されたコード(引用レベル)にし、二重引用符(コードを生成するために実行される通常のコードで何が起こるかを区別することが重要です-レベル)。今、あなたが構築することができます

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 "!" ] 

: - コード・レベルでは、我々はすべてのタイプを反復処理すると引数を引用し、formatIntまたはformatStringへの呼び出しでの引用のリストを生成彼らは同じ型を持っているので、それらがExpr<string>を入力することもできます

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

をそして今、あなたが引用されString.concatコールで二重引用符を構築することができます:コード・レベルでfoldを呼び出し、引用レベルでリスト::演算子を使用して、リスト式

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

あなたは、そのコードが型プロバイダクラスのコンテキストでどこに行くのか教えていただけますか?コンパイル単位 'TypeProviderPain'からのモジュール/名前空間 'StartupCode $ TypeProviderPain>。$ Library1'は実行されませんでしたが、実行しようとしたときに次のエラーが発生しました。モジュール名または型が含まれています 'formatInt @ 47''' '' scripty.fsx(7,9):エラーFS1109:アセンブリの' 。$ [email protected] '型への参照'TypeProviderPain'が見つかりましたが、タイプはアセンブリ内に見つかりませんでした。 ' –

+1

"formatInt"と "formatString"を独自のモジュールに移動することで、このランタイムエラーを解消できました。同様のエラーを避けるために、 "String.concat"を "String.Join"に置き換える必要もありました: '' error FS0193:コンパイルユニット 'FSharp.Core'のモジュール/名前空間 'Microsoft.FSharp.Collections'に名前空間、モジュールまたは型 'StringModule'''。これらの手順を実行した後、私は**型プロバイダを実行することができました。 –

+1

この種のエラーは、悲しいことに通常のタイプのプロバイダーの痛みです...うまくいけばうまくいきます! –

関連する問題