Everybody loves grumpy old Clint Eastwood as he tries to beat baseball player scoring software
In the aftermath of a tsunami members of an american family barely do anything at all and end up in the movie claiming they've done the impossible. Also it's the kind of a movie where people just suffer for no reason.
Я давно обещался написать статью о том, как себя показал Haskell в реальных задачах в продакте.
Для тех, кто не уследил — его в начале 2012 пролоббировали и с энтузиазмом начали внедрять программисты в Селектеле. Тогда же я обещал опубликовать отчёт о том, насколько «это всё» можно использовать.
Продакт в коммерческом проекте — это не в маленькая песочница «для себя», не в академический эксперимент в области Computer Science. Это бесконечная борьба за «линию партии», когда вокруг ад, ужас и погибель, а оно всё равно должно рабоать. Int64 в XML-RPC кодируется строкой (потому что int'ы в XML-RPC signed int32), openssl при чтении нескольких сертификатов из файла читает только первый из них, в bool надо писать либо «1», либо «0», но иногда — «2», ибо только так придумали закодировать третий режим — и т.д. и т.п. В этих условиях требования к языку постепенно перерастают в требования к его экосистеме, инфраструктуре, готовности адаптироваться к реальному миру.
Я буду писать о Haskell с позиций product owner'а, менджера проекта, системного администратора, но никак не программиста. Так что не ожидайте от меня задушевных восторгов о том, как изящно через монадки можно сделать семигрупоид и как здорово выводить типы через типы с помощью типов.
С точки зрения менеджера проекта язык программирования оценивается по нескольким метрикам, совершенно отличными от программистских. Для программиста язык и его особенности — едва ли не самая важная вещь, так как именно с ним он проводит большую часть времени. Для остальных членов команды куда важнее происходящее за пределами исходного текста. Сначала это поиск библиотек и подходящих технологий, потом задачи сопровождения, мониторинга, внедрения и отладки.
Начнём с потребительских свойств.
Программы на Haskell быстрее python, php, ruby (и других интерпретируемых языков). Быстрее Erlang/Java (и других vm-based языков). Обычно медленнее Си, хотя я видел несколько случаев, когда компилятор Haskell выдал результат, превосходящий сишный.
Для любых практических применений производительности Haskell — за глаза и за уши.
Главное достоинство по сравнению с питоном (с которого мы постепенно мигрировали) — отличная параллельность исполнения. Никаких GIL'ов, никаких «внешних балансировщиков между воркерами», никакого ада с отладкой гевента.
У Haskell штатные гринлеты и нативное использование тредов операционной системы.
Чаще всего это пофигу, но в нашей конфигурации в некоторых местах было тесно — и минимальный размер исполняемого в 22Мб — раздражал. Когда «тесные места» решились, размер перестал играть какую-либо ощутимую роль. Самый крупный наш сервер занимает 44Мб и динамически линкуется с тремя десятками so'шек.
(В этом разделе речь идёт про 'resources', то есть память, в которой хранятся данные, а не код, в top'е ему соответствует колонка RES).
В компьютерных алгоритмах используемую память обычно высчитывают в o-notation но есть важный фактор — если процессов много, и каждый из них o(1), то сколько памяти будет съедено на сервере? Те самые «плебейские константы», внезапно начинают играть роль.
Haskell использует память сравнимо с программами на питоне. Демоны (та часть из них, которая не хранит существенного объёма данных) у нас занимают от 9 до 20 мегабайт. Питоновские демоны — примерно столько же.
Надо сказать, что по этому параметру Haskell несколько уступает OCaml (у того боевые сервисы вполне могут жить с 1-2 мегабайтами памяти), и, разумеется, Си (например, modd отъедает всего 0.15Мб), но значительно лучше ситуации с Java/Erlang.
Большинство уютненьких программных сред (jvm, python, beam.smp, php, perl, .net, etc) требуют довольно много от инфраструктуры (запущенный интерпретатор/виртуальная машина, куча файлов в правильных местах, etc). Когда вы пишите программу, которая «получает от пользователя два числа, записывает их в базу данных и показывает администратору проекта их сумму», всё ок.
Но иногда оказывается так, что вам нужно написать программу, которая запускается в single mode. Или вместо init. Или из самого init. Или с suid'ом. Или ещё как-то так, что уютненькую среду исполнения негде развернуть.
Haskell генерирует исполняемый файл. Настоящий ELF. Который может быть статическим или динамическим. И это здорово.
Второй важный фактор: скорость запуска. Во многих случаях программа запускается и завершается. У питона (и многих других интерпретируемых языков) при запуске сканируется 100500 разных файлов, особенно при куче импортов, что приводит к задержкам в 100-200 мс на старте. У Haskell эта величина много меньше, потому что ld отрабатывает кратно быстрее, чем Python или PHP.
То же касается и вывода ps/top — программы на Хаскеле — это обычные исполняемые файлы, которые выглядят в списке процессов как «просто процессы», а не как питон, запускающий файлы.
Есть у этого и минус: 32/64 бита, внезапно, — это разные исполняемые файлы, а libffi5 или libffi6 — уже большая разница, которая мешает «кросс-совместимости» приложений для того или иного дистрибутива, или даже разных версий одного и того же дистрибутива.
Так как программа на Haskell является «родной» для операционной системы, то никаких специальных особенностей в мониторинге нет (для сравнения — у Java-машины свои показатели, за которыми надо следить, у Erlang'а свои).
При эксплуатации уже написанной программы интересует ровно одно: как часто оно падает, бибикает и всё портит. Так вот, в сравнении с python — несравнимо реже. Да, при должной обработке напильником можно словить утёкший в toplevel exception, но вероятность этого крайне мала (я видел один раз за всё время использования среди всех программ).
Вероятность глупых ошибок значительно меньше. И когда я говорю «значительно», это это не в теории, а на практике, то есть по наблюдению за одним и тем же продуктом, написанным большей частью одними и теми же людьми на Хаскеле и Питоне.
Питон, как и любой другой динамически типизированный язык — это сплошая мина замедленного действия. Обо всех плохих ситуациях надо думать явно, плюс никто не страхует от мелких локальных ошибок или небрежностей. Ошибки либо проявляются в рантайме, либо их можно либо прятать в неявное except: pass (что ещё хуже). Object of type 'NoneType' has no method — наше всё. А если эта ошибка оказывается в редкой ветке — тогда мина получается совсем замедленная и срабатывает тогда, когда код уже давно «стабильный и хорошо себя показавший», и вообще, 300 дней аптайма.
Тесты, которые «покрывают весь код», к сожалению, совсем не покрывают «все возможные типы входных данных» (которые, внезапно, динамические) и совсем не спасают от ошибок типизации.
На Хаскеле таких ошибок, ошибок уровня «ой, в этом ветвлении забыл проверить» или «перепутал возвращаемый тип» в программах не появляется. Программисты это аргументируют удобной системой типов, которая позволяет на этапе компиляции отловить большую часть таких ошибок, плюс язык, позволяющий писать главное, не отвлекаясь на счёт индексов массивов и временные переменные. Им виднее.
По опыту разборов найденных ошибок я могу сказать, что большая часть ошибок, с которыми мы сталкивались — это либо ошибка в ТЗ (то есть ошибка вашего покорного слуги), либо неправильно понятое ТЗ программистами. Но не локальная ошибка или забывчивость.
Из этого вытекает парадоксальный вывод: баги в программе на Хаскеле фиксить сложнее, чем в языках с динамической типизацией, потому что в языке с динамической типизацией очередное место, где вдруг внезапно вылез NoneType, поправил и ладушки, а на Хаскеле надо с алгоритмом разбираться да по повводу неясности ТЗ с другими людьми ругаться.
Сферические в вакууме программы работают только в условиях сферичности и в вакууме. Все остальные работают в реальном мире, где миллионы сложных форматов, спецификаций и протоколов. То есть нужны библиотеки. Тысячи их.
И, на удивление, на Хаскеле их достаточно много. То есть с точки зрения «взяли и начали писать боевой код» — да, потому что не нужно будет самому изобретать логгинг, ssl, готовый orm, регэкспы, поддержку локализации, времени, http-сервер и т.д. Практически всё готовое. Хотя были и неприятные моменты. Например, нам пришлось самостоятельно поддерживать реализацию bson/mongdb для Хаскеля, так как досточтимая Тенген его поддерживать прекратила.
… При этом программа на Haskell равно так же не защищена от сегфолтов, потому что большинство программ слинковано с библиотеками, которые написаны на Си, и это либо ошибка в библиотеке, или виноват программист, который эту библиотеку не так вызвал (а сам компилятор от подобного уже не защищает). В паре мест это привело к переписыванию библиотеки на чистом Хаскеле (например, по этой причине у нас написан Hen, который реализует нужное нам подмножество запросов по работе с Xen'ом, коммиты для полной поддержки приветствуются).
Никогда не думал, что это может быть проблемой, но факт: пол-часа на сборку проекта. На весьма нехилом железе с кучей ядер и сверхбыстрой СХД снизу. Лично меня, после первых моментов гордости «ух ты, у нас наша программа аж пол-часа компилируется» это начало раздражать, потому что мелкий багфикс, и здравствуй, сцена:
Сопровождение — это внесение актуальных незначительных изменений, локальное выяснение «что не так», короче, текучка у живого «своего» проекта.
Так вот, с сопровождением получилось довольно неприятно. Понятно, что и сисадмина можно научить монадическим вычислениям. Но… Ну вы поняли. Если в питоновском коде при нужде и сисадмин мог найти и поправить, то теперь код — шайтан-арба, к которой прилагаются специальные люди для её изменения, причём о проблеме приходится говорить только в диагностическом стиле («тут не работает», «это не делает»). Во-первых это несколько портит синэргию devops (если кто-то из команды не понимает сути того, что сделала вторая часть команды — это плохо), во-вторых сильно задирает требования к людям, которые за код садятся.
Поиск программистов является проблемой, как бы апологеты Хаскеля и не говорили об обратном. Даже вариант «переучить» является проблемой, потому что под функциональное программирование, особенно с «комонадами» и template haskell, нужно сильно мозги повернуть. Другими словами, язык является некоторым дополнительным препятствием, повышающим порог вхождения.
В условиях известной нехватки программистов на рынке труда, это является препятствием. С другой стороны, наличие таких вещей привлекает программистов, которым надоело “php+js, отсюда и до обеда”.
Наверняка я услышу много возмущений от апологетов языка, в том числе от программистов, с которыми я работал. Но, объективная реальность: проекты на Хаскеле пишутся медленнее, чем на Питоне. В контраргументы мне приведут изменившийся стиль программирования, большее внимание к мелочам и т.д., но всё равно, моё текущее убеждение, основанное на практике — итоговая скорость реализации нового функционала на Хаскеле заметно ниже. Увы.
Частично это компенсируется временем на пост-отладку и отлов всяких глупых багов, которые в Питоне составляли приличный шлейф после написания программы, и которого почти нет с Хаскелем, но даже с учётом этого — всё равно получается медленнее.
Аналогичная проблема с прототипированием. Если базовый прототип на питоне появляется чуть ли не копипейстом того, что в интерактивной среде в лаборатории сделал, но в Хаскеле это обычно некое священнодействие, которое на некоторое время уходит самое в себя (типы и т. д.), и только через некоторое время приводит к результату. Если оказывается, что результат «не совсем то, о чём мечтали», то становится это понятно уже ближе к финалу, а не в начале. Таким образом, цена итерации в поиске решения увеличивается, делая весь процесс менее гибким.
A decent detective story
To be eligible to use the Service, you must meet the following criteria and represent and warrant that you: … (3) are not a competitor of LinkedIn or are not using the Services for reasons that are in competition with LinkedIn;
недавно меня посетило очередное прозрение на тему юнит-тестов. Нет, я не открыл Америку, и для многих это наверняка очевидно, но, как и в прошлый раз, именно такую простую формулировку я ещё не встречал у проповедников
наивное представление, что код программы пишется только для компилятора, уже давно в прошлом. Теперь большинство понимает, что код пишется ещё и для того программиста, который будет потом его поддерживать. Но есть и ещё один человек, про которого нужно помнить: тестировщик. Да, у человека, который будет покрывать этот код юнит-тестами, есть свои требования к тому, как он должен быть написан
гибкий язык типа javascript позволяет создавать очень изолированные от внешнего мира программы, и в нём даже нет Reflection, чтобы научить их уму-разуму. Очень много нашего кода написано в такой манере, и писать под них тесты — сплошное неудовольствие. Однако если помнить о требовании «код должно быть легко тестировать», то сравнительно нудный процесс создания тестов становится намного менее пугающим
в общем, код нужно писать для компилятора, программиста и тестировщика
An impressive show-off of visual effects industry
Another optimistic movie about life experiences of a few troubled people
Артемий Трегубенко: «an impressive demo of pickpocketing»
Hailed as the greatest pickpocket in the world, Apollo Robbins studies the quirks of human behavior as he steals your watch. In a hilarious demonstration, Robbins samples the buffet of the TEDGlobal 2013 audience, showing how the flaws in our perception make it possible to swipe a wallet and leave it on its owner’s shoulder while they remain clueless.
Being in public domain is not enough for classical music to be free because performers claim their rights for the recording. MusOpen project wants to set free all of Chopin's music. It will be performed by the best artists, and free for everyone forever. I gave it $50 and I don't even listen to classics, I just like the idea very much.
Артемий Трегубенко: «apparently just perceiving stress symptoms positively makes a big difference»
Stress. It makes your heart pound, your breathing quicken and your forehead sweat. But while stress has been made into a public health enemy, new research suggests that stress may only be bad for you if you believe that to be the case. Psychologist Kelly McGonigal urges us to see stress as a positive, and introduces us to an unsung mechanism for stress reduction: reaching out to others.
Я помолился — и вы не поверите, у всех суббота, а у меня четверг!
старый анекдот
я давно привык считать, что лето продолжается с июня по август включительно. Да, в южном полушарии времена года наоборот, но у нас-то в северном это должно быть универсальным правилом. Конечно, немного странно, что самый длинный день года — летнее солнцестояние — не в середине лета, а ближе к началу, но это можно объяснить большой тепловой инерцией планеты
недавно один из коллег-поляков упомянул, что 22 июня — это начало лета. Я стал расспрашивать, и оказалось, что и у поляков, и у немцев лето начинается с солнцестояния. И вроде бы не только у них. А ещё забавно, что в русской и английской статьях в википедии разные точки зрения о точке зрения астрономов на времена года
один из плюсов такого подхода: не нужно придумывать отдельное «бабье лето», чтобы объяснить тёплые дни сентября. И да — у меня всё ещё лето ; )