x !! (-1)
fa la cosa "più sicuro" e crash
d'arresto non è sicuro. Rendere una funzione non totale distrugge la tua capacità a causa del comportamento di una funzione in base al suo tipo.
Supponiamo che take
e drop
abbiano avuto un comportamento "in caso di arresto anomalo".Prendere in considerazione il loro tipo:
take, drop :: Int -> [a] -> [a]
Una cosa di questo tipo sicuramente non vi dico che questa funzione potrebbe bloccarsi! È utile ragionare sul codice come se stessimo usando un linguaggio totale, anche se non lo siamo - un'idea chiamata fast and loose reasoning - ma per essere in grado di farlo, devi evitare di usare (e scrivere) funzioni non totali come il più possibile
Cosa fare, quindi, sulle operazioni che potrebbero non riuscire o non avere alcun risultato? I tipi sono la risposta! Un veramente sicuro variante (!!)
avrebbe un tipo che modella il caso fallimento, come:
safeIndex :: [a] -> Int -> Maybe a
Questo è preferibile al tipo di (!!)
,
(!!) :: [a] -> Int -> a
Che, per la semplice osservazione, può non hanno abitanti (totali) - non puoi "inventare" un a
se la lista è vuota!
Infine, torniamo a take
e drop
. Sebbene il loro tipo non dica completamente quale sia il comportamento, insieme ai loro nomi (e idealmente ad alcune proprietà QuickCheck) otteniamo una buona idea. Come altri soccorritori hanno sottolineato, questo comportamento è appropriato in molti casi. Se hai veramente hai la necessità di rifiutare gli input di lunghezza negativa, non devi scegliere tra non-totalità (arresto anomalo) o possibilità di comportamento sorprendente (lunghezza negativa accettata) - modellare i risultati possibili in modo responsabile con i tipi.
Questo tipo chiarisce che c'è "nessun risultato" per alcuni input:
takePos, dropPos :: Int -> [a] -> Maybe [a]
Meglio ancora, questo tipo utilizza numeri naturali; Le funzioni con questo tipo non possono nemmeno essere applicate a un numero negativo!
takeNat, dropNat :: Nat -> [a] -> [a]
Ci sono cose ragionevoli e spesso utili da fare quando gli argomenti vanno oltre i limiti: "restituisce l'intero elenco originale" o "restituisce l'elenco vuoto". Non puoi assolutamente definire '(!! (-1))', non c'è alcun valore di riserva da restituire. –
Un fatto divertente: la semantica cambiata tra i rapporti Haskell 98 e 2010. L'implementazione [esempio] (https://www.haskell.org/onlinereport/list.html) in 98 ha usato 'error' se la lista non era vuota e' n' era negativa, mentre la variante 2010 mostrava valori negativi ha lo stesso effetto di "prendi 0". – Zeta
@Zeta: è affascinante, ottima scoperta! – Lynn