2015-02-09 6 views
0

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

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

выше ссылка делает это похоже:

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

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

+3

Что означает «семантика нормальной объектной модели»? – dfeuer

+0

Я согласен «нормальным» является спорным словом. Но я не мог найти лучшего способа выразить свое замешательство. –

+3

Я хочу сказать, что гораздо лучше попытаться придумать простой язык, выражающий вашу точку зрения, чем использовать причудливые слова, такие как «семантика объектной модели». У Haskell даже нет объектов, поэтому такие описания с большей вероятностью будут путать, чем общаться. – dfeuer

ответ

9

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

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

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

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

+1

Могу ли я предложить использовать термины «конструктор типов» и «конструктор данных»? – dfeuer

+0

Я бы предпочел перегрузить слово «конструктор» и позволить конструкторам работать на уровне значений (т. Е. Что-то делать «в куче» во время выполнения) и вызывать «STRType» типа-бывшему или что-то еще. – yatima2975

3

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

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

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

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

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

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

Зачем вам это нужно? Ну, во-первых, в приведенном выше синтаксисе структура каталогов растет справа налево, тогда как большинство людей пишут каталоги слева направо. Во-вторых, вы можете захотеть добавить . как no-op (то есть 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 конструктор, который в конечном счете исчезает после компиляции. A STR a - это псевдоним для Store -> (Result a, Store), который был обернут в конструктор STR (поэтому он не может использоваться как функция напрямую без назначения деструкции).

Смежные вопросы