forked from alexeyr/miet-haskell-course
-
Notifications
You must be signed in to change notification settings - Fork 0
/
lecture3.tex
386 lines (365 loc) · 20.2 KB
/
lecture3.tex
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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
\documentclass[10pt]{beamer}
\input{lecture_preamble.tex}
\title{Лекция 3: типы данных}
\begin{document}
\begin{frame}[plain]
\maketitle
\end{frame}
\begin{frame}[fragile]
\frametitle{Типы-\enquote{перечисления}}
\begin{itemize}
\item Тип \haskinline|Bool| не встроен в язык, а определён в стандартной библиотеке:
\begin{haskell}
data Bool = False | True deriving (...)
\end{haskell}
\item Ключевое слово \haskinline|data| начинает определение.
\item \haskinline|Bool| "--- название типа.
\item \haskinline|False| и \haskinline|True| называются конструкторами данных.
\item Читаем как \enquote{У типа \haskinline|Bool| есть ровно два значения: \haskinline|False| и \haskinline|True|.}
\only<1> { \item О \haskinline|deriving| позже. }
\pause
\item Так определяется любой тип с фиксированным перечнем значений (как \haskinline|enum| в других языках):
\begin{haskell}
data Weekday = Monday | Tuesday | ...
data FileMode = Read | Write | Append | ...
\end{haskell}
\item Какие образцы у этих типов (кроме переменных и \haskinline|_|)? \pause
\item Каждое значение (конструктор) "--- образец.
\end{itemize}
\end{frame}
\begin{frame}[fragile]
\frametitle{Типы-\enquote{структуры}}
\begin{itemize}
\item \haskinline|data| также используется для аналогов структур: типов с несколькими полями. Например,
\begin{haskell}
data Point = Point Double Double
\end{haskell}
\item Первое \haskinline|Point| "--- название типа.
\item Второе "--- конструктора.
\item Когда конструктор один, названия обычно совпадают. Всегда по контексту можно определить, что из них имеется в виду.
\item Образцы этого типа \pause это\\
\begin{haskell}
Point образец_Double образец_Double
\end{haskell}
\item Зададим значение типа \haskinline|Point| и функцию на них:
\begin{haskell}
ghci> origin = Point 0 0
ghci> xCoord (Point x _) = x
ghci> xCoord origin
0.0
\end{haskell}
\end{itemize}
\end{frame}
\begin{frame}[fragile]
\frametitle{Типы-\enquote{структуры}}
\begin{itemize}
\item Конструктор является функцией
\begin{haskell}
ghci> :t Point !\pause!
Point :: Double -> Double -> Point
\end{haskell}
\item Мы можем дать полям конструктора имена. Это:\hypertarget{rec1}{}
\begin{itemize}
\item Документирует их смысл.
\item Определяет функции, возвращающие их значения.
\end{itemize}
\begin{haskell}
ghci> data Point = Point { xCoord :: Double, yCoord :: Double } deriving Show
ghci> :t yCoord
yCoord :: Point -> Double
ghci> yCoord (Point 0 (-1))
-1.0
\end{haskell}
\item Особенно полезно, когда полей много, и только по типу не понять, какое где.
\item Если успеем, вернёмся к записям в конце лекции. \hyperlink{rec2}{\beamergotobutton{Синтаксис записей}}
\end{itemize}
\end{frame}
\begin{frame}[fragile]
\frametitle{Алгебраические типы: общий случай}
\begin{itemize}
\item Может быть несколько конструкторов с полями:
\begin{haskell}
data IpAddress = IPv4 String | IPv6 String
\end{haskell}
\item Создание значения:
\begin{haskell}
ghci> googleDns = IPv4 "8.8.8.8"
\end{haskell}
\item Образцы: \haskinline|IPv4 образец_String| и \haskinline|IPv6 образец_String|.
\item Функции часто определяются по уравнению на каждый конструктор:
\begin{haskell}
asString (IPv4 ip4) = ip4
asString (IPv6 ip6) = ip6
\end{haskell}
\pause
\item Но не обязательно:
\begin{haskell}
isLocalhost (IPv4 "127.0.0.1") = True
isLocalhost (IPv6 "0:0:0:0:0:0:0:1") = True
isLocalhost _ = False
\end{haskell}
\pause
\item Ещё пример: \haskinline!data ImageFormat = JPG | PNG | IF String!
\end{itemize}
\end{frame}
\begin{frame}[fragile]
\frametitle{Типы с параметрами (полиморфные)}
\begin{itemize}
\item В определении типа могут быть параметры.
\item Пример:
\begin{haskell}
data Maybe a = Nothing | Just a
\end{haskell}
\item \haskinline|Maybe| "--- конструктор типа.
\item Вместо \haskinline|a| можно подставить любой тип, и получить снова тип: \haskinline|Maybe Int|, \haskinline|Maybe Bool|.
\item Примеры их значений: \haskinline|Just 1|, \haskinline|Nothing|.
\item В Haskell нет отношения \enquote{Надтип "--- подтип}, вместо него \enquote{Более общий "--- частный случай}.
\item \haskinline|Maybe a| описывает необязательное значение типа \haskinline|a|.
\pause
\item Ещё часто встречающийся тип:
\begin{haskell}
data Either a b = Left a | Right b
\end{haskell}
\item Могут быть сколь угодно сложные комбинации.
\item Например, \haskinline|Either (Either Int Char) (Maybe Bool)| со значением \haskinline|Right Nothing|.
\end{itemize}
\end{frame}
\begin{frame}[fragile]
\frametitle{\haskinline|Maybe| и \haskinline|null|}
\begin{itemize}
\item \haskinline|Nothing| похоже на \haskinline|null| в C-подобных языках, но явно прописано в типах.
\item Не нужно для каждой функции документировать, как работает с \haskinline|null|, достаточно посмотрить на наличие \haskinline|Maybe| в сигнатуре.
\item И поэтому нет возможного несовпадения реализации с документацией.
\item Нет типов, у которых \haskinline|null| нет и с отсутствием значения приходится что-то придумывать.
\item Есть \haskinline|Maybe (Maybe a)| (бывает полезно).
\pause
\item \enquote{\itshape \haskinline|null| "--- моя ошибка ценой в миллиард долларов... Я планировал сделать любое использование ссылок [в Algol] абсолютно безопасным. Но не удержался от соблазна добавить \haskinline|null| просто потому, что его было так легко реализовать.} (\href{https://ru.wikipedia.org/wiki/Хоар,_Чарльз_Энтони_Ричард}{Тони Хоар}, \href{https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare}{QCon 2009})
\end{itemize}
\end{frame}
\begin{frame}[fragile]
\frametitle{Кортежи}
\begin{itemize}
\item Мы могли бы определить типы
\begin{haskell}
data Pair a b = Pair a b
data Triple a b c = Triple a b c
...
\end{haskell}
(заметьте разницу смыслов \haskinline|Pair|, \haskinline|a| и \haskinline|b| в левой и правой частях!)
\item Это универсальные произведения.
\pause
\item Но определять их не нужно, так как такие типы уже есть, со специальным синтаксисом:\\
\haskinline|(a, b)| (или \haskinline|(,) a b|),\\ \haskinline|(a, b, c)| (или \haskinline|(,,) a b c|) и т.д.
\item Синтаксис значений и образцов для них аналогичный:
\haskinline|(True, Just 'a') :: |\pause\haskinline|(Bool, Maybe Char)|.
\item Максимальный размер 62.
\end{itemize}
\end{frame}
\begin{frame}[fragile]
\frametitle{Рекурсивные типы и списки}
\begin{itemize}
\item Тип может быть рекурсивным, то есть в правой части используется тот тип, который мы определяем.
\item Самый важный пример такого типа: списки.
Мы могли бы их определить как
\begin{haskell}
data List a = Nil | Cons a (List a)
\end{haskell}
Вместо этого они имеют специальный синтаксис, как и кортежи, как будто их определение
\begin{haskell}
data [a] = [] | a : [a]
\end{haskell}
\item Любой \haskinline|[a]| или пуст, или имеет \emph{голову} типа \haskinline|a| и \emph{хвост} типа \haskinline|[a]|.
\item \haskinline|:| правоассоциативно: \haskinline|1 : 2 : 4 : []| читается \haskinline|1 : (2 : (4 : []))|.
\item Сокращение (относится и к значениям и к образцам):\\
\haskinline|[x, y, z]| означает \haskinline|x : y : z : []|, а \haskinline|[x]| "--- \haskinline|x : []|.
\end{itemize}
\end{frame}
\begin{frame}[fragile]
\frametitle{Синонимы типов}
\begin{itemize}
\item На этом с \haskinline|data| разобрались (пока что).
\pause
\item Есть ещё два вида определения типов.
\item \haskinline|type| объявляет синоним типа "--- другое имя для существующего типа.
\item В стандартной библиотеке:
\begin{haskell}
type String = [Char]
type FilePath = String
\end{haskell}
\item Оба определения теперь считаются плохими! Для \haskinline|String| подробности в будущих лекциях.
\pause
\item Для \haskinline|FilePath| дело в том, что осмысленные операции над путями не те, что над строками:
\begin{haskell}
ghci> isPrefixOf
("C:\\Program Files" :: FilePath)
("C:\\Program Files (x86)" :: FilePath)
True
\end{haskell}
\end{itemize}
\end{frame}
\begin{frame}[fragile]
\frametitle{\haskinline|newtype|}
\begin{itemize}
\item \haskinline|newtype| как раз позволяет определить тип с тем же представлением, что существующий, но с другими операциями. Т.е. разумнее было бы
\begin{haskell}
newtype FilePath = FilePath String
\end{haskell}
\item Или \haskinline|FilePath [String]| со списком директорий.
\item Ещё можно рассмотреть
\begin{haskell}
newtype EMail = EMail String
\end{haskell}
\item У \haskinline|type| конструкторов значений нет, у \haskinline|newtype| "--- всегда один с одним полем.
\item Между \haskinline|newtype| и \haskinline|data| с одним конструктором с одним полем есть разница из-за ленивости.
\item Пока из них предпочитаем первое.
\end{itemize}
\end{frame}
\begin{frame}[fragile]
\frametitle{Полиморфные функции и значения}
\begin{itemize}
\item Какой тип функции
\begin{haskell}
ghci> id x = x
ghci> :t id !\pause!
id :: a -> a
\end{haskell}
\item Более явно: \haskinline|id :: forall a. a -> a|.
\item Аналогично
\begin{haskell}
ghci> foo (_, x, y) = (y, x, x)
ghci> :t foo !\pause!
id :: (a1, c, a2) -> (a2, c, c)
-- или (a, b, c) -> (c, b, b)
\end{haskell}
\item \haskinline|Just :: a -> Maybe a|
\item И \haskinline|[] :: [a]| "--- полиморфные значения не обязательно функции.
\item Параметр типа можно передать явно:
\begin{haskell}
ghci> :t id @Int
id @Int :: Int -> Int
\end{haskell}
\end{itemize}
\end{frame}
\begin{frame}[fragile]
\frametitle{Область видимости переменных типа}
\begin{itemize}
\item По умолчанию область видимости переменной типа "--- сигнатура, где она появилась. Например, в
\begin{haskell}
g :: [a] -> [a]
g (x:xs) = xs ++ [x :: a]
\end{haskell}
\pause
две переменные \haskinline|a| "--- разные и читаются как
\begin{haskell}
g :: forall a. [a] -> [a]
g (x:xs) = xs ++ [x :: forall a. a]
\end{haskell}
\pause
(так что функция не скомпилируется).
\pause
\item Но можно написать так:
\begin{haskell}
g :: forall a. [a] -> [a]
g (x:xs) = xs ++ [x :: a]
\end{haskell}
\item Точные правила можно найти в \href{https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/scoped_type_variables.html}{Lexically scoped type variables}.
\end{itemize}
\end{frame}
\begin{frame}[fragile]
\frametitle{Структурно-рекурсивные функции}
\begin{itemize}
\item Функции над рекурсивными типами часто тоже рекурсивны (или вызывают рекурсивные).
\begin{haskell}
length :: [a] -> Int
length [] = 0
length (_ : xs) = length xs + 1
\end{haskell}
\item Натуральные числа "--- \enquote{почётный} рекурсивный тип.
\pause
\item Натуральное число это либо $0$, либо $n + 1$, где $n$ "--- натуральное число.
\item Соответственно, функции над ними тоже определяются рекурсивно:
\begin{haskell}
replicate _ 0 = []
replicate x n = x : replicate x (n - 1)
\end{haskell}
\item Раньше \haskinline|n + 1| (и вообще \haskinline|+ константа|) было образцом, в Haskell 2010 их исключили.
\end{itemize}
\end{frame}
\begin{frame}[fragile]
\frametitle{Алгебраические типы}
\begin{itemize}
\item Несколько полей у конструктора соответствуют операции декартова произведения в теории множеств.
\onslide<3->{\begin{itemize}
\item В логике конъюнкции, а в алгебре произведению.
\end{itemize}}
\item А несколько конструкторов\onslide<1>{?}\onslide<2->{операции дизъюнктного объединения $A_1 \sqcup A_2 \sqcup \ldots = \{1\} \times A_1 \cup \{2\} \times A_2 \cup \ldots$}
\onslide<3->{
\begin{itemize}
\item В логике дизъюнкции, а в алгебре сумме.
\end{itemize}
}
\onslide<2->{
\item Сравните помеченное объединение с непомеченным \haskinline|union| в C/C++.
\item И с \haskinline|std::variant|.
}
\onslide<3->{\item Тип функций "--- множество функций в теории множеств, импликация в логике и возведение в степень в алгебре.}
\onslide<4->{\item Обычные законы алгебры выполняются для типов с точностью до изоморфизма, как и для множеств.}
% TODO переделать в таблицу
\end{itemize}
\end{frame}
\begin{frame}[fragile]
\frametitle{Типы и модули}
\begin{itemize}
\item Типы можно импортировать и экспортировать.
\item \haskinline|НазваниеТипа(..)| в списке экспорта указывает, что конструкторы тоже включены в список.
\item Просто \haskinline|НазваниеТипа| экспортирует только тип без конструкторов.
\item Можно также перечислить явно, какие именно конструкторы экспортируются.
\item Для импорта всё аналогично.
\end{itemize}
\end{frame}
\begin{frame}[fragile]
\frametitle{Записи}\hypertarget{rec2}{}
\hyperlink{rec1}{\beamerreturnbutton{К определению записей}}
\begin{itemize}
\item
\begin{haskellsmall}
ghci> aPoint = Point 0 (-1)
ghci> aPoint { xCoord = 1 } !\pause!
Point {xCoord = 1.0, yCoord = -1.0}
ghci> aPoint !\pause!
Point {xCoord = 0.0, yCoord = -1.0} !\pause!
ghci> Point {xCoord = x'} = it
ghci> x' !\pause!
0.0 !\pause!
ghci> :set -XOverloadedRecordDot -XNamedFieldPuns -XRecordWildCards
ghci> aPoint.xCoord
0.0 !\pause!
ghci> :set
ghci> f (Point { xCoord, yCoord }) = xCoord + yCoord
ghci> g (Point { .. }) = xCoord + yCoord
\end{haskellsmall}
\item Два последних образца "--- сокращения\\ \haskinline|Point { xCoord = xCoord, yCoord = yCoord }|.
\end{itemize}
\end{frame}
\begin{frame}[fragile]
\frametitle{Записи: минусы}\hypertarget{rec2}{}
\hyperlink{rec1}{\beamerreturnbutton{К определению записей}}
\begin{itemize}
\item
\begin{haskell}
ghci> Point { xCoord = 0 }
\end{haskell}
\pause
без \haskinline|yCoord| компилируется только с предупреждением (можно сделать ошибкой с помощью опций компилятора).
\pause
\item Записи можно применять для типов с несколькими конструкторами, но это создаёт частичные (не всюду определённые) функции.
\pause
\item Одинаковые названия полей у двух записей ведут к проблемам.
\pause
\item Получается две функции с одинаковыми названиями.
\item \haskinline|DisambiguateRecordFields| и \haskinline|DuplicateRecordFields| улучшают ситуацию.
\pause
\item Есть ещё решения на уровне библиотек.
\end{itemize}
\end{frame}
\end{document}