Определение нового типа на Haskell

Я прочитал это утверждение в коде, предоставленном для домашней работы:-

newtype STR a = STR (Store -> (Result a, Store))

Приведенная выше ссылка выглядит так:

a === (Store -> (Result a, Store))

Как это может быть действительным утверждением? Означает ли это, что a это функция, которая принимает Store в качестве аргумента и возвращает ('the same function wrapped in Result', Store)?

2 ответа

Решение

newtype определение немного сбивает с толку, потому что символ STR используется в двух разных значениях: а именно, имя типа (первое вхождение) и конструктор (второе). Переименование обоих во что-то другое приводит к эквивалентному

newtype STRType a = STRConstructor (Store -> (Result a, Store))

Другими словами, это вводит тип STRType a который структурно такой же, как Store -> (Result a, Store) (но должен быть завернут в STRConstructor)

Я надеюсь, что ваш курс / книга уже вошли в различия между type, data а также newtype; иначе это останется довольно таинственным, я боюсь...

Таким образом, для определений data / newtype в левой части содержится определение типа, включающее в себя имя и некоторые переменные типа, в то время как в правой части содержится список конструктора, обычно также включающий имена и типы. Примером является:

data List x = Nil | Cons x (List x)

Заметить, что Nil а также Cons ваши конструкторы, x а также List x типы аргументов для Cons конструктор, в то время как List это имя типа (который имеет вид * -> *; ему требуется аргумент типа ___, чтобы "заполнить его", прежде чем он сможет описать список ___).

Иногда мы хотим создать псевдоним типа. Есть два способа сделать это. Первый, type, который оставляет типы соразмерными - так что если вы пишете type DirectoryPath = [String] тогда, когда у вас есть DirectoryPath вы можете манипулировать им как списком строк; в частности, вы можете использовать : или же ++ добавить к нему; так "apps" : base_directory было бы совершенно законно Haskell.

Иногда вы просто хотите заглушить эти функции большим предупреждающим знаком: "Не используйте это, если не знаете, что делаете". Для этого вы могли бы полезно написать data FilePath = FilePath [String], Обратите внимание, что мы немного злоупотребляем обозначениями, называя тип так же, как единственный конструктор для этого типа. Теперь, чтобы сделать то же самое, вы должны написать:

case base_directory of FilePath bd -> FilePath $ "apps" : bd

Зачем тебе это делать? Ну, во-первых, в приведенном выше синтаксисе структура каталогов растет справа налево, в то время как большинство людей пишут каталоги слева направо. Во-вторых, вы можете добавить . быть неоператором (то есть bd ++ []) а также .. быть родительским указателем (т.е. tail bd). Вы также можете иметь некоторые странные соглашения (например, если список начинается с "", то это абсолютный каталог, в противном случае он относится к текущему каталогу), которые необходимо поддерживать. Наконец, вы можете позже сдвинуть код, чтобы Maybe [String] так что Nothing значение может представлять пути, которые делают сумасшедшие вещи, такие как /../.. (абсолютный путь двух родителей выше корня).

Все это становится проще, если вы можете просто сказать:

FilePath xs ./ x
    | x == "."  = FilePath xs
    | x == ".." = FilePath (tail xs)
    | otherwise = ...

а затем обеспечить, что везде люди пишут base_directory ./ "apps",

Другой пример newtype SanitizedString = Sanitized String, Так как мы не использовали type мы получаем метку времени компиляции, которая прослеживает наш код и проверяет, что строки, предоставленные пользователем, должным образом экранированы перед тем, как, скажем, они попадают в операторы вставки базы данных или пользовательский интерфейс или где-либо еще.

То, что вы, вероятно, используете это здесь, что вы можете написать Monad экземпляр для этого типа и тем самым использовать его с do-notation.

ЕСЛИ ваш код содержит только один существующий тип, newtype избегает введения дополнительных забот лени и задержек конструктора данных и так далее. В противном случае это так же, как data, Итак, в вашем коде:

newtype STR a = STR (Store -> (Result a, Store))

это не type синоним, но больше похоже на data конструктор, который в конечном итоге исчезает после компиляции. STR a это псевдоним для Store -> (Result a, Store) который был завернут в STR конструктор (поэтому он не может быть использован как функция напрямую без деструктурирующего присваивания).

Другие вопросы по тегам