28

Questo è un follow-up di Why am I getting "Non-exhaustive patterns in function..." when I invoke my Haskell substring function?In Haskell, perché i pattern non esaustivi non sono errori in fase di compilazione?

intendo che usando -Wall, GHC può avvisare contro modelli non esaustive. Mi chiedo qual è la ragione che sta dietro non fare un errore di compilazione di default dato che è sempre possibile definire in modo esplicito una funzione parziale:

f :: [a] -> [b] -> Int 
f [] _ = error "undefined for empty array" 
f _ [] = error "undefined for empty array" 
f (_:xs) (_:ys) = length xs + length ys 

Il problema non è specifico GHC-.

Forse perché ...

  • nessuno voleva far rispettare un compilatore Haskell per eseguire questo tipo di analisi?
  • una ricerca di modello non esaustiva può trovare alcuni ma non tutti i casi?
  • funzioni parzialmente definite sono considerati legittimi e utilizzati abbastanza spesso non imporre il tipo di costrutto sopra indicato? Se questo è il caso, puoi spiegarmi perché schemi non esaustivi sono utili/legittimi?

risposta

33

In alcuni casi non vi dispiace che una corrispondenza di modello non sia esaustiva. Per esempio, mentre questo potrebbe non essere l'attuazione ottimale, non credo che sarebbe d'aiuto se non compilare:

fac 0 = 1 
fac n | n > 0 = n * fac (n-1) 

Che questo non è esaustivo (i numeri negativi non corrispondono ogni caso) non ha davvero importanza per l'uso tipico della funzione fattoriale.

Inoltre potrebbe non essere generalmente possibile decidere per il compilatore se un pattern match è esaustivo:

mod2 :: Integer -> Integer 
mod2 n | even n = 0 
mod2 n | odd n = 1 

Ecco tutti i casi dovrebbero essere coperti, ma il compilatore probabilmente non in grado di rilevarlo. Dal momento che le guardie potrebbero essere arbitrariamente complesse, il compilatore non può sempre decidere se i modelli sono esaurienti. Ovviamente questo esempio dovrebbe essere scritto con otherwise, ma penso che dovrebbe anche compilare nella sua forma attuale.

+4

Un altro caso piuttosto comune sarebbe dichiarazioni lambda all'interno di una guardia/caso/se ramo. Sai che l'argomento ha una certa forma a causa del ramo in cui ti trovi, quindi includerlo nel lambda non è necessario. –

+1

Non penso che dovrebbe essere scritto come altrimenti. È una grande definizione di mod2 anche se il compilatore pensa che non sia esaustivo. Sono arrivato a questa domanda perché ho ottenuto questo errore scrivendo una funzione fib che non dovrebbe essere definita per numeri interi negativi. –

+0

forse questo è fuori portata, ma sta decidendo se una corrispondenza di modello è esauriente o non correlata al problema di interruzione? intuitivamente sembra un linguaggio scritto in modo così rigoroso come haskell dovrebbe abilitare questo tipo di analisi, almeno per i sottoinsiemi del linguaggio, ma so che l'istinto istintivo di solito è un modo orribile per indovinare se qualcosa è calcolabile o meno. – kai

12

È possibile utilizzare -Werror per trasformare gli avvisi in errori. Non so se puoi trasformare solo gli avvertimenti dei pattern non esaustivi in ​​errori, mi dispiace!

Per quanto riguarda la terza parte della tua domanda:

a volte scrivo una serie di funzioni che tendono a lavorare in stretta collaborazione e hanno proprietà che non si può facilmente esprimere in Haskell. Almeno alcune di queste funzioni tendono ad avere modelli non esaustivo, di solito i consumatori '. Ciò emerge, ad esempio, in funzioni che sono "inverse" l'una dall'altra.

Un esempio giocattolo:

duplicate :: [a] -> [a] 
duplicate [] = [] 
duplicate (x:xs) = x : x : xs 

removeDuplicates :: Eq a => [a] -> [a] 
removeDuplicates [] = [] 
removeDuplicates (x:y:xs) | x == y = x : removeDuplicates xs 

Ora è abbastanza facile vedere che removeDuplicates (duplicate as) è uguale a as (ogni volta che il tipo di elemento è in Eq), ma in generale duplicate (removeDuplicates bs) sarà in crash, perché ci sono un numero dispari di elementi o 2 elementi consecutivi differiscono. Se non va in crash, è perché bs è stato prodotto da (o avrebbero potuto essere produced by) duplicate in primo luogo !.

, così abbiamo i seguenti leggi (non valida Haskell):

removeDuplicates . duplicate == id 
duplicate . removeDuplicates == id (for values in the range of duplicate) 

Ora, se si vuole evitare che i modelli non esaustivi qui, si potrebbe fare removeDuplicates ritorno Maybe [a], o aggiungere messaggi di errore per i dispersi casi. Si potrebbe anche fare qualcosa sulla falsariga di

newtype DuplicatedList a = DuplicatedList [a] 

duplicate :: [a] -> DuplicatedList a 
removeDuplicates :: Eq a => DuplicatedList a -> [a] 
-- implementations omitted 

Tutto questo è necessario, perché non si può facilmente esprimere 'essere una lista di lunghezza pari, con coppie consecutive di elementi parità' nel sistema di tipo Haskell (a meno che non siate Oleg :)

Ma se non esportate removeDuplicates penso che sia perfettamente corretto usare schemi non esaustivi qui. Non appena lo esporti, perderai il controllo degli input e dovrai gestire i casi mancanti!

+0

Immagino tu intenda removeDuplicates (x: y: xs) | x == y = x: removeDuplica xs. – gawi

+0

Grazie, l'ho risolto! – yatima2975

Problemi correlati