I principali usi per newtypes sono:
- Per definire le istanze alternative per i tipi.
- Documentazione.
- Garanzia di correttezza dei dati/formato.
Sto lavorando a un'applicazione in questo momento in cui utilizzo estesamente i nuovi tipi. newtypes
in Haskell sono un concetto puramente in fase di compilazione. Per esempio. con i wrapper qui sotto, unFilename (Filename "x")
compilato con lo stesso codice di "x". C'è assolutamente zero hit di run-time. C'è con i tipi data
. Questo lo rende un ottimo modo per raggiungere gli obiettivi sopra elencati.
-- | A file name (not a file path).
newtype Filename = Filename { unFilename :: String }
deriving (Show,Eq)
Non voglio trattarlo accidentalmente come un percorso di file. Non è un percorso di file. È il nome di un file concettuale da qualche parte nel database.
È molto importante che gli algoritmi facciano riferimento alla cosa giusta, i nuovi tipi aiutano in questo. È anche molto importante per la sicurezza, ad esempio, prendere in considerazione il caricamento di file su un'applicazione web. Ho questi tipi:
-- | A sanitized (safe) filename.
newtype SanitizedFilename =
SanitizedFilename { unSafe :: String } deriving Show
-- | Unique, sanitized filename.
newtype UniqueFilename =
UniqueFilename { unUnique :: SanitizedFilename } deriving Show
-- | An uploaded file.
data File = File {
file_name :: String --^Uploaded file.
,file_location :: UniqueFilename --^Saved location.
,file_type :: String --^File type.
} deriving (Show)
Supponiamo che io abbia questa funzione che pulisce un nome di file da un file che è stato caricato:
-- | Sanitize a filename for saving to upload directory.
sanitizeFilename :: String --^Arbitrary filename.
-> SanitizedFilename --^Sanitized filename.
sanitizeFilename = SanitizedFilename . filter ok where
ok c = isDigit c || isLetter c || elem c "-_."
Ora da quel genero un nome file univoco:
-- | Generate a unique filename.
uniqueFilename :: SanitizedFilename --^Sanitized filename.
-> IO UniqueFilename --^Unique filename.
È pericoloso generare un nome file univoco da un nome file arbitrario, deve essere prima disinfettato.Allo stesso modo, un nome file univoco è quindi sempre sicuro per estensione. Ora posso salvare il file su disco e inserire il nome del file nel mio database, se lo desidero.
Ma può anche essere fastidioso dover avvolgere/scartare molto. A lungo termine, lo considero ne vale la pena soprattutto per evitare discrepanze di valore. ViewPatterns aiutano un po ':
-- | Get the form fields for a form.
formFields :: ConferenceId -> Controller [Field]
formFields (unConferenceId -> cid) = getFields where
... code using cid ..
Forse si dirà che non imballato in una funzione è un problema - quello che se si passa a una funzione cid
torto? Non è un problema, tutte le funzioni che utilizzano un ID conferenza utilizzeranno il tipo ConferenceId. Ciò che emerge è una sorta di sistema di contratto a livello di funzione a funzione che è forzato in fase di compilazione. Molto carino. Quindi sì, lo uso più spesso che posso, specialmente nei grandi sistemi.
Hrm ... sembra che non sia possibile contrassegnare più di una risposta come accettata. Speravo di accettare in qualche modo una ragionevole rappresentazione delle diverse opinioni su questo tema ... – StevenC