Componiendo citas de código F# de manera programática.
El siguiente código se extrae de una aplicación y se adapta para resaltar el problema de la manera más sencilla posible:
module Mo
open System
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Linq.RuntimeHelpers
open System.Linq.Expressions
type Type() =
member _.Prop1 with get() = 1
member _.Prop2 with get() = 2
let toFunc<'t when 't :> Type>(filter: 't -> Expr<bool>) =
let xp = <@ Func<'t, bool>(fun (t: 't) -> %(filter t) && t.Prop2 = 2) @>
LeafExpressionConverter.QuotationToExpression xp |> unbox<><func<'t, bool="">>>
let getFunc (i: int) =
let filter (t: Type) = <@ t.Prop1 = i @>
toFunc filter
El problema está en la línea:
let xp = <@ Func< 't, bool>(fun (t: 't) -> %(filter t) && t.Prop2 = 2) @>
El compilador se queja de fun (t: 't)
de la siguiente manera:
Error FS0446
La variable 't' está ligada en una cita, pero se utiliza como parte de una expresión insertada.
Esto no está permitido ya que puede escapar de su alcance.
La intención es componer citas en una expresión Linq de filtro. ¿Existe alguna forma (alternativa) de hacer esto?
EDICIÓN:
Es necesario un poco más de contexto:
La expresión Func devuelta se pasa posteriormente a Azure.Data.Tables.TableClient.Query (). Desafortunadamente, este método no admite expresiones que contengan llamadas a funciones.
Convertir filter
en una función citada (como sugirió Fyodor) fue mi primera versión, pero tuve que abandonarla debido a este requisito del SDK de Azure Tables.
Entonces la pregunta es:
¿Es posible lograr el resultado de una expresión que no contenga llamadas a métodos/funciones externos?</func<‘t,>
davy.ai
El problema en el código es que la variable
t
está vinculada en una cita pero se utiliza como parte de una expresión emplazada. Esto no está permitido ya que podría escapar de su alcance.Una alternativa podría ser convertir la función
filter
en una función incluida parcialmente aplicada antes de pasarla atoFunc
. Esto evitaría el uso de una expresión emplazada como%
. Aquí tienes un ejemplo:La función
toQuotedFunc
recibe unExpr<'t -> bool>
como entrada y devuelve una función incluida parcialmente aplicada que envuelve la expresiónfilter
. La funcióntoFunc
luego llama atoQuotedFunc
para obtener la función incluida, y la utiliza para construir la expresión final.Otra solución podría ser usar
Expression.Invoke
para aplicar la expresiónfilter
al parámetrot
. Aquí tienes un ejemplo:En este caso, la función
toFunc
crea un árbol de expresiones que aplica la expresiónfilter
a una expresión de parámetro, y luego la combina con la comprobación deProp2
. El árbol de expresiones resultante se compila en un delegado que devuelve unExpression<func<'t, bool="">>
.Ambas alternativas proporcionan expresiones que no contienen llamadas a métodos o funciones externas, lo que debería cumplir con el requisito del SDK de Azure Tables.</func<‘t,></func<‘t,></func<‘t,></func<‘t,>