2010-01-16 23 views
8

Perché F # non supporta in modo naturale un blocco try/with/finally?Costrutti di gestione delle eccezioni F #

Essa non ha senso per provare qualcosa, affare con qualunque eccezione getta, almeno per registrare l'eccezione, e quindi essere sicuri che qualche codice viene eseguito dopo tutto quello che?

Certo, possiamo fare

try 
    try 
     ... 
    with ex -> ... 
finally 
    ... 

ma che sembra troppo artificiale, dimostra chiaramente che "F # è contro try/con/fine". Perché?

+2

È artificiale? Vedo solo una parola chiave a tre lettere ridondante. Il C++ non viene accusato di essere "contro le variabili" perché devi scrivere un "int" ridondante. Inoltre, non sono sicuro di come si utilizzino le eccezioni, ma quando sono in programmazione, le eccezioni non riguardano la registrazione. Se hai un valore da offrire al livello in cui ti trovi quando si verifica un'eccezione, restituisci quel valore, altrimenti, fai più in alto l'eccezione, ma non vedo quale operazione abbia a che fare con esso ... –

+4

@Pascal Cuoq: per favore non fare supposizioni su ** come ** io uso le eccezioni. Se voglio registrare un'eccezione per qualsiasi motivo, non implica automaticamente che io usi le eccezioni per la registrazione, come il tuo commento implica che io faccia. Inoltre, questa non è una domanda su COME gestire un'eccezione e COSA FARE, ma è semplicemente (come indicato 2 volte nella domanda): ** PERCHÉ il supporto di F # non prova con/finally **? –

risposta

12

Come qualcuno ha già detto, di solito si utilizza try-with-finally per assicurarsi di rilasciare correttamente tutte le risorse in caso di eccezione. Credo che nella maggior parte dei casi si può fare questo più facilmente utilizzando la parola chiave use:

let input = 
    try 
    use stream = new FileStream("C:\temp\test.txt"); 
    use rdr = new StreamReader(stream); 
    Some(rdr.ReadToEnd()) 
    with :? IOException as e -> 
    logError(e) 
    None 

