es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

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

Tags:  , , ,

Answer

  1. Avatar for 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 a toFunc. Esto evitaría el uso de una expresión emplazada como %. Aquí tienes un ejemplo:

    let toQuotedFunc<'t>(filter: Expr<'t -> bool>) =
        <@ fun (t: 't) -> (%filter) t && t.Prop2 = 2 @>
    
    let toFunc<'t>(filter: Expr<'t -> bool>) =
        let quotedFunc = toQuotedFunc filter
        let xp = <@ Func<'t, bool>(%quotedFunc) @>
        LeafExpressionConverter.QuotationToExpression xp |> unbox<><func<'t, bool="">>>>
    

    La función toQuotedFunc recibe un Expr<'t -> bool> como entrada y devuelve una función incluida parcialmente aplicada que envuelve la expresión filter. La función toFunc luego llama a toQuotedFunc 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ón filter al parámetro t. Aquí tienes un ejemplo:

    let toFunc<'t>(filter: Expr<'t -> bool>) =
        let parameter = Expression.Parameter(typeof<'t>)
        let body = Expression.AndAlso(filter.Body, Expression.Equal(Expression.PropertyOrField(parameter, "Prop2"), Expression.Constant(2)))
        let lambda = Expression.Lambda<func<'t, bool="">>(body, parameter)
        (lambda.Compile(): unit -> Expression<func<'t, bool="">>])()
    

    En este caso, la función toFunc crea un árbol de expresiones que aplica la expresión filter a una expresión de parámetro, y luego la combina con la comprobación de Prop2. El árbol de expresiones resultante se compila en un delegado que devuelve un Expression<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,>

Comments are closed.