Główne zastosowania do newtypes są:
- Definiowanie alternatywne przykłady typów.
- Dokumentacja.
- Zapewnienie poprawności danych/formatów.
Pracuję teraz nad aplikacją, w której intensywnie używam newtypów. newtypes
w Haskell są pojęciem wyłącznie kompilacji. Na przykład. z poniższymi archiwami, unFilename (Filename "x")
skompilowany do tego samego kodu co "x". Nie ma absolutnie zero hit run-time. Dostępne są typy data
. To sprawia, że jest to bardzo dobry sposób na osiągnięcie wyżej wymienionych celów.
-- | A file name (not a file path).
newtype Filename = Filename { unFilename :: String }
deriving (Show,Eq)
Nie chcę przypadkowo traktować tego jako ścieżki do pliku. To nie jest ścieżka do pliku. Jest to nazwa pliku konceptualnego gdzieś w bazie danych.
Bardzo ważne jest, aby algorytmy odwoływały się do właściwych rzeczy, a nowe typy pomagają w tym. Jest to również bardzo ważne dla bezpieczeństwa, na przykład rozważ przesłanie plików do aplikacji internetowej. Mam te typy:
-- | 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)
Załóżmy, że mam tę funkcję, która myje nazwę pliku z pliku, który został przekazany:
-- | 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 "-_."
Teraz od tego wygenerować unikalny Nazwa pliku:
-- | Generate a unique filename.
uniqueFilename :: SanitizedFilename --^Sanitized filename.
-> IO UniqueFilename --^Unique filename.
To niebezpieczne, aby wygenerować unikalną nazwę pliku z arbitralnej nazwy pliku, powinno być najpierw oczyszczone.Podobnie unikalna nazwa pliku jest zawsze bezpieczna przez rozszerzenie. Mogę teraz zapisać plik na dysk i umieścić go w mojej bazie danych, jeśli chcę.
Ale może być również denerwujące, aby dużo owijać/rozwierać. Na dłuższą metę uważam, że warto, szczególnie w celu uniknięcia niedopasowania wartości. ViewPatterns pomóc nieco:
-- | Get the form fields for a form.
formFields :: ConferenceId -> Controller [Field]
formFields (unConferenceId -> cid) = getFields where
... code using cid ..
Może powiesz, że rozpakowanie go w funkcji jest problemem - co zrobić, jeśli przechodzą cid
do funkcji niewłaściwie? To nie problem, wszystkie funkcje używające identyfikatora konferencji będą używać typu ConferenceId. Wyłania się rodzaj systemu kontraktowego, który jest wymuszany podczas kompilacji. Nieźle. Tak więc używam go tak często, jak tylko mogę, szczególnie w dużych systemach.
Hrm ... wygląda tak, że nie mogę oznaczyć więcej niż jednej odpowiedzi jako zaakceptowanej. Miałem nadzieję, że w jakiś sposób zaakceptuję rozsądną reprezentację różnych opinii na ten temat ... – StevenC