2010-08-05 41 views
6

Hãy nói rằng chúng tôi có một đơn giản F # ngoặc kép:Tạo tham số F # trích dẫn

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

Đoạn trích dẫn kết quả là như sau:

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

Bây giờ tôi muốn khái quát nó, vì vậy tôi thay vì gõ "Pet "và thuộc tính" Name "Tôi có thể sử dụng một kiểu tùy ý và phương thức/thuộc tính được định nghĩa trên nó. Dưới đây là những gì tôi đang cố gắng để làm:

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

Khái niệm kết quả bây giờ là khác nhau:

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

Như bạn có thể thấy, phần chênh lệch giữa đầu tiên và biểu thức thứ hai là trong trường hợp đầu tiên biểu thức NewDelegate cấp cao nhất có chứa PropertyGet, trong khi biểu thức thứ hai bao bọc PropertyGet trong biểu thức Application/Lambda. Và khi tôi chuyển biểu thức này sang mã ngoài nó không mong đợi cấu trúc biểu thức như vậy và thất bại.

Vì vậy, tôi cần một số cách để xây dựng một phiên bản tổng quát của báo giá, vì vậy khi nó được chuyên biệt, báo giá kết quả là một kết hợp chính xác của < @@ System.Func (vui vẻ (x: Pet) -> x.Name) @@ >. Điều này có thể không? Hoặc nó chỉ có sự lựa chọn để áp dụng thủ công mô hình phù hợp với một báo giá được tạo ra và biến nó thành những gì tôi cần?

CẬP NHẬT. Để khắc phục sự cố, tôi đã triển khai bộ điều hợp sau:

 
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 

Nó hiện công việc - Giờ đây tôi có thể chuyển đổi biểu thức từ thứ 1 sang dạng thứ 2. Nhưng tôi quan tâm đến việc tìm hiểu xem điều này có thể đạt được một cách đơn giản, mà không vượt qua các cây biểu hiện.

Trả lời

6

Tôi không nghĩ rằng nó sẽ có thể làm điều này; trong trường hợp thứ hai, bạn đang cắm vào biểu thức <@ (fun (x : Pet) -> x.Name) @>, được biểu diễn bằng cách sử dụng nút Lambda, vào lỗ trong biểu thức khác. Trình biên dịch không đơn giản hóa các biểu thức trong quá trình cắm này, do đó, nút Lambda sẽ không bị xóa bất kể bạn làm gì.

Tuy nhiên mô hình phù hợp với cách giải quyết của bạn có thể được đơn giản hóa rất nhiều:

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

Trong thực tế, phiên bản phức tạp hơn của bạn là không chính xác. Điều này là do delegateArg trong mẫu bên trong nhất của bạn không khớp với giá trị của số nhận dạng delegateArg trước đó bị ràng buộc từ mẫu bên ngoài; nó là một định danh mới, ràng buộc mới cũng được gọi là delegateArg. Thực tế, số nhận dạng bên ngoài delegateArg có loại Var list trong khi mã nhận dạng bên trong có loại Expr! Tuy nhiên, do phạm vi giới hạn các biểu mẫu biểu hiện được tạo ra bởi trình biên dịch, phiên bản bị hỏng của bạn có thể không có vấn đề trong thực tế.

EDIT

Về câu hỏi followup của bạn, nếu tôi hiểu bạn một cách chính xác nó có thể không thể đạt được những gì bạn muốn. Không giống như C#, trong đó x => x + 1 có thể được hiểu là có loại Func<int,int> hoặc Expression<Func<int,int>>, trong F # fun x -> x + 1 luôn là loại int->int.Nếu bạn muốn nhận giá trị loại Expr<int->int> thì bạn thường cần sử dụng toán tử báo giá (<@ @>).

Tuy nhiên, có một cách thay thế có thể được sử dụng. Bạn có thể sử dụng thuộc tính [<ReflectedDefinition>] trên các chức năng bị ràng buộc để có sẵn các trích dẫn của chúng. Dưới đây là ví dụ:

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

Cảm ơn câu trả lời và đề xuất tuyệt vời. Tuy nhiên, tôi vẫn bị mắc kẹt với vấn đề liên quan: bạn có thể cắm một đại biểu F # đơn giản (ví dụ: vui vẻ x -> x.Name) vào một báo giá chung không phụ thuộc vào loại thực tế, như báo giá số 2 ở trên. Điều này tương tự như những gì mocking frameworks làm: họ định nghĩa một số biểu thức mà không biết giao diện cụ thể và tiêm các loại cụ thể. Tôi dường như không thể đạt được điều này trong F #. –

+0

@Vagif - Tôi không chắc tôi hiểu câu hỏi của bạn. Bạn có thể đi vào chi tiết hơn một chút về những gì bạn đang cố gắng làm không? – kvb

+0

Tôi cần gửi interop giữa F # và C#. C# mong đợi biểu thức LINQ của một loại nhất định. Tôi có thể viết nó trong F # theo cách mã hóa cứng, nhưng tôi muốn xây dựng một trình bao bọc F # chung cho việc này. Trình bao bọc này sẽ có thể lấy làm lambdas đầu vào như "vui vẻ x -> x.Name" và chuyển đổi chúng thành các trích dẫn. Tôi bắt đầu nghi ngờ điều này là không thể trong trường hợp chung. –

Các vấn đề liên quan