2010-08-05 30 views
6

Digamos que tenemos un simple F # cita:Generación parametrizados F # citas

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

La cita resultante es como:

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

Ahora quiero generalizar, así que en vez de escribir "Pet "y propiedad" Nombre "Podría usar un tipo arbitrario y método/propiedad definidos en él. Esto es lo que estoy tratando de hacer:

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

La expresión resultante es ahora diferente:

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

Como se puede ver, la diferencia entre la primera y la segunda expresión es que en el primer caso, el la expresión NewDelegate de nivel superior contiene PropertyGet, mientras que la segunda expresión ajusta PropertyGet en una expresión Application/Lambda. Y cuando paso esta expresión a un código externo, no espera esa estructura de expresión y falla.

Así que necesito alguna manera de construir una versión generalizada de la cita, por lo que cuando se especialice, la cita resultante es una coincidencia exacta de < @@ System.Func (diversión (x: Pet) -> x.Name) @@ >. es posible? ¿O solo hay opciones para aplicar manualmente la coincidencia de patrones a una cita generada y transformarla en lo que necesito?

ACTUALIZACIÓN. Como solución implementé el adaptador siguiente:

 
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 

Se hace el trabajo - ahora puedo convertir la expresión de 1ra a 2da forma. Pero estoy interesado en saber si esto se puede lograr de una manera simple, sin atravesar los árboles de expresión.

Respuesta

6

No creo que sea posible hacer esto; en el segundo caso, está conectando la expresión <@ (fun (x : Pet) -> x.Name) @>, que se representa utilizando un nodo Lambda, en el agujero de la otra expresión. El compilador no simplifica las expresiones durante este proceso de obstrucción, por lo que el nodo Lambda no se eliminará sin importar lo que haga.

Sin embargo su patrón coincidente solución se puede simplificar en gran medida:

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

De hecho, su versión más complicada es incorrecto. Esto se debe a que el delegateArg en su patrón más interno no coincide con el valor del identificador delegateArg previamente enlazado del patrón externo; es un nuevo identificador recién encuadernado que también se llama delegateArg. De hecho, el identificador externo delegateArg tiene el tipo Var list, mientras que el interno tiene el tipo Expr! Sin embargo, dado el rango limitado de formas de expresión generadas por el compilador, su versión defectuosa puede no ser problemática en la práctica.

EDITAR

En cuanto a sus preguntas de seguimiento, si he entendido bien puede que no sea posible lograr lo que quiere. A diferencia de C#, donde x => x + 1 podría interpretarse como que tiene un tipo de Func<int,int> o Expression<Func<int,int>>, en F # fun x -> x + 1 es siempre del tipo int->int.Si desea obtener un valor de tipo Expr<int->int>, generalmente necesita utilizar el operador de cotización (<@ @>).

Sin embargo, hay una alternativa que puede ser de utilidad. Puede usar el atributo [<ReflectedDefinition>] en las funciones permitidas para que sus presupuestos también estén disponibles. Aquí hay un ejemplo:

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

Gracias por la respuesta y una gran sugerencia. Sin embargo, todavía estoy atascado con un problema relacionado: ¿es posible conectar un simple delegado F # (por ejemplo, diversión x -> x.Name) en una cita genérica que es independiente del tipo real, como la cita # 2 anterior. Esto es similar a lo que hacen los marcos burlones: definen algunas expresiones sin conocer las interfaces concretas e inyectan tipos concretos. No parece ser capaz de lograr esto en F #. –

+0

@Vagif: no estoy seguro de entender su pregunta. ¿Podría entrar un poco más de detalle sobre lo que está tratando de hacer? – kvb

+0

Necesito enviar interoperabilidad entre F # y C#. C# espera la expresión de LINQ de cierto tipo. Puedo escribirlo en F # de manera codificada, pero quiero construir un contenedor F # genérico para esto. Este contenedor debería poder tomar como entrada lambdas como "fun x -> x.Name" y convertirlos en citas. Estoy empezando a sospechar que esto no es posible en el caso general. –

Cuestiones relacionadas