.title-slide
# Опциональная типизация в динамических языках .right[Андрей Власовских] .right[JetBrains] .right[http://pirx.ru/] --- # Статические и динамические языки * Сосуществуют с 1960-х гг. * Статические * C, Java, C++, Objective-C, C#, ... * Динамические * PHP, Python, JavaScript, Ruby, Erlang, ... --- # Текущее соотношение * Статическая типизация популярнее.ref[[1]] * Статическая: 70% * Динамическая: 30% * Предмет предпочтений * А также споров, flame wars, etc. .footnote[[1] По данным [TIOBE Index](http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html)] --- # Плюсы динамической типизации * `<Мнение>` Динамическая лучше! `Мнение>` * Больше свободы, многое выражается очень кратко * Сравните ощущения от Python и Ruby с Java и C++ * Опустим функциональные языки * Семейство ML, OCaml, Haskell, ... * Типы выводятся, код краткий * Сложность языков, их системы типов — научная область --- # Плюсы: эксперименты * Эксперименты с не полностью корректным кодом import urllib, json TIMELINE_URL = ... def load_tweets(username): fd = urllib.urlopen(TIMELINE_URL.format(username)) return json.load(fd) def is_reply(tweet): return tweet.text.startswith('@') --- # Плюсы: тестирование * Сильнее желание писать тесты * Делать API проще для тестирования * Проверять логику, а не типы import mylib, unittest class TweetsTest(unittest.TestCase): def test_reply_to(self): tweet = mylib.make_tweet(...) self.assertTrue(mylib.is_reply(tweet)) --- # Будем объективными * Динамические языки имеют много минусов * Производительность * Поддержка в IDE * Ошибки типизации --- # Минусы: производительность * «Всё — это объект» * Функции должны принимать любое значение * Даже числа являются тяжёлыми объектами * Позднее связывание имён * Парсер JSON * С: 11 мс, Python: 957 мс (в 90 раз) * Ограничен CPU, а не I/O --- # Минусы: поддержка в IDE * Меньше подсказок, проверок, рефакторингов * Не хватает информации для анализа class MyTest(TestCase): def test_foo(self, x): self. # OK: assertEqual, setUp, tearDown, ... x. # ??? --- # Минусы: ошибки типизации при выполнении * Статические языки исключают целый класс ошибок def f(): x = 'this is a string' x.load_tweets() --- # Минусы: документация * Без типа надо искать информацию * Читать руководство, исходный код def load_tweets(stream, settings, no_replies): ... * Тип может сказать о многом .java List
loadTweets(InputStream stream, Settings settings, boolean noReplies) throws IOException { ... } --- # Проблема * Как уменьшить недостатки динамических языков * Не отказываясь от их преимуществ * Причина недостатков * Не хватает статической информации * Инструменты не могут понять, что делается в исходном коде --- # Опциональная типизация * Динамическая, а **когда надо** — статическая * Когда надо * Повысить производительность узкого места * Сделать библиотеку более корректной, документированной * Когда не надо * В экспериментах и прототипах * В пользовательском коде (как правило) --- # Инструменты для разных языков * Компиляторы * Производительность * Средства статического анализа * Проверка типов * IDE * Подсказки, инспекции кода, рефакторинги --- # IDE от JetBrains *
PyCharm * Python, поддержка Django *
RubyMine * Ruby, поддержка Ruby on Rails *
PhpStorm * PHP, HTML, JavaScript *
WebStorm * JavaScript --- # План * Динамическая типизация * Преимущества * Недостатки * Опциональная типизация * .current[Примеры применения опциональной типизации] * .next[Семантика языка: Cython] * .next[Аннотации в языке: Erlang, Dart] * .next[Документация: Sphinx] * .next[Вывод типов: Python] --- .title-slide # Типы в семантике языка --- # Пример: Типы в языке Cython * Диалект языка Python * У интерпретатора Python есть API на C * Cython транслирует код Python в вызовы C API * Язык Cython — Python с типами C --- # Cython: без типов * Факториал на чистом Python def fact(x): res = 1 for i in range(1, x + 1): res *= i return res --- # Cython: трансляция в Python C API .cpp static PyObject *__pyx_pf_3tmp_1fact(PyObject *self, PyObject *v_x) { PyObject *v_res, *v_i, *int_1, *t_1, *t_2; Py_ssize_t t_3 = 0; ... v_res = int_1; t_1 = PyNumber_Add(v_x, int_1); t_2 = PyTuple_New(2); PyTuple_SET_ITEM(t_2, 0, int_1); PyTuple_SET_ITEM(t_2, 1, t_1); t_1 = PyObject_Call(builtin_range, t_2, NULL); for (;;) { if (t_3 >= .strong[PyList_GET_SIZE(t_1)]) break; v_i = .strong[PyList_GET_ITEM(t_1, t_3)]; t_3++; v_res = .strong[PyNumber_InPlaceMultiply(v_res, v_i)]; } return v_res; } --- # Cython: типизация * Факториал над числами `int64_t` ctypedef long long int64_t cdef int64_t fact(int64_t x): cdef int64_t res, i res = 1 for i in range(1, x + 1): res *= i return res --- # Cython: трансляция с типами .cpp static PyObject *__pyx__pf_3tmp_cython_fact(PyObject *self, PyObject *arg_x) { int64_t v_x; v_x = PyInt_AsLongLong(arg_x); return PyLong_FromLongLong(f_3tmp_cython_fact(v_x, 0)); } static int64_t f_3tmp_cython_fact(int64_t v_x) { int64_t v_res, v_i, t_1, t_2; v_res = 1; t_1 = v_x + 1; for (t_2 = 1; t_2 < t_1; t_2 += 1) { v_i = t_2; v_res = v_res * v_i; } return v_res; } --- # Cython: итоги * Сравнение производительности * 1 млн. вызовов `fact(20)` * Python: 20.8 с, Cython: 13.8 с, Cython с типами: 1.5 с * Типы используются для * Поиск эффективной реализации операции * Упрощение конвенции вызова функции * Эффективные конструкции управления --- .title-slide # Аннотации типов в языке --- # Пример: аннотации в Erlang * Встроенные в язык аннотации * Не влияют на выполнение программы * Проверяются синтаксически при компиляции * Инструмент Dialyzer для проверки совместимости типов * Типизирована почти вся стандартная библиотека --- # Erlang: аннотированная функция .erlang -module(file). -spec open(Filename, Modes) -> {ok, IoDevice} | {error, Reason} when Filename :: name(), Modes :: list(mode()), ... -type name() :: string() | atom(). * Использование test(X) -> {ok, Fd1} = file:open("data.txt", [read]), {ok, Fd2} = file:open(X, write). ~~~~~ --- # Erlang: язык описания типов * Язык достаточно развит * Параметризованные типы * Сокрытие реализации: абстрактные типы данных -spec singleton(T) -> list(T). singleton(X) -> [X]. --- # Пример: аннотациии в Dart * Встроенные в язык аннотации * Компилятор выдаёт предупреждения о несоответствии типов * Не влияют на выполнение программы * Режим отладки, когда типы проверяются динамически --- # Dart: аннотированная функция * Неправильные типы не мешают выполнению .javascript class Point { Point(this.x, this.y); var x, y; } main() { Point p = new Point(2, 3); num q = new Point(3, 4); ~~~~~~~~~~~~~~~ print('${p.x}, ${q.x}'); } --- # Аннотации: итоги * Расширения языка, не влияющие на его семантику * Не мешают использовать динамическую природу языка * При желании можно обеспечить корректность части программы * Типы используются для * Проверка совместимости типов инструментами --- .title-slide # Типы в документации --- # Пример: документация в комментариях Python * Инструменты документации Sphinx и Epydoc * Похоже на Javadoc, Doxygen, etc. * Синтаксис для указания типа функции * Тип задаётся произвольной строкой, но есть соглашения --- # Sphinx: документированная функция def ping_n_times(id_addr, n): """ Description of function. :type ip_addr: (int, int, int, int) or string :type n: int :rtype: Stats of float """ return Stats(ping(ip_addr) for _ in range(n)) def f(): stats = ping_n_times((192, 168.9, 1, 1), 10) ~~~~~ stats = ping_n_times('localhost', 10) stats. # OK: average, min, max stats.min() + "ms" ~~~ --- # Sphinx: итоги * IDE для динамических языков * Возможностей меньше, чем для статических * Более полезны, заменяют собой проверки компилятора * Типы используются для * Лучшая документация: исходный код, генерация HTML * Поиск класса для типа и подсказка атрибутов в IDE * Проверка совместимости типов в IDE --- .title-slide # Вывод типов --- # Пример: вывод типов для Python * Тип возвращаемого значения функции можно вычислить статически * Если можно проследить поток данных в функции * Можно вывести типы в 10-30% случаев * Используется в IDE, дополняет анализ типов --- # Вывод типов: возможные варианты * Вычисленный тип: `int or str or Answer or unknown` def f(x): if x < 0: return -1 elif x == 42: return Answer(x) elif x > 42: s1 = 'got {param}'.format(param=x) s2 = s1 return s2 else: return x --- .title-slide # Итоги --- # Введение опциональной типизации * Способы введения * На уровне семантики языка * В аннотациях языка * В документирующих комментариях * Вывод типов * Получаемый эффект * Производительность * Корректность * Поддержка в IDE и документация --- # Заключение * Опциональная типизация полезна * Устраняет часть недостатков динамических языков * Очень полезно для библиотечного кода * Изучайте инструменты для вашего языка * Статические анализаторы * IDE * Опции интерпретаторов --- .center.middle # Вопросы? http://pirx.ru/ @vlasovskikh