Penso che questo è in gran parte il motivo per cui non è necessario try-with-finally come spesso come si farebbe in altre lingue. Ma ovviamente ci sono alcune situazioni in cui potresti averne bisogno (ma potresti ovviamente evitarlo creando istanza di espressioni di oggetti (che è sintatticamente molto semplice), ma penso che sia così raro che il team di F # in realtà non è necessario preoccuparsi di questo

0

Ma ciò sembra troppo artificiale, dimostra chiaramente che "F # è contro try/with/finally". Perché?

Immagino che F # potrebbe essere "contro" l'eccezione di gestione a tutti. Per motivi di interoperabilità .NET, deve supportarli, ma in pratica, non esiste alcuna gestione delle eccezioni * nella programmazione funzionale.

Eccezioni di lancio/cattura significa eseguire "salti verso il nulla" che non sono nemmeno notati dal sistema di tipi che è fondamentalmente contrario alla filosofia funzionale.

È possibile utilizzare il codice puramente funzionale (monadico) per avvolgere le eccezioni. Tutti gli errori sono gestiti attraverso i valori, in termini del sistema di tipi sottostante e senza salti/effetti collaterali.

Invece di scrivere una funzione

let readNumber() : int = ... 

che possono generare eccezioni arbitrari, sarà sufficiente dichiarate

let readNumber() : int option = ... 

che rende questo punto automaticamente chiaro la sua firma tipo.

* Ciò non significa che non gestiamo situazioni eccezionali, si tratta solo del tipo di gestione delle eccezioni in .NET/C++.

+0

Non sono affatto d'accordo con l'affermazione che "non ci sono eccezioni che passano alla programmazione funzionale". I programmatori di ML hanno fatto (buon) uso di eccezioni sin dall'inizio della lingua, anche quando scrivono pure funzioni. È vero che i sistemi di tipo comunemente usati non ne tengono conto, ma è un problema talmente piccolo che i sistemi perfettamente fini che sono stati proposti per gestirli (uno di Yi et al e uno di Pessaux et al) sono rimasti esperimenti accademici . –

+0

Ciononostante qualsiasi lancio di eccezione significa interrompere il flusso del programma * implicitamente * che è quindi intrinsecamente non funzionale. – Dario

+1

in primo luogo, F # non è un linguaggio di programmazione puramente funzionale, non privo di effetti collaterali. L'intero scopo di 'try/with/finally' sarebbe quello di separare nettamente * il codice * principale * dal codice * recovery * dal codice * finalization *, qualcosa di molto presente nel mondo non funzionale. –

1

chiarirò il mio commento in questa risposta.

  1. io sostengo non c'è alcun motivo di ritenere che si desidera catturare eccezioni e finalizzare alcune risorse allo stesso livello. Forse ti sei abituato a farlo in quel modo in un linguaggio in cui era conveniente gestirli entrambi allo stesso tempo, ma è una coincidenza quando succede. La finalizzazione è utile quando non si rilevano tutte le eccezioni dal blocco interno. try...with è per la cattura delle eccezioni in modo che il calcolo possa continuare normalmente. Non c'è semplicemente alcuna relazione tra i due (semmai, vanno in direzioni opposte: stai colto l'eccezione, o lascialo passare?).

  2. Perché devi finalizzare qualcosa? Il GC non dovrebbe gestire risorse senza riferimento per te? Ah ... ma il linguaggio sta cercando di darti accesso ai primitivi di sistema che funzionano con effetti collaterali, con allocazioni e disallocazione esplicite. Devi de-allocare ciò che hai assegnato (in tutti i casi) ...Non dovresti incolpare l'interfaccia marcia che il sistema sta fornendo invece di F #, che in questo caso è solo il messenger?

+0

Perché CW? Se è la tua opinione, non c'è bisogno di nascondersi dietro. –

+0

In ogni caso, F # è concepito per funzionare all'interno della piattaforma .NET ed è chiaramente un linguaggio funzionale impuro. Detto questo, ** no, non dovrei incolpare l'interfaccia marcia che il sistema sta provando **, ma mi piacerebbe davvero capire perché la lingua sembra andare contro. –

+0

L'intera questione è piuttosto soggettiva e polemica, e dovrebbe essere CW secondo me. –

6

Ortogonalità? Puoi semplicemente annidare un try-con all'interno di un try-finally, come mostri. (Questo è ciò che accade a livello di IL comunque, credo.)

Detto questo, provare-con-finalmente è qualcosa che possiamo considerare in una versione futura del linguaggio.

Personalmente ho solo provato a volerlo un paio di volte, ma quando ne hai bisogno, è un po 'fastidioso dover fare il nesting/rientro extra. In generale, trovo che raramente scrivo codice di gestione delle eccezioni, e di solito è solo uno o l'altro (ad esempio, per ripristinare una semantica invariabile o altra transazione, o un 'catch' nella parte superiore di un'app per registrare un'eccezione o uno spettacolo la diagnostica dell'utente).

Ma non penso ci sia molto da "leggere dentro" riguardo al design del linguaggio qui.

2

senza entrare nei dettagli, perché i grandi dettagli sono in

Expert .NET 2.0 IL Assembler di Serge Lidin

See:. Ch. 14, Exception Handling Managed

"Il finalmente e colpa gestori non possono coesistere pacificamente con altri gestori, quindi se un blocco sorvegliato ha un finalmente o colpa gestore, non può avere altro. Per combinare un gestore finalmente o colpa con altri gestori, è necessario nido custodito e gestore bocks all'interno di altri blocchi custoditi, ..., in modo che ogni finalmente o colpa gestore ha un proprio blocco custodito personali ."

pag. 300

a try/catch utilizza un guasto conduttore e un try/finally utilizza un gestore finalmente.

Vedi: ILGenerator.BeginFaultBlock Method

Se si emettono un guasto gestore in un blocco di eccezione che contiene anche un gestore cattura o un infine gestore, il codice risultante è inverificabile.

Quindi, tutti i linguaggi .net potrebbero essere considerati come l'uso di sintattica surgar e poiché F # è così nuovo, non l'hanno ancora implementato. Nessun danno nessun fould.