2012-01-31 19 views
6

En circunstancias normales, las funciones F # se pueden convertir a delegados llamando al new DelegateType y pasando la función como un argumento. Pero cuando el delegado contiene el parámetro byref, esto no es posible directamente. Por ejemplo el código:¿Por qué una función con byref no se puede convertir directamente en delegar?

type ActionByRef<'a> = delegate of 'a byref -> unit 

let f (x:double byref) = 
    x <- 6.0 

let x = ref 42.0 
let d = new ActionByRef<_>(f) 

no se compilará, dando el siguiente error:

This function value is being used to construct a delegate type whose signature includes a byref argument. You must use an explicit lambda expression taking 1 arguments.

Tras el error, modificar el código para utilizar

let d = new ActionByRef<_>(fun x -> f(&x)) 

obras. Pero mi pregunta es: ¿por qué es esto necesario? ¿Por qué F # no permitirá la conversión de la función nombrada a este delegado, pero la conversión de lambda está bien?

Me encontré con este comportamiento al investigar another question. Me doy cuenta de que byref es solo para compatibilidad con otros lenguajes .Net.

Respuesta

10

Creo que el problema es que byref<'T> no es un tipo real en F # - parece un tipo (para simplificar el lenguaje), pero se compila en un parámetro marcado con el out. Esto significa que byref<'T> solo se puede usar en un lugar donde el compilador realmente puede usar el indicador out.

El problema con los valores de función es que puede construir una función, p. por aplicación parcial:

let foo (n:int) (b:byref<int>) = 
    b <- n 

Cuando se pasa foo como argumento para un constructor delegado, que es un caso concreto de aplicación parcial (sin argumentos), pero la aplicación parcial en realidad basta para construir un nuevo método y luego dar que al delegado:

type IntRefAction = delegate of byref<int> -> unit 

let ac = IntRefAction(foo 5) 

el compilador podría ser inteligente y generar nuevo método con byref parámetro (o out bandera) y luego pasar que por referencia a la función real, pero en general, no habrá otra compiler- método generado cuando no usas el fun ... -> ... sintaxis. Manejar esto agregaría complejidad y creo que es un caso relativamente raro, por lo que el compilador de F # no hace eso y le pide que sea más explícito ...

+0

Interesante, no pensé en eso. Un pequeño detalle, creo que 'byref' es más como' ref', no 'out'. – svick

+0

@svick - Estás en lo cierto - en realidad, en el nivel IL, la bandera es 'ref' y la C#' out' no existe en absoluto ... –

+0

Derecha, C# 's' out' es en realidad 'ref' más Atributo 'System.Runtime.InteropServices.Out'. – ildjarn

Cuestiones relacionadas