2017-04-17 3 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 입력에서 얻을 방법을 알아낼 수 없습니다 : 내가 디자인 타임에 매개 변수의 존재와 유형을 알고 수행 할 때

,이 작업을 수행 할 수 있습니다 견적.

여기 전체 Provider 형 코드입니다 : 최소한의 예를 들어

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

,의 우리가 유형 목록 Provider 형의 맥락에서 일부 인용과리스트를 (있다고 가정 해 봅시다, 당신의 목록을 가지고

open Microsoft.FSharp.Quotations 

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

우리는 formatInt 또는 formatString가 따라 호출하는 표현식을 구성 할 : 유형과 args 가능성도 this 인스턴스를 포함하는 인용의 목록)입니다 유형과 모든 포맷 된 문자열을 연결합니다 : 이제

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

를, 그것은 (제공된 인용 부호 (따옴표 수준)과 인용을 생성하기 위해 실행되는 일반 코드에서 코드를 발생하는 구별하는 것이 중요합니다 -수평). 코드 수준에서, 우리는 모든 종류의 반복 및 인수를 인용 formatInt 또는 formatString에 전화로 견적의 목록을 생성 - 그들은 동일한 유형이 있기 때문에 사람들이 Expr<string>를 입력 할 수 있습니다 :

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

지금 빌드 할 수 있습니다

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

을 그리고 지금 당신이 인용 String.concat 전화와 견적을 구성 할 수 있습니다 : 코드 수준에서 fold를 호출하고 견적 수준에서 목록 :: 연산자를 사용하여 목록을 표현

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

코드가 유형 공급자 클래스의 컨텍스트에서 어디에 표시 될 수 있습니까?컴파일을 시도했지만 컴파일하려고했지만 다음과 같은 오류가 발생했습니다 : ''오류 FS0193 : 모듈/네임 스페이스 'StartupCode $ TypeProviderPain>. $ Library1'컴파일 유닛 'TypeProviderPain'에서 가져 오지 않았습니다. 모듈 이름 또는 형식을 포함합니다. 'formatInt @ 47''' ''scripty.fsx (7,9) : 오류 FS1109 : 어셈블리의'. $ [email protected] '형식에 대한 참조 'TypeProviderPain'이 발견되었지만 형식이 해당 어셈블리에서 발견되지 않았습니다. ' –

+1

"formatInt"및 "formatString"을 자체 모듈로 이동하여 런타임 오류를 없앨 수있었습니다. 유사한 오류를 피하기 위해 "String.concat"을 "String.Join"으로 대체해야했습니다 :''error FS0193 : 컴파일 유닛 'FSharp.Core'의 모듈/네임 스페이스 'Microsoft.FSharp.Collections'에 namespace, module 또는 'StringModule'''을 입력하십시오. 이러한 단계를 수행 한 후에는 ** 유형 공급자를 실행할 수있었습니다 **. –

+1

그런 종류의 오류는 슬프게도 평범한 유형의 공급자 통증입니다 ... 다행히 생각해보십시오! –