-
Notifications
You must be signed in to change notification settings - Fork 10
/
lecture2.tex
297 lines (278 loc) · 15.8 KB
/
lecture2.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
\documentclass[10pt]{beamer}
\input{lecture_preamble.tex}
\title{Лекция 2: основы синтаксиса (продолжение)}
\begin{document}
\begin{frame}[plain]
\maketitle
\end{frame}
\begin{frame}[fragile]
\frametitle{Существенные отступы}
\begin{itemize}
\item Вспомним виденное раньше
\begin{haskell}
let {x :: Integer; x = 2}
\end{haskell}
По правилам Haskell можно написать это же без фигурных скобок и точки с запятой:
\begin{haskell}
let x :: Integer
x = 2
\end{haskell}
\item Если два выражения (или других фрагмента кода) относятся к одному уровню одного блока, то они должны иметь одинаковый отступ (начинаться в одном и том же столбце).
\item Фрагмент кода, являющийся частью другого, должен иметь отступ больше той строки, где тот начинается.
\item Но не обязательно больше начала его самого.
\end{itemize}
\end{frame}
\begin{frame}[fragile]
\frametitle{Правило преобразования отступов}
\begin{itemize}
\item Если после ключевых слов \haskinline|where|, \haskinline|let|, \haskinline|do|, \haskinline|of| нет открывающей фигурной скобки \haskinline|{|\footnote{Если она есть, то правило перестаёт действовать до соответствующей ей закрывающей.}, то такая скобка вставляется и начинается новый блок.
\item Его базовый отступ "--- столбец, содержащий следующий непробельный символ.
\item После этого каждая строка, которая начинается:
\begin{itemize}
\item в том же столбце: относится к этому блоку и перед ней ставится \haskinline|;|
\item правее: продолжает предыдущую, перед ней ничего не ставится.
\item левее: блок закончился, перед ней ставится \haskinline|}| (одновременно может закончиться несколько блоков).
\end{itemize}
\end{itemize}
\end{frame}
\begin{frame}[fragile]
\frametitle{Условные выражения}
\begin{itemize}
\item В любом языке нужна возможность выдать результат в зависимости от какого-то условия.
\item В Haskell это выражения \haskinline|if| и \haskinline|case|.
\item Синтаксис \haskinline|if|:
\begin{haskell}
if условие then выражение1 else выражение2
\end{haskell}
Варианта без \haskinline|else| нет.
\item Многострочно пишется так:
\begin{haskell}
if условие
then выражение1
else выражение2
\end{haskell}
\item Тип \haskinline|условия| обязательно \haskinline|Bool|.
\item Типы \haskinline|выражения1| и \haskinline|выражения2| должны совпадать.
\item Это же и тип всего \haskinline|if ... then ... else ...|
\item Ближе к \haskinline|?:|, чем к \haskinline|if| в C-подобных языках.
\end{itemize}
\end{frame}
\begin{frame}[fragile]
\frametitle{Сопоставление с образцом}
\begin{itemize}
\item Синтаксис \haskinline|case|: \\
\begin{haskell}
case выражение of
образец1 -> выражение1
образец2 -> выражение2
\end{haskell}
\item Образец это \enquote{форма} для значений типа, которая может содержать несвязанные переменные. Для конкретного значения он либо подходит (и связывает эти переменные), либо не подходит.
\item Процедура вычисления:
\begin{itemize}
\item Вычислить значение \haskinline|выражения|.
\item Сопоставить его с каждым образцом по очереди.
\item Если первым подошёл \haskinline|образецN|, вычислить \haskinline|выражениеN| и вернуть его значение.
\item Если ни один не подошёл, выкинуть ошибку.
\end{itemize}
\end{itemize}
\end{frame}
\begin{frame}[fragile]
\frametitle{Образцы для известных нам типов}
\begin{itemize}
\item \haskinline|True| и \haskinline|False| "--- образцы для \haskinline|Bool|. Они подходят, только если значение совпадает с ними.
\item Аналогично \haskinline|LT|, \haskinline|EQ| и \haskinline|GT| для \haskinline|Ordering|, а числовые литералы для числовых типов.
\item Переменная "--- образец для любого типа, который подходит для любого значения.
\begin{itemize}
\item В Haskell все переменные в образцах \enquote{свежие} и перекрывают видимые снаружи переменные с тем же названием.
\end{itemize}
\item \haskinline|_| тоже подходит для любого значения любого типа и означает, что это значение не важно.
\item Образцы похожи на выражения, но ими не являются: это новая синтаксическая категория!
\end{itemize}
\end{frame}
\begin{frame}[fragile]
\frametitle{Связь \haskinline|case| и \haskinline|if|}
\begin{itemize}
\item Пример:
\begin{haskell}
if условие
then выражение1
else выражение2
\end{haskell}
это ровно то же самое, что
\begin{haskell}
case условие of
True -> выражение1
False -> выражение2
\end{haskell}
или
\begin{haskell}
case условие of
True -> выражение1
_ -> выражение2
\end{haskell}
\end{itemize}
\end{frame}
\begin{frame}[fragile]
\frametitle{Охраняющие условия}
\begin{itemize}
\item У каждого образца могут быть дополнительные условия (зависящие от его переменных):
\begin{haskell}
образец
| условие1 -> выражение1
| условие2 -> выражение2
\end{haskell}
При удачном сопоставлении они проверяются по порядку. Если все оказались ложны, сопоставление переходит к следующему образцу.
\item Последнее условие часто \haskinline|otherwise| (синоним \haskinline|True|), тогда хотя бы одно условие точно истинно.
\item Чтобы сравнить переменную в образце с внешней, нужно использовать \haskinline|==| в условии:
\begin{haskell}
case foo x of
y | y == x -> ... -- не то же, что x -> ...
_ -> ...
\end{haskell}
\end{itemize}
\end{frame}
\begin{frame}[fragile]
\frametitle{Многоветвенные \haskinline|if|}
\begin{itemize}
\item Цепочка \haskinline|if ... else if ...|, оформленная по правилам отступа, напоминает лестницу:
\begin{haskell}
if условие1
then результат1
else if условие2
then результат2
else результат3
\end{haskell}
\item С расширением \haskinline|MultiWayIf| можем написать:
\begin{haskell}
if | условие1 -> результат1
| условие2 -> результат2
| otherwise -> результат3
\end{haskell}
\item Это можно сделать и с \haskinline|case| без расширений: \only<1>{как?} \pause
\begin{haskell}
case () of _ | условие1 -> ...
\end{haskell}
\end{itemize}
\end{frame}
\begin{frame}[fragile]
\frametitle{Определение функций по случаям}
\begin{itemize}
\item Часто тело функции "--- \haskinline|case| по аргументу, например.
\begin{haskell}
not x = case x of
True -> False
False -> True
\end{haskell}
\item Такие функции можно записать несколькими равенствами, по одному на ветвь \haskinline|case|:
\begin{haskell}
not True = False
not False = True
\end{haskell}
\item Это работает и с охранными условиями:
\begin{haskell}
not x | x = False
| otherwise = True
\end{haskell}
\item и для нескольких параметров:
\begin{haskell}
nand True True = False
nand _ _ = True
\end{haskell}
\end{itemize}
\end{frame}
\begin{frame}[fragile]
\frametitle{Локальные определения: \haskinline|let|}
\begin{itemize}
\item Локальные определения дают две выгоды:
\begin{itemize}
\item Более читаемый код.
\item Избавление от повторяющихся вычислений.
\end{itemize}
\item В Haskell два способа их задать: \haskinline|let| и \haskinline|where|.
\item Синтаксис \haskinline|let|:
\begin{haskell}
let переменная1 = выражение1
функция2 x = выражение2
...
in выражение3
\end{haskell}
\item Всё \haskinline|let ... in ...| "--- выражение.
\item Первое проявление ленивости: будут вычислены только те переменные, которые понадобятся для вычисления \haskinline|выражения3|.
\end{itemize}
\end{frame}
\begin{frame}[fragile]
\frametitle{Локальные определения: \haskinline|where|}
\begin{itemize}
\item Синтаксис \haskinline|where|:
\begin{haskell}
функция образец1 | условие1 = выражение3
| условие2 = выражение4
where переменная1 = выражение1
функция2 x = выражение2
\end{haskell}
\item Видимы в условиях и в правых частях (но не для других образцов).
\item Можно применить только к определениям.
\item В том числе к локальным.
\end{itemize}
\end{frame}
\begin{frame}[fragile]
\frametitle{Модули}
\begin{itemize}
\item Программа на Haskell состоит из модулей.
\item Модуль \haskinline|Модуль| (названия с заглавной буквы) определяется в файле \haskinline|Модуль.hs|:
\begin{itemize}
\item Для названия вида \haskinline|A.B.C| будет файл \ghcinline|A/B/C.hs|.
\end{itemize}
\begin{haskell}
module Модуль(функция1) where
import ...
функция1 :: ...
функция1 x = ...
\end{haskell}
\item \haskinline|функция1| экспортирована, она доступна другим модулям и GHCi. Все остальные "--- нет.
\item Можно экспортировать всё, опустив список экспортов (включая скобки).
\end{itemize}
\end{frame}
\begin{frame}[fragile]
\frametitle{Импорт из другого модуля}
\begin{itemize}
\item У директивы \haskinline|import| есть много вариантов.
\item По адресу \url{https://wiki.haskell.org/Import} есть полный перечень.
\item Нам пока достаточно простейших:
\item \begin{haskell}
import Модуль(функция1, функция2)
\end{haskell}
импортирует конкретные функции.
\item \begin{haskell}
import Модуль
\end{haskell}
импортирует всё, что возможно.
\item Импортированные функции доступны как \haskinline|функция| и как \haskinline|Модуль.функция|.
\end{itemize}
\end{frame}
\begin{frame}[fragile]
\frametitle{Загрузка модуля}
\begin{itemize}
\item В GHCi можно скомпилировать и загрузить модуль (вместе с зависимостями) командой \haskinline|:load Модуль|. Подробности в документации:
\begin{itemize}
\item \href{http://downloads.haskell.org/~ghc/latest/docs/html/users_guide/ghci.html#ghci-load-scope}{The effect of :load on what is in scope}
\end{itemize}
\item Под Windows можно просто дважды щёлкнуть на файл или нажать Enter в Проводнике.
\item Потом при изменениях файла повторить \haskinline|:load| или сделать \haskinline|:reload|.
\item Многие редакторы позволяют это автоматизировать.
\end{itemize}
\end{frame}
\begin{frame}[fragile]
\frametitle{Stack}
\begin{itemize}
\item Для сборки лабораторных мы используем \href{https://docs.haskellstack.org/en/stable/#quick-start-guide}{Stack}.
\item Ещё одна распространённая система сборки: \href{https://cabal.readthedocs.io/}{Cabal}.
\item Обе ставятся вместе с Haskell через \ghcinline|ghcup|.
\item Основные команды можно посмотреть в документации, но нам пока достаточно
\begin{itemize}
\item \ghcinline|stack repl| для запуска GHCi с загруженными файлами проекта.
\item \ghcinline|stack test| для запуска тестов.
\end{itemize}
\end{itemize}
\end{frame}
\end{document}