Читаем Учебник по Haskell полностью

(Use -XMultiParamTypeClasses to allow multi-parameter classes)

In the class declaration for ‘Multi’

Failed, modules loaded: none.

Компилятор сообщает нам о том, что у нас слишком много параметров в классе Multi. В рамках стандар-

та Haskell можно создавать лишь классы с одним параметром. Но за сообщением мы видим подсказку, если

мы воспользуемся расширением -XMultiParamTypeClasses, то всё будет хорошо. В этом сообщении имя рас-

ширения закодировано в виде флага. Мы можем запустить ghc или ghci с этим флагом и тогда расширение

будет активировано, и модуль загрузится. Попробуем:

Prelude> :q

Leaving GHCi.

$ ghci -XMultiParamTypeClasses

Prelude> :l Test

[1 of 1] Compiling Test

( Test. hs, interpreted )

Ok, modules loaded: Test.

*Test>

Модуль загрузился! У нас есть и другая возможность подключить модуль с помощью прагмы LANGUAGE.

Имя расширения записано во флаге после символов -X. Добавим в модуль Test расширение с именем

MultiParamTypeClasses:

{-# LANGUAGE MultiParamTypeClasses #-}

module Test where

class Multi a b where

Теперь загрузим ghci в обычном режиме:

*Test> :q

Leaving GHCi.

$ ghci

Prelude> :l Test

[1 of 1] Compiling Test

( Test. hs, interpreted )

Ok, modules loaded: Test.

254 | Глава 17: Дополнительные возможности

Обобщённые алгебраические типы данных

Предположим, что мы хотим написать компилятор небольшого языка. Наш язык содержит числа и логиче-

ские значения. Мы можем складывать числа и умножать. Для логических значений определена конструкция

if-then-else. Определим тип синтаксического дерева для этого языка:

data Exp = ValTrue

| ValFalse

| If Exp Exp Exp

| Val Int

| Add Exp Exp

| Mul Exp Exp

deriving (Show)

В этом определении кроется одна проблема. Наш тип позволяет нам строить бессмысленные выражения

вроде Add ValTrue (Val 2) или If (Val 1) ValTrue (Val 22). Наш тип Val включает в себя все хорошие вы-

ражения и много плохих. Эта проблема проявится особенно ярко, если мы попытаемся определить функцию

eval, которая вычисляет значение для нашего языка. Получается, что тип этой функции:

eval :: Exp -> Either Int Bool

Для решения этой проблемы были придуманы обобщённые алгебраические типы данных (generalised

algebraic data types, GADTs). Они подключаются расширением GADTs. Помните когда-то мы говорили, что

типы можно представить в виде классов. Например определение для списка

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

можно мысленно переписать так:

data List a where

Nil

:: List a

Cons :: a -> List a -> List a

Так вот в GADT определения записываются именно в таком виде. Обобщение заключается в том, что

теперь на месте произвольного параметра a мы можем писать конкретные типы. Определим тип GExp

{-# LANGUAGE GADTs #-}

data Exp a where

ValTrue

:: Exp Bool

ValFalse

:: Exp Bool

If

:: Exp Bool -> Exp a -> Exp a -> Exp a

Val

:: Int -> Exp Int

Add

:: Exp Int -> Exp Int -> Exp Int

Mul

:: Exp Int -> Exp Int -> Exp Int

Теперь у нашего типа Exp появился параметр, через который мы кодируем дополнительные ограничения

на типы операций. Теперь мы не сможем составить выражение Add ValTrue ValFalse, потому что оно не

пройдёт проверку типов.

Определим функцию eval:

eval :: Exp a -> a

eval x = case x of

ValTrue

-> True

ValFalse

-> False

If p t e

-> if eval p then eval t else eval e

Val n

-> n

Add a b

-> eval a + eval b

Mul a b

-> eval a * eval b

Если eval получит логическое значение, то будет возвращено значение типа Bool, а на значение типа Exp

Int будет возвращено целое число. Давайте убедимся в этом:

Расширения | 255

*Prelude> :l Exp

[1 of 1] Compiling Exp

( Exp. hs, interpreted )

Ok, modules loaded: Exp.

*Exp> let notE x = If x ValFalse ValTrue

*Exp> let squareE x = Mul x x

*Exp>

*Exp> eval $ squareE $ If (notE ValTrue) (Val 1) (Val 2)

4

*Exp> eval $ notE ValTrue

False

*Exp> eval $ notE $ Add (Val 1) (Val 2)

< interactive>:1:14:

Couldn’t match expected type Bool’ against inferred type Int’

Expected type: Exp Bool

Actual type: Exp Int

In the return type of a call of Add’

In the second argument of ‘($)’, namely ‘Add (Val 1) (Val 2)’

Сначала мы определили две вспомогательные функции. Затем вычислили несколько значений. Haskell

Перейти на страницу:

Похожие книги

C++: базовый курс
C++: базовый курс

В этой книге описаны все основные средства языка С++ - от элементарных понятий до супервозможностей. После рассмотрения основ программирования на C++ (переменных, операторов, инструкций управления, функций, классов и объектов) читатель освоит такие более сложные средства языка, как механизм обработки исключительных ситуаций (исключений), шаблоны, пространства имен, динамическая идентификация типов, стандартная библиотека шаблонов (STL), а также познакомится с расширенным набором ключевых слов, используемым в .NET-программировании. Автор справочника - общепризнанный авторитет в области программирования на языках C и C++, Java и C# - включил в текст своей книги и советы программистам, которые позволят повысить эффективность их работы. Книга рассчитана на широкий круг читателей, желающих изучить язык программирования С++.

Герберт Шилдт

Программирование, программы, базы данных