2012-01-31 11 views
6

In circostanze normali, le funzioni F # possono essere convertite in delegati chiamando lo new DelegateType e passando la funzione come argomento. Ma quando il delegato contiene il parametro byref, questo non è possibile direttamente. Per esempio il codice:Perché una funzione con byref non può essere convertita direttamente in delegato?

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) 

non verrà compilato, dando il seguente errore:

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.

Dopo l'errore, modificare il codice per utilizzare

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

opere. Ma la mia domanda è: perché è necessario? Perché F # non consente la conversione dalla funzione denominata a questo delegato, ma la conversione da lambda va bene?

Mi sono imbattuto in questo comportamento durante la ricerca di another question. Mi rendo conto che lo byref è inteso solo per compatibilità con altri linguaggi .Net.

risposta

10

Penso che il problema è che byref<'T> non è un tipo effettivo in F # - sembra un tipo (per semplificare il linguaggio), ma viene compilato su un parametro contrassegnato con il flag out. Ciò significa che byref<'T> può essere utilizzato solo in un luogo in cui il compilatore può effettivamente utilizzare il flag out.

Il problema con i valori di funzione è che è possibile costruire la funzione, ad es. dall'applicazione parziale:

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

Quando si passa foo come argomento di un costruttore delegato, è un caso specifico di applicazione parziale (senza argomenti), ma l'applicazione parziale effettivamente deve costruire un nuovo metodo e poi invia che al delegato:

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

let ac = IntRefAction(foo 5) 

il compilatore potrebbe essere intelligente e generare nuovo metodo con byref parametro (o out bandiera) e quindi passare tale con riferimento alla funzione effettiva, ma in generale, non vi sarà altra compiler- metodo generato quando non si utilizza fun ... -> ... sintassi. Gestire ciò aggiungerebbe complessità e penso che sia un caso relativamente raro, quindi il compilatore F # non lo fa e ti chiede di essere più esplicito ...

+0

Interessante, non ci ho pensato. Un nitpick, penso che 'byref' sia più come' ref', non 'out'. – svick

+0

@svick - Sei corretto - in realtà, a livello IL, il flag è 'ref' e C#' out' non esiste affatto ... –

+0

A destra, 'out' di C# è in realtà' ref' più il Attributo 'System.Runtime.InteropServices.Out'. – ildjarn

Problemi correlati