Допустим, у нас есть простая цитата 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, []))
Теперь я хочу обобщить его, чтобы вместо типа «Pet» и свойства «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))
Как видите, разница между первым и вторым выражением заключается в том, что в первом случае выражение NewDelegate верхнего уровня содержит PropertyGet, а второе выражение оборачивает PropertyGet в выражение Application / Lambda. И когда я передаю это выражение во внешний код, оно не ожидает такой структуры выражения и завершается ошибкой.
Так что мне нужен какой-то способ построения обобщенной версии цитаты, поэтому, когда она становится специализированной, результирующая цитата точно соответствует <@@ System.Func (fun (x: Pet) -> x.Name) @ @>. Это возможно? Или есть только один вариант - вручную применить сопоставление с образцом к сгенерированной цитате и преобразовать его в то, что мне нужно?
UPDATE . В качестве обходного пути я реализовал следующий адаптер:
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-ю. Но мне интересно узнать, можно ли это сделать простым способом, не обходя деревья выражений.