- Конструктор значения "Cons" имеет параметр одного типа.
Нет: вы уже параметризовали его, когда объявили data List a
. Одним из эффективных свойств этого является то, что если у меня есть Nil :: List Int, я не могу поменять его на Nil :: List Char.
- Если вы используете конструктор значений «Минусы», необходимо указать 2 поля. Первое обязательное поле - это экземпляр List. Второе обязательное поле - это экземпляр.
Вы поменялись местами: первое обязательное поле - это экземпляр a, второе поле - это экземпляр List.
Эта глава из реального мира Haskell может представлять интерес.
Спасибо. Это глава, в которой я сейчас нахожусь. Итак ... когда в коде написано «Cons a (Список a)», я подумал, что часть «Cons a» указывает на то, что конструктор значения Cons был параметризован. Они еще не охватили синтаксис для параметризованных типов, поэтому я догадался, что синтаксис должен требовать повторения «a», если вы собираетесь использовать a. Но вы говорите, что это не нужно? И поэтому это не то, что означает «а»?
Неа. После того, как мы объявили параметр в нашем типе, мы можем использовать его повторно, чтобы сказать «этот тип должен использоваться там». Это немного похоже на сигнатуру типа a -> b -> a
: a параметризует тип, но тогда я должен использовать то же самое, что и возвращаемое значение.
ОК, но это сбивает с толку. Кажется, что первое «а» означает «первое поле является экземпляром»,
Нет, это не правда. Это просто означает, что тип данных параметризуется по некоторому типу a.
и это ТАКЖЕ означает «первое поле имеет то же значение, что и значение, которое они передали для a». Другими словами, он указывает значение типа AND.
Нет, это тоже неправда.
Вот поучительный пример, синтаксис которого вы могли или не могли видеть раньше:
foo :: Num a => a -> a
Это довольно стандартная подпись для функции, которая берет число, что-то с ним делает и дает вам другой номер. Что я на самом деле подразумеваю под «числом» в языке Haskell, тем не менее, это некоторый произвольный тип «a», который реализует класс «Num».
Таким образом, это разбирает на английский:
Пусть a указывает тип, реализующий класс типов Num, тогда сигнатурой этого метода является один параметр с типом a, а возвращаемое значение типа a
Нечто подобное происходит с данными.
Мне также кажется, что экземпляр List в спецификации Cons также сбивает вас с толку: будьте очень осторожны при разборе этого: тогда как Cons задает конструктор, который по сути является шаблоном, который Haskell собирается обернуть данные into (List a) выглядит как конструктор, но на самом деле это просто тип, такой как Int или Double. a - это тип, а не значение в каком-либо смысле этого термина.
Редактировать: В ответ на самое последнее редактирование.
Я думаю, что сначала требуется вскрытие. Тогда я разберусь с вашими вопросами.
Конструкторы данных на Haskell немного странные, потому что вы определяете сигнатуру конструктора, и вам не нужно создавать никаких других скаффолдингов. Типы данных в Haskell не имеют представления о переменной-члене. (Примечание: есть альтернативный синтаксис, к которому такой способ мышления более поддается, но давайте пока проигнорируем его).
Другое дело, что код на Haskell плотный; его тип подписи таковы. Поэтому ожидайте увидеть один и тот же символ, повторно используемый в разных контекстах. Вывод типа также играет здесь большую роль.
Итак, вернемся к вашему типу:
data List a = Cons a (List a)
| Nil
Я разбил это на несколько частей:
data <b>List a</b>
Это определяет имя типа и любые параметризованные типы, которые будут у него позже. Обратите внимание, что вы увидите это только в сигнатурах других типов.
<b>Cons</b> a (List a) |
<b>Nil</b>
Это имя конструктора данных. Это НЕ тип . Мы можем, однако, сопоставить шаблон с этим, ала:
foo :: List a -> Bool
foo Nil = True
Обратите внимание, что List a является типом в сигнатуре, а Nil является и конструктором данных, и "вещью", для которой мы сопоставляем шаблон.
Cons <b>a (List a)</b>
Это типы значений, которые мы вставляем в конструктор. Минусы имеет две записи, одна из которых имеет тип a, а другая имеет тип List a.
Итак, мой вопрос: почему «а» в первом поле означает «тип этого поля -« а », а значение этого поля -« а », тогда как« а »во втором поле означает только "тип этого поля 'Список' '?
Простой: не думайте, что мы указываем тип; думаю, что Haskell выводит тип из этого. Таким образом, для наших целей мы просто вставляем 0, а Nil - во второй раздел. Затем Хаскелл смотрит на наш код и думает:
- Хм, интересно, какой тип минусов 0, ноль
- Ну, Cons это конструктор данных для List a. Интересно, что это за тип List a
- Что ж, в первом параметре используется a, так как первый параметр - это Int (еще одно упрощение; 0 на самом деле странная вещь, типизированная как Num), так что это означает, что a - это Num
- Эй, ну, это также означает, что типом Nil является List Int, хотя там нет ничего, что могло бы сказать, что
(Обратите внимание, что на самом деле это не так, как это реализовано. Haskell может делать много странных вещей при выводе типов, что частично объясняет, почему сообщения об ошибках отстой.)