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

ничением. “Для любых” означает, что мы не можем делать никаких предположений о внутренней природе

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

их в новые полиморфные функции (как в map), отбрасывать (как const) или перекладывать из одного ме-

ста в другое (как в swap или reverse). Мы можем немного смягчить ограничение, если укажем в контексте

функции какие классы определены для значений данного типа.

Все стандартные полиморфные типы имеют вид:

fun :: forall a b .. z. Expr(a, b, ... , z)

Причём Expr не содержит forall, а только стрелки и применение новых типов к параметрам. Такой тип

называют полиморфным типом первого порядка (rank). Если forall стоит справа от стрелки, то его можно

вынести из выражения, например, следующие выражения эквивалентны:

fun :: forall a.

a -> (forall b. b -> b)

fun :: forall a b. a -> (b -> b)

Так мы можем привести не стандартный тип к стандартному. Если же forall встречается слева от стрел-

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

Порядок полиморфизма определяется как самый максимум среди всех подвыражений, что стоят слева от

стрелки плюс один. Так в типе

runST :: (forall s. ST s a) -> a

Слева от стрелки стоит тип первого порядка, прибавив единицу, получим порядок для всего выражения.

Если вдруг нам захочется воспользоваться такими типами, мы можем включить одно из расширений:

{-# Language Rank2Types #-}

{-# Language RankNTypes #-}

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

придётся помогать компилятору расставляя типы сложных функций вручную.

Лексически связанные типы

Мы уже привыкли к тому, что когда мы пишем

swap :: (a, b) -> (b, a)

компилятор понимает, что a и b указывают на один и тот же тип слева и справа от стрелки. При этом типы

a и b не обязательно разные. Иногда нам хочется расширить действие контекста функции и распространить

его на всё тело функции. Например ранее в этой главе, когда мы имитировали числа через типы, для того

чтобы извлечь число из типа, мы пользовались трюком с функцией proxy:

instance Nat a => Nat (Succ a) where

toInt x = 1 + toInt (proxy x)

proxy :: f a -> a

proxy = undefined

Единственное назначение функции proxy~– это передача информации о типе. Было бы гораздо удобнее

написать:

instance Nat a => Nat (Succ a) where

toInt x = 1 + toInt (undefined :: a)

Проблема в том, что по умолчанию любой полиморфный тип в Haskell имеет первый ранг, компилятор

читает нашу запись как (x :: forall a. a), и получается, что мы говорим: x имеет любой тип, какой

захочешь! Не очень полезная информация. Компилятор заблудился и спрашивает у нас: “куда пойти?” А

мы ему: “да куда захочешь”. Как раз для таких случаев существует расширение ScopedTypeVariables. Оно

связывает тип, объявленный в заголовке класса/функции с типами, которые встречаются в теле функции.

В случае функций есть одно отличие от случая с классами. Если мы хотим расширить действие переменной

из объявления типа функции, необходимо упомянуть её в слове forall в стандартном положении (как для

типа первого порядка). У нас был ещё один пример с proxy:

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

dt :: (Nat n, Fractional a) => Stream n a -> a

dt xs = 1 / (fromIntegral $ toInt $ proxy xs)

where proxy :: Stream n a -> n

proxy = undefined

В этом случае мы пишем:

{-# Language ScopedTypeVariables #-}

...

dt :: forall n. (Nat n, Fractional a) => Stream n a -> a

dt xs = 1 / (fromIntegral $ toInt (undefined :: n))

Обратите внимение на появление forall в определении типа. Попробуйте скомпилировать пример без

него или переместите его в другое место. Во многих случаях применения этого рсширения можно избежать

с помощью стандартной функции asTypeOf, посмотрим на определение из Prelude:

asTypeOf :: a -> a -> a

asTypeOf x y = x

Фактически это функция const, оба типа которой одинаковы. Она часто используется в инфиксной форме

для фиксации типа первого аргумента:

q = f $ x ‘asTypeOf‘ var

Получается очень наглядно, словно это предложение обычного языка.

И другие удобства и украшения

Стоит упомянуть несколько расширений. Они лёгкие для понимания, в основном служат украшению

записи или для сокращения рутинного кода.

Директива deriving может использоваться только с несколькими стандартными классами, но если мы

определили тип-обёртку через newtype или просто синоним, то мы можем очень просто определить новый

тип экземпляром любого класса, который доступен завёрнутому типу. Как раз для этого существует расши-

рение GeneralizedNewtypeDeriving:

newtype MyDouble = MyDouble Double

deriving (Show, Eq, Enum, Ord, Num, Fractional, Floating)

Мы говорили о том, что обычные числа в Haskell перегружены, иногда возникает необходимость в пе-

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

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

1С: Бухгалтерия 8 с нуля
1С: Бухгалтерия 8 с нуля

Книга содержит полное описание приемов и методов работы с программой 1С:Бухгалтерия 8. Рассматривается автоматизация всех основных участков бухгалтерии: учет наличных и безналичных денежных средств, основных средств и НМА, прихода и расхода товарно-материальных ценностей, зарплаты, производства. Описано, как вводить исходные данные, заполнять справочники и каталоги, работать с первичными документами, проводить их по учету, формировать разнообразные отчеты, выводить данные на печать, настраивать программу и использовать ее сервисные функции. Каждый урок содержит подробное описание рассматриваемой темы с детальным разбором и иллюстрированием всех этапов.Для широкого круга пользователей.

Алексей Анатольевич Гладкий

Программирование, программы, базы данных / Программное обеспечение / Бухучет и аудит / Финансы и бизнес / Книги по IT / Словари и Энциклопедии
1С: Управление торговлей 8.2
1С: Управление торговлей 8.2

Современные торговые предприятия предлагают своим клиентам широчайший ассортимент товаров, который исчисляется тысячами и десятками тысяч наименований. Причем многие позиции могут реализовываться на разных условиях: предоплата, отсрочка платежи, скидка, наценка, объем партии, и т.д. Клиенты зачастую делятся на категории – VIP-клиент, обычный клиент, постоянный клиент, мелкооптовый клиент, и т.д. Товарные позиции могут комплектоваться и разукомплектовываться, многие товары подлежат обязательной сертификации и гигиеническим исследованиям, некондиционные позиции необходимо списывать, на складах периодически должна проводиться инвентаризация, каждая компания должна иметь свою маркетинговую политику и т.д., вообщем – современное торговое предприятие представляет живой организм, находящийся в постоянном движении.Очевидно, что вся эта кипучая деятельность требует автоматизации. Для решения этой задачи существуют специальные программные средства, и в этой книге мы познакомим вам с самым популярным продуктом, предназначенным для автоматизации деятельности торгового предприятия – «1С Управление торговлей», которое реализовано на новейшей технологической платформе версии 1С 8.2.

Алексей Анатольевич Гладкий

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