2011-09-19 16 views
13

¿Alguien sabe por qué sub arroja una excepción cuando add no? ¿Y es esto un error?Comportamiento incoherente entre (+) y (-) al usar 'en línea' y evaluación de cotización

open Microsoft.FSharp.Linq.QuotationEvaluation 

let inline add x = x + x 
let inline sub x = x - x 

let answer = <@ add 1 @>.Eval() // 2, as expected 
let answer2 = <@ sub 1 @>.Eval() // NotSupportedException 

Tenga en cuenta, sin la palabra clave en línea no se produce la excepción (pero el código no es genérico) Además, la excepción es solamente arrojados al usar citas. La evaluación normal funciona bien.

Gracias

Editar: ejemplo de código simplificado

+0

Trate de no usar el PowerPack de métodos de evaluación. Si realmente tiene que hacerlo, hay otras maneras de evaluar citas. Por ejemplo, Unquote de Stephen Swensen (http://code.google.com/p/unquote/). –

+0

@Ramon, mientras aprecio el aprobación, y estoy de acuerdo en que el evaluador de Unquote puede ser una mejor opción en muchos escenarios (evaluación no compilada más rápida, admite más patrones de cotización, admite Silverlight 4, probablemente menos errores por ser más simple), necesito apuntar que este problema es más profundo que cualquier motor de evaluación puede manejar y así se puede ver en el evaluador de Unquote también: la llamada problemática al operador 'NoDynamicInvocation'' -' está enterrada en el momento de la compilación y no se puede evitar. –

+1

Presenté un error con el proyecto PowerPack por básicamente el mismo problema hace un tiempo (aunque no entendí que '-' era el problema en ese momento): http: //fsharppowerpack.codeplex.com/workitem/5882, pero ahora creo que esto es algo que debe informarse directamente al equipo del compilador. –

Respuesta

13

Gracias por esta pregunta - que es un muy buen informe de error con un simple repro y no podía creer esto, pero tienes toda la razón. Más funciona, pero menos no.

El problema es quesub y add se compilan como los métodos genéricos y la versión de LINQ invoca estos métodos genéricos. La alineación se realiza después de que las citas se almacenen, por lo que el código entre comillas contiene una llamada al método sub. Esto no es un problema en el código F # normal, porque las funciones están en línea y los operadores están resueltos a + o - en algunos tipos numéricos.

Sin embargo, la versión genérica utiliza una búsqueda dinámica. Si nos fijamos en prim-types.fs:3530, verá:

let inline (+) (x: ^T) (y: ^U) : ^V = 
    AdditionDynamic<(^T),(^U),(^V)> x y 
    when ^T : int32  and ^U : int32  = (# "add" x y : int32 #) 
    when ^T : float  and ^U : float  = (# "add" x y : float #) 
    // ... lots of other cases 

El AdditionDynamic es lo que se llama desde el método genérico. Hace la búsqueda dinámica, que será más lenta, pero funcionará. Curiosamente, para el operador menos, la biblioteca # F no incluir la implementación dinámica:

[<NoDynamicInvocation>] 
let inline (-) (x: ^T) (y: ^U) : ^V = 
    ((^T or ^U): (static member (-) : ^T * ^U -> ^V) (x,y)) 
    when ^T : int32  and ^U : int32  = (# "sub" x y : int32 #) 
    when ^T : float  and ^U : float  = (# "sub" x y : float #) 
    // ... lots of other cases 

no tengo ni idea de por qué este es el caso - no creo que haya ninguna razón técnica, pero explica por qué obtener el comportamiento que informaste Si observa el código compilado utilizando ILSpy, verá que el método add hace algo y el método sub acaba de lanzar (por lo que aquí es de donde proviene la excepción).

En cuanto a una solución, debe escribir el código de forma que no utilice el operador genérico negativo. Probablemente, la mejor opción es evitar inline funciones (ya sea mediante el uso de sub_int o sub_float) o escribiendo su propia implementación dinámica de sub (que se puede hacer probablemente muy eficiente usando DLR (ver this post).

+0

¿Por qué '' inline sub x = x + (-x) 'no funciona? ¿La misma razón, ninguna impl dinámica para la negación unaria? – Daniel

+0

@Daniel - sí. Después de mirar alrededor (buscar 'DynamicImplTable' para obtener una lista de todas las operaciones que admiten la implementación dinámica), no creo que haya una manera de hacerlo. Puede usar '+', '*', genérico, cero genérico, 'signo' y algunos otros. No veo cómo crear restas usando esto :-) –

+2

En realidad, soy muy consciente de este problema al haber implementado un motor de evaluación de presupuestos para Unquote. Hay * muchos * operadores centrales que carecen de soporte dinámico de invocación y no parece haber consistencia en las elecciones para aquellos que los tienen y aquellos que no. Esto significa que cualquier motor de evaluación de presupuestos debe implementar resolución dinámica personalizada para los que faltan (el rendimiento es otra razón convincente para hacerlo de todos modos, especialmente para los operadores numéricos): http://code.google.com/p/unquote/source/browse/ tags/2.1.0/Unquote/DynamicOperators.fs –

Cuestiones relacionadas