Изучай Haskell во имя добра! (Липовача) - страница 202

>ghci> head [3,4,5,undefined,2,undefined]

>3

Теперь рассмотрите следующий тип:

>data CoolBool = CoolBool { getCoolBool :: Bool }

Это ваш обыкновенный алгебраический тип данных, который был объявлен с использованием ключевого слова >data. Он имеет один конструктор данных, который содержит одно поле с типом >Bool. Давайте создадим функцию, которая сопоставляет с образцом значение >CoolBool и возвращает значение >"привет" вне зависимости от того, было ли значение >Bool в >CoolBool равно >True или >False:

>helloMe :: CoolBool –> String helloMe (CoolBool _) = "привет"


Вместо того чтобы применять эту функцию к обычному значению типа >CoolBool, давайте сделаем ей обманный бросок – применим её к значению >undefined!

>ghci> helloMe undefined

>*** Exception: Prelude.undefined

Тьфу ты! Исключение! Почему оно возникло? Типы, определённые с помощью ключевого слова >data, могут иметь много конструкторов данных(хотя >CoolBool имеет только один конструктор). Поэтому для того чтобы понять, согласуется ли значение, переданное нашей функции, с образцом >(CoolBool _), язык Haskell должен вычислить значение ровно настолько, чтобы понять, какой конструктор данных был использован, когда мы создавали значение. И когда мы пытаемся вычислить значение >undefined, будь оно даже небольшим, возникает исключение.

Вместо ключевого слова >data для >CoolBool давайте попробуем использовать >newtype:

>newtype CoolBool = CoolBool { getCoolBool :: Bool }

Нам не нужно изменять нашу функцию >helloMe, поскольку синтаксис сопоставления с образцом одинаков независимо от того, использовалось ли ключевое слово >newtype или >data для объявления вашего типа. Давайте сделаем здесь то же самое и применим >helloMe к значению >undefined:

>ghci> helloMe undefined

>"привет"

Сработало! Хм-м-м, почему? Ну, как вы уже узнали, когда вы используете ключевое слово >newtype, язык Haskell внутренне может представлять значения нового типа таким же образом, как и первоначальные значения. Ему не нужно помещать их ещё в одну коробку; он просто должен быть в курсе, что значения имеют разные типы. И поскольку язык Haskell знает, что типы, созданные с помощью ключевого слова >newtype, могут иметь лишь один конструктор данных и одно поле, ему не нужно вычислять значение, переданное функции, чтобы убедиться, что значение соответствует образцу >(CoolBool _).



Это различие в поведении может казаться незначительным, но на самом деле оно очень важно. Оно показывает, что хотя типы, определённые с помощью деклараций >data и >newtype, ведут себя одинаково с точки зрения программиста (так как оба имеют конструкторы данных и поля), это фактически два различных механизма. Тогда как ключевое слово