2010-09-04 7 views

risposta

24

Uno dei motivi è la denotational semantics of Haskell.

Una delle proprietà ordinate di funzioni (puro) Haskell è la loro monotonia - più definiti i rendimenti argomento valore più definito. Questa proprietà è molto importante per es. ragionare sulle funzioni ricorsive (leggi l'articolo per capire perché).

Denotazione di eccezione per definizione è il fondo, _|_, l'elemento minimo in poset corrispondente al tipo specificato. Così, per soddisfare il requisito monotonia, la seguente disuguaglianza deve tenere per qualsiasi denotazione f della funzione Haskell:

f(_|_) <= f(X) 

Ora, se noi potessimo prendere eccezioni, potremmo rompere questa disuguaglianza per "riconoscere" il fondo (cattura la eccezione) e restituendo il valore più definito:

f x = case catch (seq x True) (\exception -> False) of 
     True -> -- there was no exception 
      undefined 
     False -> -- there was an exception, return defined value 
      42 

Ecco completa dimostrazione di lavoro (richiede base-4 Control.Exception):

import Prelude hiding (catch) 
import System.IO.Unsafe (unsafePerformIO) 
import qualified Control.Exception as E 

catch :: a -> (E.SomeException -> a) -> a 
catch x h = unsafePerformIO $ E.catch (return $! x) (return . h) 

f x = case catch (seq x True) (\exception -> False) of 
     True -> -- there was no exception 
      undefined 
     False -> -- there was an exception, return defined value 
      42 

Un'altra ragione, come ha osservato TomMD, sta infrangendo la trasparenza referenziale. È possibile sostituire le stesse cose con lo stesso numero e ottenere un'altra risposta. (Uguale in senso denotazionale, cioè indicano lo stesso valore, non nel senso ==.)

Come faremmo? Si consideri la seguente espressione:

let x = x in x 

Questa è una ricorsione non fatale, in modo che non ci riporta alcuna informazione, e quindi è indicato anche da _|_. Se siamo stati in grado di intercettare le eccezioni, potremmo scrivere funzione f come

f undefined = 0 
f (let x = x in x) = _|_ 

(Quest'ultima è sempre vero per le funzioni severe, perché Haskell fornisce alcun mezzo per rilevare il calcolo non fatale - e non può in linea di principio, a causa del Halting problem.)

+1

Ok. Questo sembra essere uno dei difficili backround matematici dietro haskell. Grazie per la tua breve descrizione. – fuz

+1

Scusate ma questa risposta è del tutto sconcertante per quello che penso sia una domanda molto semplice ... ma questo probabilmente riflette più sulla mia mancanza di comprensione di Haskell piuttosto che sulla vostra risposta. –

+1

dodgy_coder: Mi dispiace, non è stato utile per te. Potrebbero esserci prospettive diverse dalle quali si può rispondere a questa domanda. Si potrebbe dire che è possibile (non) farlo perché i tipi di funzioni corrispondenti lo consentono (non). Quindi, naturalmente, potresti chiedere perché i tipi sono così come sono. La risposta è "ci sono buone ragioni per (non) consentire tali cose", e ho cercato di delineare queste ragioni sopra. –

14

Perché le eccezioni possono interrompe referential transparency.

Probabilmente stai parlando di eccezioni che sono in realtà i risultati diretti di input. Per esempio:

head [] = error "oh no!" -- this type of exception 
head (x:xs) = x 

Se rimpiangiamo di non essere in grado di catturare gli errori come questo allora affermo a voi che le funzioni non dovrebbero essere affidamento su error o altre eccezioni, ma dovrebbero invece utilizzare un tipo di ritorno adeguato (Maybe, Either, o forse MonadError). Questo ti obbliga ad affrontare la condizione eccezionale in modo più esplicito.

A differenza di quanto sopra (e quali sono le cause alla base della tua domanda), le eccezioni possono essere dovute a segnali come condizioni di memoria completamente indipendenti dal valore calcolato. Questo non è chiaramente un concetto puro e deve vivere in IO.

+2

Puoi dare un esempio di rottura della trasparenza referenziale in questo modo? –

+0

Quindi vuoi dirmi, che le escissioni non sono il modo di pensare in modo funzionale, quindi dovrei generalmente cercare di evitarli? Grande! – fuz

+0

Roman: Quello che ho detto è che se si potevano catturare eccezioni nel codice puro e basare i risultati su quelle eccezioni, un'azione del genere avrebbe infranto la trasparenza referenziale. –

1

Potrei sbagliarmi nella mia spiegazione, ma è così che lo capisco.

Poiché le funzioni sono pure in Haskell, il compilatore ha il diritto di valutarle in qualsiasi ordine desideri e produce comunque lo stesso risultato. Ad esempio, in funzione:

square :: Int -> Int 
square x = x * x 

espressione square (square 2) può essere valutata in diversi modi, ma riduce sempre allo stesso risultato che è 16.

Se chiamiamo square da altrove:

test x = if x == 2 
     then square x 
     else 0 

square x può essere valutato in seguito, "all'esterno" della funzione test quando il valore è effettivamente necessario. In quel momento lo stack delle chiamate può essere completamente diverso da quello che ti aspetteresti di dire in Java.

Quindi, anche se volessimo rilevare una potenziale eccezione generata da square, dove dovresti posizionare la parte catch?