Основные области применения новых типов:
- Для определения альтернативных экземпляров типов.
- Документация.
- Обеспечение правильности данных/формата.
Сейчас я работаю над приложением, в котором я широко использую новые типы. newtypes
в Haskell — это чисто концепция времени компиляции. Например. с распаковщиками ниже, unFilename (Filename "x")
скомпилирован в тот же код, что и "x". Существует абсолютно нулевое попадание во время выполнения. Есть с data
типами. Это делает его очень хорошим способом достижения перечисленных выше целей.
-- | A file name (not a file path).
newtype Filename = Filename { unFilename :: String }
deriving (Show,Eq)
Я не хочу случайно рассматривать это как путь к файлу. Это не путь к файлу. Это имя концептуального файла где-то в базе данных.
Для алгоритмов очень важно ссылаться на правильную вещь, в этом помогают новые типы. Это также очень важно для безопасности, например, рассмотрите возможность загрузки файлов в веб-приложение. У меня есть такие типы:
-- | 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)
Предположим, у меня есть эта функция, которая очищает имя файла из загруженного файла:
-- | 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 "-_."
Теперь из этого я создаю уникальное имя файла:
-- | Generate a unique filename.
uniqueFilename :: SanitizedFilename -- ^ Sanitized filename.
-> IO UniqueFilename -- ^ Unique filename.
Опасно генерировать уникальное имя файла из произвольного имени файла, его следует сначала очистить. Таким образом, уникальное имя файла всегда безопасно по расширению. Теперь я могу сохранить файл на диск и поместить это имя файла в свою базу данных, если захочу.
Но также может раздражать необходимость много обертывать/разворачивать. В долгосрочной перспективе я считаю, что это того стоит, особенно для предотвращения несоответствия значений. ViewPatterns немного помогают:
-- | Get the form fields for a form.
formFields :: ConferenceId -> Controller [Field]
formFields (unConferenceId -> cid) = getFields where
... code using cid ..
Может быть, вы скажете, что развернуть его в функции — это проблема — что, если вы неправильно передадите cid
в функцию? Не проблема, все функции, использующие идентификатор конференции, будут использовать тип ConferenceId. Возникает что-то вроде системы контрактов между функциями, которая принудительно применяется во время компиляции. Довольно мило. Так что да, я использую его так часто, как могу, особенно в больших системах.
person
Christopher Done
schedule
10.10.2010