haskell-notes

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 перегружены, иногда возникает необходимость в пе-

регруженных строках, как раз для этого существует расширение OverloadedStrings. При этом за обычной

записью строк может скрываться любой тип из класса:

class IsString a where

fromString :: String -> a

Расширение TypeOperators позволяет определять инфиксные имена не только для конструкторов типов,

но и для самих типов, синонимов типов и даже классов:

data a :+: b = Left a | Right b

17.3 Краткое содержание

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

является полигоном для испытания самых разнообразных идей. Это экспериментальный язык. Но в практиче-

ских целях в 1998 году был зафиксирован стандарт языка, его обычно называют Haskell98. Любое расшире-

ние подключается с помощью специальной прагмы Language. Новый стандарт Haskell Prime включит в себя

наиболее устоявшиеся расширения. Также мы рассмотрели несколько полезных классов и синтаксических

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

17.4 Упражнения

Это была справочная глава, присмотритесь к рассмотренным возможностям и подумайте какие нужны

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

вам в чужом коде или в библиотеках.

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

Глава 18

Средства разработки

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

устанавливать и создавать библиотеки, писать документацию.

18.1 Пакеты

В Haskell есть ещё один уровень организации данных, мы можем объединять модули в пакеты (package).

Также как и модули пакеты могут зависеть от других пакетов, если они пользуются модулями их этих па-

кетов. Одним пакетом мы уже пользовались и довольно часто, это пакет base, который содержит все стан-

дартные модули, например такие как Prelude, Control.Applicative или Data.Function. Для создания и

установки пакетов существует приложение cabal. Оно определяет протокол организации и распростране-

ния модулей Haskell.

Создание пакетов

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

хранятся в директории с именем src. Для того чтобы превратить набор модулей в пакет, нам необходимо

Страницы: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162