2010-08-05 2 views
6

의 우리가 간단한 F 번호 인용 있다고 가정 해 봅시다 :생성 매개 변수 F 번호 견적

 
type Pet = { Name : string } 
let exprNonGeneric = <@@ System.Func(fun (x : Pet) -> x.Name) @@> 

결과 인용은 같은 것입니다 :

 
val exprNonGeneri : Expr = 
    NewDelegate (System.Func`2[[FSI_0152+Pet, FSI-ASSEMBLY, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], 
      x, PropertyGet (Some (x), System.String Name, [])) 

지금 나는 그것을 일반화 할를, 그래서 대신 유형의 I "애완 동물 "속성 및"Name "속성에 정의 된 임의의 형식 및 메서드/속성을 사용할 수 있습니다. 여기에 내가 할 노력하고 무엇 :

 
let exprGeneric<'T, 'R> f = <@@ System.Func<'T, 'R>(%f) @@> 
let exprSpecialized = exprGeneric<Pet, string> <@ (fun (x : Pet) -> x.Name) @> 

결과 표현은 이제 다른 : 당신이 볼 수 있듯이

 
val exprSpecialized : Expr = 
    NewDelegate (System.Func`2[[FSI_0152+Pet, FSI-ASSEMBLY, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], 
      delegateArg, 
      Application (Lambda (x, 
            PropertyGet (Some (x), System.String Name, [])), 
          delegateArg)) 

, 제 1 및 제 2 표현의 차이는 첫 번째 경우에 최상위 NewDelegate 표현식은 PropertyGet을 포함하고, 두 번째 표현식은 PropertyGet을 Application/Lambda 표현식으로 둘러 쌉니다. 그리고이 표현식을 외부 코드에 전달할 때 그러한 표현 구조를 기대하지 않고 실패합니다.

일반화 된 버전의 견적을 작성하려면 어떤 방법이 필요하므로 특수화되면 결과 인용 부호는 < @@ System.Func (fun (x : Pet) -> x.Name)과 정확하게 일치합니다. @@ >. 이것이 가능한가? 또는 생성 된 견적에 패턴 매칭을 수동으로 적용하고 필요한대로 변환 할 수있는 유일한 선택입니까?

업데이트. 해결 방법으로 다음 어댑터를 구현했습니다.

 
let convertExpr (expr : Expr) = 
    match expr with 
    | NewDelegate(t, darg, appl) -> 
     match (darg, appl) with 
     | (delegateArg, appl) -> 
      match appl with 
      | Application(l, ldarg) -> 
       match (l, ldarg) with 
       | (Lambda(x, f), delegateArg) -> 
        Expr.NewDelegate(t, [x], f) 
       | _ -> expr 
      | _ -> expr 
    | _ -> expr 

이제는 표현식을 1에서 2로 변환 할 수 있습니다. 그러나 표현 트리를 가로 지르지 않고 간단한 방법으로 달성 할 수 있는지 알아 보는 데 관심이 있습니다.

답변

6

이렇게 할 수 없을 것이라고 생각합니다. 두 번째 경우에는 Lambda 노드를 사용하여 표현 된 표현식 <@ (fun (x : Pet) -> x.Name) @>을 다른 표현식의 구멍에 연결합니다. 컴파일러는이 연결 프로세스 중에 표현식을 단순화하지 않으므로 Lambda 노드는 사용자가 수행 한 작업에 관계없이 제거되지 않습니다. 귀하의 패턴 매칭 해결 방법이 크게 간소화 할 수있다 그러나

:

let convertExpr = function 
| NewDelegate(t, [darg], Application(Lambda(x,f), Var(arg))) 
    when darg = arg -> Expr.NewDelegate(t, [x], f) 
| expr -> expr 

는 사실, 당신의 더 복잡한 버전이 올바르지 않습니다. 이는 가장 안쪽 패턴의 delegateArg이 외부 패턴의 이전에 바인딩 된 delegateArg 식별자와 일치하지 않기 때문입니다. 그것은 새롭고 갓 태어난 식별자로 delegateArg이라고도합니다. 사실, 바깥 쪽 delegateArg 식별자의 유형은 Var list이고 내부 식별자의 유형은 Expr입니다! 그러나, 컴파일러에 의해 생성 된 표현 양식의 제한된 범위를 감안할 때 깨진 버전은 실제로 문제가되지 않을 수도 있습니다. 당신의 후속 질문에 대해서는

편집

, 내가 제대로 이해한다면 당신이 원하는 것을 달성하지 못할 수 있습니다. x => x + 1Func<int,int> 또는 Expression<Func<int,int>> 유형으로 해석 될 수있는 C#과 달리 F # fun x -> x + 1은 항상 int->int입니다.Expr<int->int> 유형의 값을 가져 오려면 일반적으로 (<@ @>)이라는 인용 연산자를 사용해야합니다.

그러나 사용할 수있는 대안이 하나 있습니다. 레터 바운드 기능에 [<ReflectedDefinition>] 속성을 사용하여 따옴표를 사용할 수도 있습니다. 다음은 그 예입니다.

open Microsoft.FSharp.Quotations 
open Microsoft.FSharp.Quotations.ExprShape 
open Microsoft.FSharp.Quotations.Patterns 
open Microsoft.FSharp.Quotations.DerivedPatterns 

let rec exprMap (|P|_|) = function 
| P(e) -> e 
| ShapeVar(v) -> Expr.Var v 
| ShapeLambda(v,e) -> Expr.Lambda(v, exprMap (|P|_|) e) 
| ShapeCombination(o,l) -> RebuildShapeCombination(o, l |> List.map (exprMap (|P|_|))) 


let replaceDefn = function 
| Call(None,MethodWithReflectedDefinition(e),args) 
    -> Some(Expr.Applications(e, [args])) 
| _ -> None 


(* plugs all definitions into an expression *) 
let plugDefs e = exprMap replaceDefn e 

[<ReflectedDefinition>] 
let f x = x + 1 

(* inlines f into the quotation since it uses the [<ReflectedDefinition>] attribute *) 
let example = plugDefs <@ fun y z -> (f y) - (f 2) @> 
+0

답변 해 주신 점 감사드립니다. 그러나 여전히 관련 문제가 있습니다. 간단한 F # 대리인 (예 : fun x -> x.Name)을 위에 인용 된 # 2와 같은 실제 유형과 무관 한 일반 인용에 연결할 수 있습니까? 이는 조롱 프레임 워크가하는 것과 유사합니다. 즉, 구체적인 인터페이스를 모르고 구체적인 유형을 주입하지 않고 일부 표현식을 정의합니다. 나는 F #에서 이것을 달성 할 수 없을 것 같습니다. –

+0

@Vagif - 귀하의 질문을 이해할 수 있는지 잘 모르겠습니다. 당신이하려는 일에 대해 좀 더 자세히 설명해 주시겠습니까? – kvb

+0

F #과 C# 사이에 interop을 보내야합니다. C#은 특정 종류의 LINQ 표현식을 기대합니다. F #으로 하드 코딩 된 방식으로 작성할 수 있지만 이에 대한 일반 F # 래퍼를 작성하려고합니다. 이 래퍼는 "fun x -> x.Name"과 같은 입력 람다를 가져 와서 인용으로 변환 할 수 있어야합니다. 나는 이것이 일반적인 경우에는 불가능하다고 생각하기 시작했다. –