52700.fb2 Учебник по Haskell - читать онлайн бесплатно полную версию книги . Страница 291

Учебник по Haskell - читать онлайн бесплатно полную версию книги . Страница 291

ми зависимостями в типах выписывать типы крайне неудобно. Например в библиотеке создания парсеров

Parsec. С этим ограничением приходится писать огромные объявления типов для крохотных выражений.

Что-то вроде:

fun :: (Stream s m t, Show t) => ParsecT s u m a -> ParsecT s u m [a]

fun = g . h (q x) y

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

файла:

{-# Language NoMonomorphismRestriction #-}

Полиморфизм высших порядков

Когда мы говорили об ST нам встретилась функция с необычным типом:

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

Слово forall обозначает для любых. Любой полиморфный тип в Haskell подразумевает, что он определён

для любых типов. Например, когда мы пишем:

reverse :: [a] -> [a]

map

:: (a -> b) -> [a] -> [b]

На самом деле мы пишем:

reverse :: forall a. [a] -> [a]

map

:: forall a b. (a -> b) -> [a] -> [b]

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

По названию слова forall может показаться, что оно несёт в себе много свободы. Оно говорит о том, что

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

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

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

их в новые полиморфные функции (как в 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)