2. Read – Evaluate – Print - Loop
В Emacs используется интерпретатор
NJ/SML
Статическая и динамическая среда
выполнения
Рестарт для зачистки данных в среде
3. val x = e;
Синтаксис:
◦ Ключевые слова val = ;
◦ Переменная x
◦ Выражение e
Поддерживаются вложенные выражения
val z = (x + y) + (y + 2);
4. Синтаксис (как записывается)
Правила проверки типов
◦ Определение типа или ошибка
◦ Примеры типов: int bool unit
Правила вычисления выражения
(применяются после успешной проверки
типа)
◦ Возвращается значение или exception или
бесконечный цикл
Демо...
5. val a = 1
val b = a (* b присваивается 1 *)
val a = 2
Не важно что будет дальше со значением a
Переменной нельзя присвоить значение
повторно, его можно только затенить
6. Синтаксис:
◦ e1 + e2 где e1 и e2 это выражения
Проверка типа:
◦ Если e1 и e2 имеют тип int,
то e1 + e2 тоже будет иметь тип int
Вычисление выражения:
◦ Если e1 вычисляется в v1 и e2 вычисляется в v2,
то e1 + e2 рассчитывается как сложение v1 и v2.
7. Значение – это выражение, которое не
требует вычислений
◦ 8+9 - это не значение
Любое значение – это выражение
Не все выражения являются значениями
Каждое значение «вычисляется в само в
себя» без
• Примеры:
◦ 34, 17, 42 имеет тип int
◦ true, false имеет тип bool
◦ () имеет тип unit
8. Синтаксис: (e1, e2, … en)
Проверка типов: если e1 – имеет тип t1, …
en – tn, то выражение имеет тип t1 *…* tn
Вычисление: e1 – v1, … en – vn, то результат
(v1, … , vn)
9. Синтаксис: #1 e, #2 e, …
Проверка типов: если e имеет тип t1*…*tn,
то #1 e имеет тип t1, …
Вычисление: возвращается
соответствующий элемент кортежа
Допустимы вложенные кортежи
val x1 = ((1,2), (3, (4,4)))
10. Построение списков
◦ [] – пустой список
◦ [v1, v2, …, vN] – список
◦ e1::e2 – добавление элемента e1 в список e2
◦ e1@e2 – слияние списков e1 и e2
Проверка типов
◦ Список содержит элементы одного типа. Пример
int list, bool list, int list list
Вспомогательные функции
◦ null e, hd e, tl e, length e …
11. В сравнении с методом из ООП языков
(Java, C#)
◦ Нет ключевого слова this
◦ Нет return инструкции
◦ Часто в ФЯ применяются рекурсивные вызовы,
например для обхода элементов коллекции
12. Синтаксис: fun x0 (x1 : t1, … , xn : tn) = e
Вычисление: функция – это значение!
Добавляется x0 в динамическую среду
выполнения
Проверка типов: значение функции
определяется автоматически через
выведение типов
Объявляется переменная x0: (t1*t2…*tn)->t
13. Синтаксис: e0 (e1, …, en)
Проверка типов:
– e0 имеет тип (t1 * … * tn) -> t
– e1 - t1, …, en - tn
e0 (e1, … , en) имеет тип t
Вычисляется e0 для fun x0 (x1 : t1, … , xn :
tn) = e
1. Вычисляются значения аргументов v1, …, vn
2. Результат вычисления e. Динамическая среда
расширена значениями x1 : v1, …, xn : vn, а
также есть x0 – для рекурсивных вызовов
15. Синтаксис
◦ Let b1 b2 … bn in e end
bi – это объявление переменной, может
присваиваться выражение, объявляться функция
Проверка типов
◦ Тип всего выражения определяется типом e
Область видимости (scope)
Реиспользование кода (уменьшение багов,
простота разработки, но тяжело вносить
изменения в реиспользуемый код)
16. Рекурсивно заданная функция в своем
определении содержит себя
Рекурсия заменяет циклы в языке ML
Хвостовая рекурсия – дешевый вызов
рекурсии (пример расчета факториала с
простой и хвостовой рекурсией)
◦ Применение переменной аккумулятора
17. t option – тип для любого типа t
Типы options
◦ NONE имеет тип ‘a option
◦ SOME e имеет тип t option, если e имеет тип t
Работа с options
◦ isSome тип ‘a option -> bool (false если
NONE)
◦ valOf тип ‘a option -> ‘a (исключение, если
NONE)
18. Не возможно выполнять операцию
присвоения к переменным, частям
кортежей, элементам списка
Aliasing – доступ к одному и тому же
объекту, используя разные переменные.
Имея 2 функции sort_pair1 и sort_pair2, не
возможно отличить возвращается ли копия
кортежа или alias, в случае выполнения
условия if
19. fun sort_pair1 (pair : int * int) =
if #1 pair < #2 pair
then pair
else (#2 pair, #1 pair)
fun sort_pair2 (pair : int * int) =
if #1 pair < #2 pair
then (#1 pair, #2 pair)
else (#2 pair, #1 pair)
20. Каковы преимущества?
◦ Нет возможности отличить используется ли alias
для объекта или его копия
◦ Нет необходимости думать об использовании
alias. Фокусируемся на других вещах
◦ Можно смело использовать alias, не опасаясь
возможности изменения передаваемого объекта
Эта особенность применяется в
параллельных вычислениях
Пример из Java. В Java поддерживается
изменение значения созданных
переменных
21. Контейнер с именованными полями
Синтаксис: {f1=e1, … , fn=en}, доступ к
полю #f1 {record}, f1 – имя поля.
Кортежи – это синтаксический сахар для
записей вида {1=e1, 2=e2, …, n=en}.
Кортеж (e1, e2, … en), доступ #1 (tuple).
22. Разобрали объявление переменных,
функций
Пример
datatype mytype = TwoInts of int * int
| Str of string
| Pizza
создали новый тип mytype. Тип имеет 3
конструктора TwoInts, Str и Pizza
Создаем конструкторы, которые
производят значения нового типа
23. Для определения какой конструктор
использован для данного экземпляра типа
используются case выражения
Демо с арифметическими выражениями
24. Pattern-matching (англ.)
Используется при объявлении переменной
вместо указания имени переменной
val p = e
Используется при объявлении функции
fun f p = e
Каждая функция принимает только один
аргумент
Пример с суммированием элементов
кортежа
25. Можно указать в конструкторе datatype
использование любого типа
демо с option и бинарным деревом,
суммирование узлов и листьев
26. Объявление: exception <name> of t
Вызов: raise <name> <значение>
Обработка: e1 handle <name>
<параметры> => e2
27. Функции первого класса – могут
использоваться как значения.
Функции высшего порядка - могут
принимать другие функции в качестве
параметра.
Пример f(f(f...f(x))) n раз (n_times)
Разбор типа: fn : ('a -> 'a) * int * 'a -> 'a
28. У анонимной функции нет имени, поэтому
еѐ нельзя вызывать рекурсивно
fn args => e
Пример, double_n_times
29. Область видимости (англ. scope) – это
контекст выполнения программы, где по
имени можно обращаться к переменным
или функциям.
Область видимости в блоке, в функции, в
let-выражении, глобальный, лексический
Lexical scope. Не важно где вызывают
функцию, важно где еѐ объявили
30. Значение функции состоит из двух частей:
◦ Код функции
◦ Среда, которая была на момент определения функции
Эта пара значений называет замыканием
функции
Доступ к этим свойствам значения функции
невозможен в коде
При вызове функции вычисляется код в рамках
среды (которая дополняется аргументами
функции)
Пример работы lexical scope.
31. Тело функции не вычисляется пока функция
не будет вызвана
Тело функции вычисляется каждый раз,
когда функция вызывается
Выражение в определении переменной
вызывается один раз и вычисляется, когда
переменная объявляется.
32. Основные операции со списками filter, map,
fold: f(f(f(acc, x1), x2), x3)
Демо с реализацией и использованием
операций.
33. Идиома – это элементарная конструкция
типичная для одного или нескольких
языков программирования в нашем случае
для функциональных языков
Комбинирование функций (композиция)
Currying
Callback – подпись на события и обратный
вызов функций.
34. SML использует жадную стратегию для
вычислений
fun f(x:int):bool = true;
fun arg () : int = 3;
f(arg()) (* вызовет метод arg, хотя параметр x
не используется *)
Отложенные вычисления реализуются с
помощью обертки выражения анонимной
функцией (thunk).
35. Статическая типизация – контроль за
типами при компиляции до запуска
программы (Pascal, C++, Java, C#)
Динамическая типизация – контроль типов
во время выполнения программы (PHP,
Python, Ruby, Perl, Javascript)
ML – статически типизированный
ML – язык с неявным определением типов.
Применяется выведение типов
36. Выведение типов – процесс определения
типов выражений, переменных, который
приведет к успешной проверке типов.
Задача выведения типов
◦ Установить типы переменных в коде.
◦ Удостовериться, что все выражения проходят
проверку типов.
37. Неизменяемость значений переменных
Использование функций в роли аргумента
Рекурсивные типы данных
Использование рекурсии в функции для
обхода элементов списка
Использование сопоставления шаблонов
Ленивая инициализация
38. Функции первого порядка, closures в C#,
Java, Javascript
Анонимные функции в C#, Javascript
LINQ в C#
Callback в C#, Javascript
Полиморфизм типов (generics)
Неявная типизация, выведение типов в C# и
Javascript
39. Типы и операции, матрица
Разрешение задачи на ООЯП через
выделение типов и интерфейса
Решение на ФЯП через определение
datatype и выделения операций с
перечислением пар и pattern-matching’ом
41. задачи оптимизации,
генетическое программировние,
сложные доказательства
эвристические задачи (например, шахматы)
современные языки программирования
включают в себя различные парадигмы
программирования (multi-paradigm) в том
числе многие идиомы из ФЯ (например, C#,
Javascript).
42. Списки – это тоже datatypes
Вложенные шаблоны, пример zip3,
nondecreasing, multsign sgn = P | N | Z
Синонимы типов
Модули и сигнатуры