Пользователь
18,5
рейтинг
19 сентября 2013 в 15:30

Разработка → Haskell в продакте: Отчёт менеджера проекта

Я давно обещался написать статью о том, как себя показал Haskell в реальных задачах в продакте.

Для тех, кто не уследил — его в начале 2012 пролоббировали и с энтузиазмом начали внедрять программисты в Селектеле. Тогда же я обещал опубликовать отчёт о том, насколько «это всё» можно использовать.

Продакт в коммерческом проекте — это не в маленькая песочница «для себя», не академический эксперимент в области Computer Science. Это бесконечная борьба за «линию партии», когда вокруг ад, ужас и погибель, а оно всё равно должно работать. Int64 в XML-RPC кодируется строкой (потому что int'ы в XML-RPC signed int32), openssl при чтении нескольких сертификатов из файла читает только первый из них, в bool надо писать либо «1», либо «0», но иногда — «2», ибо только так придумали закодировать третий режим — и т.д. и т.п. В этих условиях требования к языку постепенно перерастают в требования к его экосистеме, инфраструктуре, готовности адаптироваться к реальному миру.

Я буду писать о Haskell с позиций product owner'а, менджера проекта, системного администратора, но никак не программиста. Так что не ожидайте от меня задушевных восторгов о том, как изящно через монадки можно сделать семигрупоид и как здорово выводить типы через типы с помощью типов.

С точки зрения менеджера проекта язык программирования оценивается по нескольким метрикам, совершенно отличными от программистских. Для программиста язык и его особенности — едва ли не самая важная вещь, так как именно с ним он проводит большую часть времени. Для остальных членов команды куда важнее происходящее за пределами исходного текста. Сначала это поиск библиотек и подходящих технологий, потом задачи сопровождения, мониторинга, внедрения и отладки.

Начнём с потребительских свойств.

Cкорость исполнения программ


Программы на 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, отсюда и до обеда”.

Скорость разработки


Наверняка я услышу много возмущений от апологетов языка, в том числе от программистов, с которыми я работал. Но, объективная реальность: проекты на Хаскеле пишутся медленнее, чем на Питоне. В контраргументы мне приведут изменившийся стиль программирования, большее внимание к мелочам и т.д., но всё равно, моё текущее убеждение, основанное на практике — итоговая скорость реализации нового функционала на Хаскеле заметно ниже. Увы.

Частично это компенсируется временем на пост-отладку и отлов всяких глупых багов, которые в Питоне составляли приличный шлейф после написания программы, и которого почти нет с Хаскелем, но даже с учётом этого — всё равно получается медленнее.

Аналогичная проблема с прототипированием. Если базовый прототип на питоне появляется чуть ли не копипейстом того, что в интерактивной среде в лаборатории сделал, но в Хаскеле это обычно некое священнодействие, которое на некоторое время уходит самое в себя (типы и т. д.), и только через некоторое время приводит к результату. Если оказывается, что результат «не совсем то, о чём мечтали», то становится это понятно уже ближе к финалу, а не в начале. Таким образом, цена итерации в поиске решения увеличивается, делая весь процесс менее гибким.
Георгий Шуклин @amarao
карма
268,0
рейтинг 18,5
Пользователь
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (125)

  • +7
    Спасибо за рассказ. Весьма познавательно.
    Из него только я не очень понял (может просмотрел) какая нужда привела к переписыванию существующего решения с… на…
    *UPD* Точнее какую проблему вы хотели решить именно хаскелем?
    • +7
      В ряде проектов питон оказался неудобен (там, где требовалась многопоточность), местами раздражали нелепые ошибки, которые исправлялись и исправлялись, а потом появлялись снова. В паре мест требовался настоящий исполняемый файл без выкрутасов с рантаймом.

      А вот написание «всего нового» на Хаскеле — инициатива программистов.
      • +2
        А Go как альтернативу рассматривали?
        • +13
          Зачем? В смысле, какие проблемы это должно решить? Какие создаст я могу сказать прямо сейчас, даже не присматриваясь. Новый язык — несформированная экосистема, проблемы детского роста и т.д. Лет 5-7 выдержки для языка — минимум для начала его использования в крупном продакте.
          • +3
            Ну, в теории он даст большинство описанных в статье плюсов без большинства описанных в статье минусов. Из плюсов отсутствует настолько детальная проработка и контроль типов (впрочем, именно благодаря этому код проще писать и менять). Из минусов сохраняется сложность поиска новых разработчиков.

            Что касается «лет выдержки», то здесь ситуация не такая однозначная. Во-первых, публично он был выложен 4 года назад. Во-вторых, имеет смысл учитывать, что этот язык появился далеко не на пустом месте, а как развитие других языков и писался людьми, которые имеют в этой области некоторый опыт (это был лёгкий сарказм). С другой стороны, стабилизировали его только года полтора назад, так что во многом Вы правы.
            • +3
              Я думаю, программисты, как подтянутся, объяснят, чем они так любят типы хаскеля и монадки. Насчёт инфраструктуры — это довольно сложный вопрос, потому что я вот с ходу ткнулся в пару библиотек, и не нашёл их:

              * libxc/libxl (управление зеном)
              * xenapi (управление xenserver)
              * openflow
              * Поддержка vhd-формата

              То есть я уже против :)
              • НЛО прилетело и опубликовало эту надпись здесь
                • +1
                  У хаскеля был биндинг на си, но пришлось писать более комфортный для высокоуровневых функций.
          • 0
            А как на счет D? Веб-Сервер Vibed вделывает по скорости все существующие решения.
  • +4
    Интересно. Единственное, насчет чего я бы поспорил — это сравнение скорости выполнения Java и Haskell. На том же debian shootout скорости практически равны, с небольшим преимуществом у java. Erlang, при этом, по скорости отстает значительно. Поэтому не стоит складывать Java и Erlang в одну кучу.
    По поводу потребления памяти — из опыта не скажу, но в тех же бенчмарках разница небольшая. Хотя подозреваю, что java жрет больше.
    Хотелось бы больше информации по поводу сложности сопровождения. Понятно, что каждый желающий в хаскель не залезет, но интересует скорость внедрения доработок по сравнению с питоном. Были ли ситуации, когда надо было делать жуткий рефакторинг? Есть ли неудобства, связанные с отсутствием java-подобных (или хотя бы PyCharm'оподобных IDE)?
    • +2
      Неудобно. Я пишу в обычном текстовом редакторе + обычная консоль. Leksah крив.
      • +5
        A Sublime Text + SublimeHaskell не пробовали? Мне показалось, довольно удобно.
        • +2
          Я его и имел ввиду. Удобство относительно. По сравнению с условным notepad.exe — удобно, полноценными IDE — нет.
    • 0
      интересует скорость внедрения доработок по сравнению с питоном

      Зависит в основном от качества архитектуры. В случае Хаскеля архитектура, к тому же, должна соответствовать парадигме ФП.
    • 0
      Насчет ide – есть превосходный ghc-mod совместимый с Emacs, Vim, Sublime
    • +1
      Хотелось бы больше информации по поводу сложности сопровождения. Понятно, что каждый желающий в хаскель не залезет, но интересует скорость внедрения доработок по сравнению с питоном. Были ли ситуации, когда надо было делать жуткий рефакторинг?

      Ну были, конечно, но, думаю это относиться к любому языку программирования, с другой стороны из-за отличной системы типов рефакторинг проходит мягче.

      Есть ли неудобства, связанные с отсутствием java-подобных (или хотя бы PyCharm'оподобных IDE)?

      О есть, однако ходят слухи, что кто-то из сотрудников JB начал писать плагин для Х-ля. Ну ещё есть довольно продвинутый SublimeHaskell для Sublime Text и недавно FP Complete зарелизили web-IDE.
  • +11
    Очень круто! Напишите сюда: www.haskell.org/haskellwiki/Haskell_in_industry

    Спасибо за статью максимально по делу.

    Программы на Haskell быстрее python, php, ruby (и других интерпретируемых языков). Быстрее Erlang/Java (и других vm-based языков). Обычно медленнее Си, хотя я видел несколько случаев, когда компилятор Haskell выдал результат, превосходящий сишный.

    Для любых практических применений производительности Haskell — за глаза и за уши.
    Нельзя сравнивать производительность языков абстрактно, только для конкретного приложения. Для большинства приложений Haskell будет медленнее Java и почти всегда гораздо медленнее, чем оптимальный Си. У вас многопоточное сетевое приложение? Много биндингов на тот же самый Си? Тут действительно может быть побыстрее Явы.

    Наверняка я услышу много возмущений от апологетов языка, в том числе от программистов, с которыми я работал. Но, объективная реальность: проекты на Хаскеле пишутся медленнее, чем на Питоне.
    Это очевидно.

    Интересно, во сколько раз уменьшился общий объем кода.
    • +9
      С бухты-барахты говорить тяжело, потому что с тех пор в коде очень много поменялось, но вот, например, небольшая компонента, шлюз для консолей. Была переписана как proof of concept для Хаскеля, потому была просто переписана, без привнесения функционала.

      Включает в себя веб-сокеты, базы данных, ssl, etc.

      haskell: 212 строк.
      python: 263 строки.
  • +3
    Немного холиварный пост местами, но все равно было очень интересно почитать. Всегда было интересно прочитать про реальный опыт с Хаскелем. Язык то ведь очень хороший, а вниманием обделен.
    Про скорость разработки, я думаю вы со всем статически типизируемыми и языками получите примерно похожее. Мы например на Scala пишем, сильно в функциональщину не ударяемся — проект компилится примерно 5-10 мин состоящий из двух приложений и нескольких библиотек.
    Так что чисто для прототипов надо использовать динамические языки — я лично использую js.
  • +2
    Никогда не думал, что это может быть проблемой, но факт: пол-часа на сборку проекта. На весьма нехилом железе с кучей ядер и сверхбыстрой СХД снизу. Лично меня, после первых моментов гордости «ух ты, у нас наша программа аж пол-часа компилируется» это начало раздражать, потому что мелкий багфикс, и здравствуй, сцена:


    GHC или Cabal умеют в инкрементальную сборку? Это могло бы намного ускорить компиляцию при мелких фиксах.
    • +8
      Мелкие фиксы, но в продакт. А в продакт инкрементальную сборку не используют, чистый CI с чистой сборкой с нуля. На локальных машинах программисты просто оптимизацию сильно снижают, плюс неполная пересборка и т.д.
      • +2
        Полчаса как-то дохрена даже для нулевой сборки. У меня на довольно дохлом серверке с обычными винтами можно пол хакаджа собрать за такое время…
        • +1
          15 минут занимает компилляция одного файла с template haskell
          • +1
            Это на сколько мегабайт там макросов то?..
          • +1
            А не пробовали разбить его на более мелкия файлы? Медленная компиляция обычно начинается где-то в районе 1-2к строк (возможно из-за квадратичности inline-ов).
            • +1
              Спасибо за совет, попробуем генерировать несколько файлов, если уж совсем тяжко станет жить.
          • 0
            А на SSD не пробовали? Если дёргает много файлов, должно, по идее, ускорить…
            • 0
              А поделитесь опытом ускорения компиляции чего-нибудь с переходом на SSD? Желательно, с цифрами
              • +1
                Если вам надо быстро компилировать, то нет ничего проще, чем ram-backed storage. В общем случае это просто nfs-сервер с async режимом и 10ым рейдом снизу.

                Гоняться за тюнингом персонально компиляции я не гонялся, там много нюансов, включая тюнинг файловой системы и ядра, и главное, оно того стоить будет? Любая ssd'шка делает так, что большую часть времени компьютер считает, а не ждёт IO.
              • 0
                В этой статье вроде было
            • 0
              У нас более чем быстрое хранилище для собственных серверов. Никакой магии, просто nfs с wb, но оно по производительности вполне обгоняет средней руки ssd по причине того, что всё в память, а потом уже асинхронно на диски.
            • 0
              GHC в памяти ворочается, на диск только объектные файлы или бинарники уже пишет.
  • 0
    Если сравнивать время итераций, то Питон заслуживает право на ошибку? Ну т.е. разработка на петоне оказывается быстрее чем на хаскеле даже с учетом времени на исправление ошибок?
    • +3
      Вероятнее всего, по мере роста размера проекта соотношения могут меняться, но на этапе раннего прототипирования (когда проверяется алгоритм, функциональность чего-то и т.д.) питон получается быстрее.
  • НЛО прилетело и опубликовало эту надпись здесь
    • +1
      Пара человек в компании была на момент лоббирования решения, остальные либо сколько-то знали на момент принятия на работу, либо выучились. В принципе, общая атмосфера среди программистов настраивала на уклон в computer science. Лично я считал это несколько перебором, но начальник отдела разработки вполне всё это поддерживал. (Говорю в прошедшем, потому что я ухожу, сама позиция, видимо, не поменялась).
      • +2
        Бежите от любителей computer science с их Хаскелем?
        • +4
          Нет, от изменений в жизни бэкофиса, с которыми я категорически не согласен. Проза жизни, далёкая от разработки.
  • 0
    Спасибо за рассказ, очень интересно.
    Будете ли продолжать использовать хаскель дальше или же откажетесь от него?
    • +1
      Не знаю — с этим вопросам уже к Селектелу, я оттуда через неделю ухожу.
      • +9
        Не надо было с хаскелем связываться!

        «Ещё ни одного менеджера не уволили за выбор C++» ©
      • +2
        т.е. рассказ в связи с уходом чтоль? :)
        • +6
          Я обещал через «пару лет» написать. Получилось полтора, так что время, когда я его пишу, да, в связи с тем, что ухожу, но сама идея рассказать «каково оно» была давно, но я не торопился, чтобы не писать по первым двум успешным проектам.
  • –6
    С точки зрения менеджера проекта язык программирования оценивается по нескольким метрикам, совершенно отличными от программистских. Для программиста язык и его особенности — едва ли не самая важная вещь, так как именно с ним он проводит большую часть времени. Для остальных членов команды куда важнее происходящее за пределами исходного текста. Сначала это поиск библиотек и подходящих технологий, потом задачи сопровождения, мониторинга, внедрения и отладки.


    Интересно, у вас занимаются поиском библиотек и технологий, сопровождают и отлаживают не программисты? Боюсь тогда представить, кто.

    Программисты — это не те, кто целыми днями «выводят типы через типы» или еще какую-нибудь фигню. Платят за результат, а не за изящные типы. Если вы считаете программистов блаженными, которые кайфуют от каких-то непонятных и ненужных штучек — плохой из вас менеджер. Или вы наняли не тех людей.

    Весьма портит впечатление от статьи.
    • +3
      Сопровождением кода занимаются программисты, да. Но в ряде случаев devops подразумевает, что если сисадмин, обнаруживший специфичное неудобство, может что-то исправить, то он и исправляет.

      Я как раз писал о том, что получилось так, что стало «все баги только через программистов», что усложняет процесс.

      Поиском библиотек занимаются в основном программисты, а вот технологии — это, скорее, удел уже больше системных администраторов (специфика проектов — сисадмины лучше видят и понимают предметную область).
      • 0
        Отличный процесс, когда сисадмин вносит правки на продакшен.
        • +8
          Да, потому что в некоторых ситуациях сисадмин лучше программиста понимает, что происходит. Учтите, что у нас очень специфичная область (виртуализация, хостинг и т.д.), так что «сисадмин», это на самом деле «эксперт в предметной области».

          Ситуацию, когда главный бухгалтер исправляет в excel код формирования специфичного отчёта, вы себе представить можете?

          В данной ситуации аналогично — довольно часто большие куски кода целиком диктуются бизнес-логикой, а не архитектурой приложения, так что поправить нужный кусок не так уж сложно.
          • НЛО прилетело и опубликовало эту надпись здесь
      • 0
        Ну, вообще-то не сисадминов, а, скорее, архитекторов :)
  • –2
    В компьютерных алгоритмах используемую память обычно высчитывают в o-notation но есть важный фактор — если процессов много, и каждый из них o(1) то сколько памяти будет съедено на сервере? Те самые «плебейские константы», внезапно начинают играть роль.

    Как используемую память можно высчитывать в о-нотации? Чем является входной аргумент в этой нотации?
    • +2
      некое «n», определяющее, с каким объёмом данных работает программа. Вы, вероятнее всего, хотели придраться к o-малое. Поправил.
      • –2
        Придираться совсем не хотел, просто не сталкивался с таким, потому и спросил. В о-нотация обычно сложность алгоритма обозначают
        • +5
          Для памяти ровно такое же правило. Если у алгоритма запросы по памяти O(n!), не используйте его в продакте.
        • +10
          Впервые вижу человека понимающего «Big O» нотацию в приложении к сложности, но не понимающего в приложении к памяти, уж извините. Чем вообще нужно заниматься, чтобы оказатьс в такой позиции что сложноть алгоритма тебя волнует, а потребляемые ресурсы — нет?
  • +4
    Первое впечатление от Хаскеля — очень высокий порог вхождения новичка в существующий проект.
  • 0
    удалено. не туда
  • +4
    Расскажите пожалуйста про объём технического долга, как он изменяется в ходе работы над проектом, на питоне и на хаскеле.
    • +1
      Особой разницы нет. По крайней мере с моей стороны. Техдолг копится, часть его устаревает, часть выполняется, часть продожает болтаться в бэклоге. Поскольку содержимое issue, связанное с техдолгом, всегда покрыто мраком, понятным только посвящённым, то я в него особо не вникаю. Команде кажется, что надо делать — делаем; нет энтузиазма, болтается, иногда закрывается с «outdated».
  • +10
    > в bool надо писать либо «1», либо «0», но иногда — «2», ибо только так придумали закодировать третий режим

    занес в цитаты
    • +1
      Отличный use case для хаскеля.
      data MyBool = Zero | One | Two, при получении значения извне пишем функцию Int -> MyBool, и при каждом дальнейшем использовании мы будем знать все возможные варианты значений, а если где-то не учтём какой-то вариант в нашей логике, то компилятор выдаст предупреждение.
      Пишем также функцию MyBool -> Int для взаимодействия с внешним миром, и в ней тоже нет шансов забыть о возможных вариантах.

      Потом если кому то взбредёт в голову закодировать ещё один режим цифрой 3. Правим MyBool, и компилятор покажет все места, где MyBool используется в логике, и где это изменение может привести к ошибке. Вин!
      • +6
        Значение «3» в типе называющемся Bool — это не win, это epic fail.
        • +12
          Да, но если эти значения хранятся в питоне, то о возможных значениях знает только тот кто это сделал. Обо всех местах, где этот хак может что-нибудь сломать, не знает никто. А если отразить эту информацию в типах хаскеля, то этот epic fail станет контролируемым, предсказуемым и поддерживаемым. Когда хак сработает (принесёт быструю сиюминутную пользу), можно будет уже сделать по-нормальному, это будет очень просто сделать — правим тип данных и компилятор покажет все места где нужно что-то поменять из-за этого изменения.

          А если этот epic fail живёт в какой-то внешней системе, которую мы не можем контролировать, с ним спокойно можно жить, зная о нём и явно его обрабатывая.
        • 0
          На самом деле в продакте мы с таким случаем не сталкивались, а я с ним столкнулся лично на домашнем линуксе, когда возился с IPv6.

          sysctl:
          net.ipv6.conf.default.accept_dad = 1
          net.ipv6.conf.default.accept_ra = 1
          net.ipv6.conf.default.accept_ra_defrtr = 1
          net.ipv6.conf.default.accept_ra_pinfo = 1
          net.ipv6.conf.default.accept_ra_rtr_pref = 1
          net.ipv6.conf.default.accept_redirects = 1
          


          Так вот, если линукс сам маршрутизатор, то он игнорирует accept_ra, а для того, чтобы он начал всё-таки объявления принимать, надо записать net.ipv6.conf.default.accept_ra=2. Формально это вообще строка (sysfs), так что «ну добавили 2, что тут такого».
  • 0
    Правильно ли я понимаю, что основное преимущество Хаскелла по сравнению с Питоном (по вашему мнению) — статическая типизация?
    • +4
      1. Многопоточность без GIL
      2. Нормальные исполняемые файлы
      3. Статическая типизация.

      Всё остальное — это уже к программистам, чем он там им не нравится, а чем нравится.
      • 0
        Вы вскользь коснулись этой темы, но я хочу переспросить, чтобы быть уверенным. Какого рода проекты вы выполняли с использованием Хаскелла? Какие у них были функциональные особенности? Те преимущества, о которых вы говорите, как-то помогали в специфике?
        • 0
          Очень огрубляя, круг задач:
          * Сервер API, реализующий логику для панели управления
          * near-real-time биллинг (постоянные списания)
          * бэк-часть администрирования — это сервер апи и утилиты для админов
          * более низкоуровневые задачи на уровне dom0 зена, манипуляции с доменами и их данными, получение их статуса.
          * Всякое дополнительное (те же консоли у виртуалок через веб-сокеты)
          • +1
            Вы erlang совсем выпилили?
            • 0
              Нет-нет, очень активно используем. Единственный случай на моей памяти, когда выпиливали — это как раз половина «сервера API», о котором пишет amarao, мы тогда использовали Эрланг как веб-сервер и менеджер Хаскелль-бекендов, подключаемых через механизм портов Эрланга, но это оказалось не очень удобно и мы оставили здесь только Хаскелль.
              • 0
                А для чего у вас остался erlang?
                • +1
                  Ну например недавно появившаяся услуга «Мониторинг» подавляющей частью написана на Эрланге, некоторые инфраструктурные внутренние сервисы на Эрланге.
                  • 0
                    Понятно. Спасибо.
              • 0
                Хотел еще спросить. В списке кандидатов был ocaml?
                • 0
                  Был в общем-то, у нас даже есть некоторые вещи на Окамле, но в те времена с Окамлом было не так хорошо как сейчас, OPAM ещё не был запущен, Real World Ocaml ещё даже не начали писать.
                  • 0
                    Если выбирать сейчас? На мой взгляд, ocaml, более практичный, ну не ленивый из коробки.
                • 0
                  Если переходить с питона, то ещё есть смысл подумать.
                  А если с хаскеля — теряется контроль побочных эффектов. Да и синтаксис смешной…
                  • 0
                    Понятно, спасибо. Синтаксис, это все на любителя.
    • 0
      Стоит особо подчеркнуть, что компилятор умеет делать вывод типов.
    • +14
      Перешёл с питона на хаскель. Больше всего доставляют:

      1. Паттерн матчинг.
      2. Изоляция чистого кода от побочных эффектов и изменения состояния от ввода-вывода. Это, пожалуй, самое принципиальное отличие хаскеля даже от его собратьев по функциональщине.
      3. Выведение типов и их зависимостей. А не просто статика а-ля алгол или auto из сишарпа.
      4. Заточеность под композицию. Вместе с №3 — чумовая вещь для DSL. Наверно как в Ruby, только наааамного безопасней из-за №2.
      5. На самом деле довольно примитивный язык, но практически любые модные фичи подключаются простым импортом. Акторы эрланга, каналы go, макросы лиспа — есть всё. И многое ещё влепят.
      6. Динамическая типизация там где надо. Вместе с №1 парсить тот же JSON парктически так же легко, как и в питоне / js.
      7. Библиотеки во всю использующие вышеперечисленое написаные настоящими титанами кода. Да и сам GHC это просто Engineering Marvel.
      • 0
        Что подразумевается под «auto из сишарпа»? var из сишарпа? При разработке C#, кстати, были заимствованы идеи из хаскеля. Могу привести ссылку на research-док, если захотите найти, но не найдете.

        Еще хочу отметить, что в современном C# вполне себе можно писать изящные функциональные вещи. Не хаскель, конечно, но методы расширения, дженерики, yield-нотация для энумераторов и нотация конструкторов коллекций позволяют создавать прекрасный немногословный код.

        P.S. Мне Haskell тоже нравится, не подумайте, что принижаю каким-то образом. Когда начинал его изучать, поражался как многие вещи похожи на то, как в шарпе сделано, погуглил — нашел причину (ту, что в начале комментария).
  • +1
    Если была так нужна статическая типизация, почему не попробовали Scala или Java? Легче найти программистов и сопровождать проще…
    • –1
      Нужны были нормальные эльфы.
      • +8
        Как опытный тролль не могу не согласиться: эльфы — это важно.
    • +5
      java в моём представлении относится к «нежным» языкам (питон, эрланг, etc), так как хочет себе собственную инфраструктуру (то есть работать в качестве компонента нижележащей инфраструктуры не может).

      Быстрый-быстрый пример: реализация команды mount.
    • +3
      Ну со Scala еще можно сравнивать, но система типов Java не дотягивает до Haskell вообще никак.
      • 0
        Замену вроде бы изначально искали не для Haskell, а для Python. С ним у Java разница не настолько драматична, a Scala — так вообще, на мой взгляд, ничем не проигрывает…

        Ну, причину отбраковки указали выше, и она сработала на более ранней стадии.
  • –11
    С одной стороны прикольно, что кто-то в реальной жизни таки использует Хаскель, а с другой совершенно непонятны рациолнальные причины такого выбора, похоже, кто-то просто пролоббировал любимую игрушку. Все ваши проблемы легко решились бы современным C++ приправленным (по желанию) Boost'ом.

    Я, кстати, думаю, что уже можно начинать присматриваться к D для реальной жизни — очень приятная и правильная «смесь» C++, Java и Python'а.
    • +4
      современный С++ ровно так же слетает по сегфолту из-за опечатки в вычислении указателя или криво дёрнутого delete.
      • 0
        Я не зря написал современный C++. В современном C++ использование сырых указателей и delete можно и нужно сводить к минимому.
        • +7
          Даже современный С++ позволяет прострелить себе коленку в два счёта, особенно по неопытности.

          Хаскель же в этом отношении довольно дружественный и если всё же читать его ошибки, то там по-человечьи всё рассказывается — что происходит и даже как это исправить.
          • –11
            Вы в чем-то правы, конечно, и именно поэтому я сейчас рекламирую D как альтернативу C++.

            Но стоят ли все эти усилия по выворачивание мозга Хаскелем конечного результата? Не лучше ли было бы уважаемым программистам-хаскелефилам эту психическую энергию потратить, например, на изучение человеческого иностранного языка?
            • +4
              Да нет там никакого выкручивания. Хаскель для начинающего это просто такой правильный питон.
              • +1
                На самом деле есть. Но если сделать над собой существенное, но непродолжительное усилие (заставить продраться через несколько заборов, в том числе забор монад; ни за что не поверю, что без их хоть какого-нибудь понимания возможно писать что-нибудь полезное; но при этом ни за что не поверю, что их невозможно понять), то не слишком умно написанные программы по виду действительно чем-то напоминают питон (особенно, если гуй лепить, например :D )
                • +2
                  Я делал несколько заходов — связанное с выведением типов ок, связанное с паттерн-матчингом ок, но дальше там начинается какое-то неприятное «language centric», которое у меня всегда вызывало скрытую неприязнь. Чем более прямым языком выражается программист (даже если это удваивает размер кода), тем легче это потом читать без понимания самых высших форм выразительности, которые он достиг.

                  Условно говоря, лично мне кажется, что программы, в которых написано вот так вот:

                  foo (1,2,3,4,5)
                  bar (1,2,3,4,5)
                  baz(1,2,3,4,5)

                  лучше, чем программы, в которых написано вот так:

                  ops=[foo, bar, baz]
                  map(ops.iterator,(1,2,3,4,5))

                  • +1
                    На самом деле, если без каких-то уточняющих обстоятельств (например, для случая когда мы заранее не знаем, какой набор функций нам необходимо применить к аргументу), код из вашего последнего примера — терминальная стадия идиотизма. Может, случай меня миновал, но я не могу припомнить, чтобы видел такой код без необходимости.

                    Что же касается простоты и линейности, культура написания кода на Хаскелле заключается в комбинировании (композиции) функций, что можно записать очень компактно и окинуть одним взглядом. Буквально, как предложение прочитать. Безусловно, это требует приобретения некоторого навыка скорочтения, что повышает порог, особенно для имеющих укоренившийся императивный бэкграунд. Но это не требует обязательного применения страшных, неизвестно что делающих операторов, вроде <**>, >>? или .&. (хотя без этого иногда не обойтись), и я не вижу в этой конкретной сложности ничего плохого.
                    • +3
                      не, не, я не про композицию функций, карринг и т.д. Я про некую любовь к написанию микрофреймворков, которые якобы должны сделать «главное место» проще для чтения, а по сути — изобретение правил, которым должны следовать все, кто имеет дело с этим кодом.

                      При том, что они дают возможность «лаконично изъясняться», для человека, который в этот код сунулся «просто посмотреть» это превращается в дополнительное разгадывание ребусов на тему «а какая фабрика у нас тут гененрирует методы для декораторов, которыми обвешаны функции, осуществляющие диспатчинг сообщений посредством pattern matching'а в списке доступных вызовов?»
                • +2
                  Это самостоятельно и без практической цели осваивать тяжело. А если есть конкретная задача и ментор — 2 недели и поехали.
          • 0
            там по-человечьи всё рассказывается — что происходит и даже как это исправить
            О, нет. Исправление ошибок компиляции — самый высокий барьер при освоении языка.
            • +3
              Как новичок в хаскелле и C++ могу сказать, что пока возникло такое ощущение, что в первом после исправления ошибок компиляции в рантайме все работает как задумывалось, тогда как во втором успешное прохождение компиляции ничего не значит. Сколько раз брался пописать на плюсах и все время бросал из-за цикла 5 минут кодинга — 5 минут отладки компиляции — 20 минут отладки сегфолтов. Лично мне в C++ (да и в Java, C# и т.п.) устранение ошибок компиляции видится ублажением компилятора в большинстве случаев, тогда как в хаскелле сразу видно, что я налажал с композицией, т.к. синтаксис простой и предсказуемый.
      • +5
        >слетает по сегфолту из-за опечатки в вычислении указателя или криво дёрнутого delete
        В современном С++ писать delete или вычислять указатели надо разве что если ты пишешь что-то типа драйвера. Во всех остальных случаях это явная ошибка архитектуры или банальное отсутствие знаний.
        • 0
          Допустим. Но, скажите, от ошибки в плюсике оно точно спасёт?
          • +2
            Нет, не спасет. Потому что так же легко можно словить сегфолт, например, проиндексировав вектор.

            Я люблю современный идиоматический C++, но категорически не соглашусь с комментаторами выше по поводу его безопасности — она не намного больше, чем у обычного C. Что он реально дает, так это управление памятью (-> меньше утечек), более естественное написание устойчивого к проблемам кода (exceptions + RAII), и больше DRY.
            • +1
              А ну-ка покажите мне, где вот тут можно ошибиться в плюсике или неверно проиндексировать вектор и словить сегфолт?

              vector<int> vec;
              vec.push_back( 10 );
              vec.push_back( 20 );
               
              for (int i : vec ) 
              {
                  cout << i;
              }
              
              • +5
                Конкретно в вашем коде — нигде. Но вы же понимаете, что иногда нужно, например, проитерировать по парам элементов, или запомнить индекс (или итератор), и дереференсить его позже. И вот тут полное отсутствие проверок границ даст о себе знать.

                Это все можно сделать «правильно», разумеется. Проблема в том, что даже идиоматические плюсы не подталкивают к этому правильному решению.

                Кстати, что забавно — в C++11, при всех прелестях unique_ptr/shared_ptr, которые реально убрали много мороки с памятью, добавилась новая категория сегфолтных проблем благодаря лямбдам с ссылочным автозахватом, и std::function (и всяческие аналоги и обертки), позволяющие эти лямбды возвращать куда не надо без оглядки на время жизни объекта. Я работал с кодом, который активно использовал лямбды, еще когда это был C++0x, и это было реальной проблемой — при всей моей любви к ним.
  • 0
    А что бы вы сказали, если бы ваши программисты предложили использовать Лисп?
    • +5
      (Во-первых (я (не (люблю (много (скобочек)))))

      Во-вторых, я бы попросил доказательств. Что-то не очень большое, реализованное на нём из нашего хозяйства. По поим представлениям лисп не бывает компилированный, а работает он в режиме «уютненькой лисп-машины», то есть представить на нём /bin/init в initramfs не получается.
      • +1
        По поим представлениям лисп не бывает компилированный, а работает он в режиме «уютненькой лисп-машины», то есть представить на нём /bin/init в initramfs не получается.

        stackoverflow.com/questions/913671/are-there-lisp-native-code-compilers
        • 0
          Ясно, спасибо. Скобочки это не отменяет, впрочем.
          • +1
            Это расхожее предубеждение. JavaScript вообще вне конкуренции со своими }); }); }); });.
  • +7
    Странно насчет скорости разработки.

    С трудом себе представляю, чтобы разработка чего-то больше десятка строк на питоне оказалась быстрее, чем на Хаскелле, если только нет совсем уж готовых библиотек/наработок.

    И практически не представляю, чтобы в большом проекте на питоне можно было внести серьезные изменения (не добавления), без длительного тестирования.

    Скорость разработки новых фич по-прежнему отстает уже на протяжении пары лет или же это скорее был начальный этап?
    • +3
      Не из-за Х-ля уменьшилась скорость разработки. amarao ближе к концу пишет, что «в контраргументы мне приведут изменившийся стиль программирования» — из этих изменений сильнее всего повлияло, что программисты примерно в это же время решили проводить более содержательные code review.
  • +1
    Эх, кто бы так на Rust попробовал и отписАлся… Сверху так посмотришь: Вам на Питоне не понравилась динамическая типизация и отстранённость от железа; а на Хаскеле — что он слишком сложен/оригинален. В Rust же и типизация хоть куда, и стиль повествования привычный, да и близость к железу/библиотекам. Вот только дозреть ему до 1.0, и будет счастье ;)
    • –3
      Есть еще scala — про нее много кто отписывался, включая твиттер.
      Там очень много схожего с Rust (pattern matching, type class, actors).
      При этом обычно код не уходит в астрал, но при особом желании снос крыши можно довести до уровня хаскеля и даже дальше.
      • +2
        Rust не бежит в JVM, этим он привлекателен
  • –1
    Как вы оценивали скорость разработки haskell vs python? Ведь глупо же оценивать сложность задачи по кол-ву строк кода.
  • +3
    Огромное спасибо за статью. Очень интересно. Однако у меня есть подозрение, что некоторые вещи вы делаете не так. Вот пара наводящих вопросов:

    — По поводу количества используемой памяти. Это c учетом использования строгих вычислений там, где это нужно (например), или вы пишите на Haskell, будто он строгий язык, и не паритесь?
    — Почему вы предпочли persistent-mongodb написание собственной библиотеки?
    — По поводу скорости компиляции. В отличии от всяких там Erlang, в Haskell, вроде, перед сборкой можно не чистить уже скомпилированные модули. Вы не пробовали убрать make clean из Jenkins?
    • +2
      Вы не пробовали убрать make clean из Jenkins?

      И потом на боевом сервере отлавливать непонятные глюки?

      habrahabr.ru/post/193722/#comment_6749496
      • +4
        Вы не могли бы привести пример непонятного глюка, который возникает в Haskell при использовании инкрементальной сборки? Просто я действительно не представляю, как добиться такого эффекта в Haskell. Если проект собирается с нуля, то не удивительно, что сборка занимает пол часа.
        • +2
          Такое может быть при использовании cabal-dev и параллельной разработки нескольких пакетов, один из которых зависит от другого. Речь не о сборке в продакшн, конечно, но при сборке для тестирования, например у нас возникали ситуации, когда зависимый пакет уже установлен в cabal-dev, удовлетворяет зависимостям (программисты ещё не бампнули версии), но интерфейсы уже изменены — ошибка компиляции.

          Кстати, вероятно, с выходом Cabal 1.18, который поддерживает sandbox, такой проблемы уже нет. Но это случилось буквально над днях и мы ещё не успели перейти на него.
          • 0
            Интересно. Спасибо.
    • +4
      По поводу количества используемой памяти. Это c учетом использования строгих вычислений там, где это нужно (например), или вы пишите на Haskell, будто он строгий язык, и не паритесь?


      Мы действительно в начале пару раз столкнулись с недостаточным пониманием ленивости, накопления санков и т.д. Но сейчас, конечно это уже не проблема, например у нас, как рекомендует Джона Тибел, поля структур по-умолчанию строгие.
      Почему вы предпочли persistent-mongodb написание собственной библиотеки?

      Мы используем persistent-mongodb, на самом деле речь идёт о библиотеке MongoDB, которая была когда-то написана Тенгеном, затем им же дропнута, и теперь мы продолжаем её поддерживать, persistent-mongodb использует её же. Если интересно, то пару дней назад начался процесс её переписывания — изначально библиотека была довольно низкого качества.
  • +2
    Аналогичная проблема с прототипированием. Если базовый прототип на питоне появляется чуть ли не копипейстом того, что в интерактивной среде в лаборатории сделал, но в Хаскеле это обычно некое священнодействие, которое на некоторое время уходит самое в себя (типы и т. д.), и только через некоторое время приводит к результату. Если оказывается, что результат «не совсем то, о чём мечтали», то становится это понятно уже ближе к финалу, а не в начале. Таким образом, цена итерации в поиске решения увеличивается, делая весь процесс менее гибким.

    Не допускаете ли Вы, что это может быть обусловлено лишь большей опытностью программистов в Питоне? Мне кажется, дело в том, что уровень виртуоза во владении Питоном требует куда меньших знаний и трудозатрат, чем Хаскелом. Поэтому Ваши программисты могли просто ещё не реализовать своего потенциала в Хаскел — отсюда и меньшая производительность.

    От себя могу заметить, что, имея за плечами год ежедневной работы с Хаскел, уже ощущаю, что выполняю работу на уровне производительности, сравнимом с ООП языками из своего багажа. При этом я ощущаю и гигантский потенциал для роста своей производительности. Я осознаю, что до сих пор порой трачу время на моменты, когда в мозгу происходит «перещёлкивание» и, вдруг, я открываю для себя ещё что-то новое.
    • 0
      Спустя год ситуация особо не менялась, так что опытность/неопытность начального периода уже не особо актуальна.
  • 0
    «RES» в top значит resident, там показывается сколько «физической» памяти использует процесс, сюда входят и данные и исполняемый код.
  • –1
    Прочитал два раза. Так и не понял какую проблему(ы) решали.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.