Язык Erlang известен не очень широко, хотя это довольно интересная технология, очень сильная в своей нише. Последние несколько лет Erlang все чаще применяется в веб-разработке.

Отношение к Erlang противоречивое – есть как убежденные сторонники, так и суровые критики. Однако мало кто использует его в разработке. Так что специалистов, которые могут хвалить или ругать этот язык, опираясь на собственный опыт применения, совсем немного.

В компании Wargaming Erlang применяется более 3 лет. Есть около десятка разработчиков и несколько проектов, работающих под большими нагрузками. И в этой статье мы поделимся своим опытом и видением Erlang.



Как все началось


В Wargaming используется микросервисная архитектура, где разные сервисы взаимодействуют между собой как по HTTP-протоколу, так и по AMQP. Поэтому активно используются сервера RabbitMQ. Можно сказать, с RabbitMQ все и началось. И хотя программисты о Erlang еще не знали, но архитекторы и DevOps уже нарабатывали опыт, разбираясь, почему где-то упал RabbitMQ, что можно извлечь из его логов и crash dump; и что нужно сделать, чтобы он не падал :)
Надо сказать, что наши архитекторы на тот момент уже давно питали симпатии к Erlang, но не внедряли его как язык разработки, пока не появились веские причины для этого.

Не секрет, что наши игровые сервера работают на движке Big World, где игровая логика пишется на Python (Python был и остается основным языком разработки в компании). Изначально через эти сервера работал и внутриигровой чат. Но на какой-то момент тратить ресурсы серверов (которых и так не хватало) на чат, стало невозможно.

Чат World of Tanks тесно связан с игрой, поддерживает множество специфических функций. Поэтому для него было необходимо собственное решение. Оно, конечно, должно было быть кластерным, так как один сервер нельзя масштабировать под имеющиеся и планируемые в будущем нагрузки. Вполне логично, что для такого проекта был выбран Erlang. Тогда в компанию и пришел первый Erlang-разработчик.

Как все продолжалось


Помимо игровых серверов, у нас есть множество веб-сервисов (вроде регистрации и авторизации игроков, платежей и т.п.). Первоначально это все было написано на Python/Django, и в результате быстрого роста числа игроков в вебе начались проблемы.
Тогда мы начали использовать Twisted. Но и разработчики, и архитекторы считали его слишком сложным, а разработку на нем слишком дорогой. В качестве замены архитекторы предлагали использовать Erlang, но у руководства возникли обоснованные сомнения – откуда взять достаточно Erlang-разработчиков, ведь это не самая популярная технология.

Между тем, прототипы, написанные на Erlang, показывали хорошую производительность в бенчмарках, а Twisted продолжал причинять боль. И архитекторы пошли дальше прототипов, сделали и запустили один небольшой веб-сервис на Erlang. Он был написан за пару недель усилиями одного архитектора и оказался удачным. Потом был второй проект, третий. Компания начала поиск Erlang-разработчиков, а наши Python’исты без труда осваивали новый язык (для некоторых это было проще, чем разобраться с Twisted).

На сегодняшний день в компании десяток разработчиков, которых можно назвать Erlang-разработчиками – они большую часть кода (или даже весь) пишут на Erlang. Некоторые из них уже несколько лет пишут на Erlang, а кто-то только перешел с Python.

Работы в вебе у нас много. Растет количество игр, которые выпускает Wargaming. Мы переходим от специализированных (под конкретную игру) сервисов к универсальным. Также стартуют новые проекты, многие из которых пишутся на Erlang. В вакансиях мы чаще ищем Python-разработчиков (основной язык программирования в нашей компании), но упоминаем, что знание Erlang будет плюсом :)

В результате нам удалось собрать команду сильных Erlang-разработчиков. Среди них, например, Сергей Прохоров, чей патч приняла команда Erlang/OTP. Так что теперь в виртуальной машине Erlang есть немного кода Сергея.

Мнения разработчиков


Имея внутри компании достаточно много Erlang-разработчиков с разным опытом, мне удалось собрать широкий спектр мнений об Erlang. Любопытно отметить разницу в восприятии языка между опытными и начинающими разработчиками.

Опытные в целом относятся к Erlang позитивно. И это не удивительно, иначе они бы не писали на нем несколько лет и не стали бы опытными :) Начинающие относятся по-разному. У них больше критики и недовольства. Это тоже не удивительно, потому что в сравнении с Python, Erlang заметно беднее и по выразительности, и по инфраструктуре, и по богатству библиотек.

Начнем с общих моментов, с которыми согласны все наши разработчики:


  • Erlang показывает хорошую производительность в вебе, измеряемую числом запросов в секунду. Добиться аналогичной производительности на Python дольше, сложнее и дороже.
  • Erlang язык узкоспециализированный. Не стоит пытаться решать на нем любые задачи.


Плюсы Erlang, общее мнение:


  • Хорошая модель многопоточности, которой удобно пользоваться. Модель акторов и асинхронность позволяют легко писать эффективный многопоточный код.
  • За 20 лет использования в нагруженных проектах сформировались стандарты и шаблоны, собранные в OTP-фреймворк. С ними проще строить сложные, но стабильные и надежные системы.
  • Erlang умеет использовать все доступные ядра. Нет необходимости запускать отдельный экземпляр сервиса для каждого ядра.
  • Низкий порог вхождения. Язык простой, изучается быстро (однако тут есть некоторые подводные камни, о которых будет написано ниже).


Плюсы, которые видят начинающие разработчики:


  • Замкнутая экосистема, проекту не нужно ничего из внешнего мира.
  • Не нужно внешнее in-memory хранилище (Redis, memcached), можно держать кэш прямо в оперативной памяти сервиса.
  • Не нужно запускать несколько экземпляров сервиса для каждого ядра, под внешним супервизором, со своей конфигурацией. Один сервис может использовать все ядра, и имеет свою гибкую систему супервизоров.
  • Меньше экземпляров сервисов – меньше общения между ними по HTTP или AMQP. Гораздо удобнее общаться между процессами внутри одного узла.
  • Проще разрабатывать распределенные системы. У Erlang есть свои средства для построения кластеров и общения между узлами.


Плюсы, которые видят опытные разработчики


  • Функциональная парадигма, иммутабельные структуры данных.
  • Удобно мониторить сервис в реальном времени, искать и устранять проблемы прямо на работающем узле.
  • Можно менять конфигурацию без перезагрузки сервиса.


Минусы, общее мнение


  • Библиотек мало, еще меньше – хороших. Многое нужно писать самим или дорабатывать.
  • Ограниченный синтаксис языка. Иногда не хватает более удобных синтаксических конструкций. Например, не помешала бы конструкция let, чтобы вводить области видимости. Докучает необходимость создавать переменные с числовыми суффиксами: Var1, Var2, Var3.
  • Язык консервативный и развивается очень медленно. Например, тип map внедряли несколько лет (и до конца не доделали).
  • Медленно развивается инфраструктура для веба, нет веб-фреймворка.
  • Язык ориентирован на более низкоуровневое применение, чем то, что привычно Python/Django или Ruby/Rails-разработчику.


Минусы, которые видят начинающие разработчики


  • Строки реализованы ужасно, средств для работы со строками очень мало. Опытные разработчики вообще строками не пользуются, и поэтому забыли про такую проблему :)
  • Слабая стандартная библиотека, мало возможностей. Это особенно заметно на фоне Python. Несогласованности в стандартных модулях (названия функций, порядок аргументов, возвращаемые значения).
  • Функциональная парадигма, иммутабельные структуры данных. Что для опытного разработчика плюс, для начинающего, не знакомого с парадигмой – минус. Это требует отдельного изучения.
  • Тяжело читать сообщения об ошибках.


Минусы, которые видят опытные разработчики


  • Нет хороших инструментов для управления зависимостями и сборки пакетов. Rebar3 и hex – движение в нужную сторону, но они еще сырые и не приняты широко.
  • Релизы устроены сложно и решают не те задачи. Например, они решают задачу горячего обновления, а это не нужно веб-разработчику. Правильнее строить сервис так, чтобы он безболезненно переживал остановку одного узла.
  • Синтаксис конфигов не удобен DevOps. Честно говоря, он не удобен и самим разработчикам. Лучше описывать конфигурацию в более простых форматах, например, ini.


Еще несколько индивидуальных мнений


  • Python-разработчику проще освоить Erlang, чем Twisted, не смотря на то, что это совсем другой язык. Проекты на Erlang проще, разрабатываются быстрее и ведут себя надежнее, чем аналогичные на Twisted.
  • Сообщество Erlang-разработчиков маленькое и не очень активное. Каждая команда живет в своем проекте. Мало кто обменивается опытом и шарит знания коллегам из других компаний. Обычная ситуация, когда каждый разработчик пишет свой набор библиотек, который используется только в его проекте.
  • Есть крупные open source проекты, как Cowboy или Riak, но нет проектов, вокруг которых сложилось бы активное сообщество.
  • Виртуальная машина Erlang хороша, но сам по себе язык – слишком консервативный, не стремится к развитию. Поэтому активное развитие сейчас идет в рамках новых языков для этой виртуальной машины, как, например, Elixir.
  • Erlang и Python неплохо дополняют друг друга, компенсируют слабые стороны. Правильно сочетать их вместе в одном проекте. Бизнес-логику, ORM, работу с базой лучше делать на Python, а многопоточную обработку данных, связь между сервисами, фоновые процессы – на Erlang.
  • Erlang обладает ужасным синтаксисом, который заставляет страдать :)


Что получается в сухом остатке?


В целом разработчики с удовольствием критиковали Erlang, и минусов находили больше, чем плюсов. Это не удивительно, ведь к достоинствам технологии быстро привыкаешь, считаешь их само собой разумеющимся, а недостатки продолжают раздражать и привлекать внимание. Однако у нас в компании разработчики сами выбирают язык для проектов (конечно, из списка одобренных архитекторами :)), это решение не навязывается сверху. И раз уж они пишут на Erlang, значит он не так плох. На сегодняшний день в нашем вебе используются Python 2.7, Python 3.5 и Erlang. Архитекторы не против попробовать и другие варианты: язык Go, один из JVM-языков, еще что-то – но пока желающие нашлись только на Elixir, и об этом ниже.

Коварный порог входа


Наверняка многие слышали, что у Erlang низкий порог входа, и разработчик уже через 2 недели знакомства с языком может писать код для продакшена. Это правда, но не вся. Язык действительно простой и легко изучается, но есть и подводные камни.

Во-первых, «детские проблемы» на которых многие неопытные разработчики спотыкаются, а опытные, научившись их решать, забывают. Классический пример – получить deadlock в gen_server. Это случается почти у всех. Своего рода ритуал вхождения в клуб :) Из той же песочницы проблема понимания, что строками пользоваться не нужно, а использовать только binary. Этого правила легко придерживаться в своем проекте, но, к сожалению, в сторонних библиотеках может быть по-всякому, и к этому нужно быть готовым.

Во-вторых, есть и совсем не детские проблемы. И первая из них – недостаток информации. По Erlang написано всего несколько хороших книг. Большинство из них описывают язык как таковой, а не применение его для решения прикладных задач. Многие важные вопросы не описаны вовсе, ни в книгах, ни в документации. Какие инструменты использовать для сборки проекта? Как мониторить работающий узел? Как обнаружить переполнение mailbox? А другие варианты утечки памяти? Как находить узкие места, мешающие масштабировать систему? Какие библиотеки использовать? Их, с одной стороны, мало. С другой – популярные библиотеки имеют много форков. И не понятно, что это за форки, чем отличаются, как поддерживаются. Те, кто искал драйвер для работы с MySQL, хорошо понимают эту проблему :) И это мы еще не взялись за распределенные системы – отдельный мир с кучей своих нюансов. Обо всем этом можно узнать только от другого опытного разработчика. Поэтому важно, чтобы они всегда были в команде.

Несколько слов об Elixir


Существует несколько языков, которые компилируются в байт-код Erlang и работают на виртуальной машине Erlang. Самый популярный из них – Elixir.

Несколько лет назад на Erlang обратили внимание Ruby/Rails-разработчики. Они искали решение проблем с производительностью, характерных для Rails. Проблемы с производительностью решились, но все остальное в Erlang им не понравилось. И не удивительно, из мощного высокоуровневого языка с богатейшей инфраструктурой они попали в суровый низкоуровневый мир.
Ruby/Rails-разработчики быстро поняли, что так жить они не хотят, и начали строить для себя более комфортную среду. Результатом их усилий стал язык Elixir и веб-фреймворк Phoenix, у каждого из которых есть активное сообщество. За 3 года они сделали больше для веб-разработки, чем в Erlang было сделано за 10 лет.

Мнения об Elixir, как и об Erlang, мнения противоречивы. Одни считают его легко читаемым, лаконичным и мощным. Другие – сложно читаемым и непродуманным. Консервативные Erlang-разработчики относятся к Elixir прохладно. А те, кто пришел в Erlang из Ruby или Python, принимают Elixir с любопытством.

Плюсы языка:


  • Богатая стандартная библиотека;
  • Высокоуровневый веб-фреймворк Phoenix;
  • Хорошая инфраструктура: инструменты сборки, менеджер пакетов, библиотеки;
  • Хорошая документация;
  • Активное сообщество.

Минусы:


  • Язык молодой, не накоплен опыт в индустрии;
  • Знание Erlang все равно нужно, слишком тесная связь между этими языками;
  • Низкий bus-фактор.

Сообщество занято инфраструктурой, библиотеками и фреймворком, а ядро языка разрабатывается небольшой командой. Есть риск, что если эта команда потеряет финансирование, и ее никто не заменит, то язык перестанет развиваться и поддерживаться.

В вебе Wargaming прототип одного из будущих сервисов делают на Elixir. Посмотрим, как он себя покажет.


Таково мнение наших Erlang-разработчиков. А что думаете вы? Что именно в языке вас привлекает, а что – мешает работать и попросту портит кровь? Поделитесь в комментариях! Расскажите о своем опыте работы с Erlang и реализованных проектах.

Спасибо за внимание,
Юрий Жлоба
Веб-разработчик в Wargaming

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


  1. sumerman
    21.03.2016 12:36
    +5

    Мы пришли к Erlang + Elixir (в тех местах где у вас python, а у нас ruby; ruby пока ещё есть местами). По поводу библиотек — проблема преувеличена, почти везде проблема найти хорошую: Python+Twisted или Ruby + EM или Scala + Akka и вы внезапно не можете нормально пользовать большинство библиотек, потому, что их авторы не знали, что иногда надо асинхронно.


    1. roller
      22.03.2016 01:24

      В разрезе эпрланга и акторной модели как не вспомнить Celluloid


      1. sumerman
        22.03.2016 01:25

        ЕМНИП он не работал и не работает. Чисто на поиграть.


        1. mtungusov
          22.03.2016 11:53

          Заявление ни о чем.
          У нас несколько сервисов написаны с использованием Celluloid.
          Sidekiq v3.x использовал Celluloid.


  1. loz
    21.03.2016 13:14
    -1

    Спасибо за статью. Недавно еще обнаружил такую штуку — по сути реализация эрланга на джаве: http://www.paralleluniverse.co/quasar/ прям с собственным планировщиком, процессами, бехейвиорами и тд и тп.


    1. potan
      21.03.2016 14:27

      Опыт Akka показывает что JVM плохо подходит для акторной модели. В Quasar как-то смогли обойти все ее ограничения?


      1. RomkoGoofique
        21.03.2016 14:45
        +1

        "Какие ваши доказательства?"

        Не наезда ради для. Правда интересно.
        Мне из минусов JVM кроме общей кучи (и как следствие stop-the-world) ничего в голову, просто, не приходит.


        1. sumerman
          21.03.2016 14:52

          Почти ничего из библиотек не в курсе того, что их пытаются вот так вот пользовать => непредсказуемое поведение в продакшене.


          1. RomkoGoofique
            21.03.2016 14:55

            И какая из существующих VM успешно справилась с этим?

            Речь, я полагаю, о блокирующих вызовах, укладывающих шедулер?


            1. sumerman
              21.03.2016 16:37
              +1

              Тут либо то что вы озвучили, либо квазаропроблемы (назовём это так, хотя такие же проблемы у em-synchrony).

              В первом случае у BEAM (Erlang) всё очень хорошо пока вы не зовёте С, при этом в FFI есть довольно много возможностей дружить с VM. В Haskell всё тоже отлично в пределах языка с его спарками. В Go можно смело блокироваться на IO, но вот на вычислениях не стоит. Больше — не в курсе.

              Квазаро-проблемы это ситуация, когда в виртуальную машину где вообще-говоря не стоит блокировать притаскивают каким-то способом а-ля-эрланг вытесняющую многозадачность, но для поддержки этого типично оно переписывает байткод или делает какой-нибудь monkey-patching (в случае em-synchrony) и в результате у нас сторонняя библиотека оказывается не совсем тем, что написал и протестировал её автор.


              1. nwalker
                21.03.2016 18:08
                +2

                >> Go можно смело блокироваться на IO
                На сетевое IO. Файловое, консольное, subprocess — блокируют тред планировщика.


                1. sumerman
                  21.03.2016 18:09
                  +2

                  Нда, всё печальнее, чем я думал :( Как гоферы живут тогда если файлы и подпроцессы нельзя трогать без блокировки?


                  1. nwalker
                    21.03.2016 18:27
                    +4

                    Ну, в случае чего, рантайм создаст новый тред.

                    Живут они с надеждой на лучшее, наверное.
                    В принципе, можно вынести такие операции в отдельный ограниченный пул горутин, в этом случае, когда все станет плохо, система в целом может и не развалиться.


                    1. erlyvideo
                      22.03.2016 10:51
                      +1

                      у нас в флюссонике есть целая подсистема того, как танцевать вокруг диска, что бы он колом не поставил весь сервер.

                      Надеяться не на кого.


        1. potan
          21.03.2016 15:18

          Дорогие нити. В виртуальной машине Erlang поддерживается пул системных нитей, которые выполняют его процессы, при этом стандартнаяя библиотека позволяет блокировать процесс, переиспользую нить. В JVM в акторах выполнять блокирующие операции не рекомендуется, так как они блокируют и нить из пула. Это очень мешает программированию. Как это решается в Quasar я при беглом взгяде не понял.
          В Erlang очереди сообщений интегрированы с планировщиком. Процессы, которые вызывают риск переполнения чьего-нибудь mailbox, выполняются медленнее, позволяя его разгрести. В JVM эти понятия реализуются сильно на разных уровнях и реализовать такое поведение будет довольно сложно.


          1. Optik
            21.03.2016 15:40
            +2

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


          1. RomkoGoofique
            21.03.2016 15:47

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

            В Akka акторы — это просто объекты со сравнимым с Эрланговым оверхедом (около 300 байт).

            Шедулер ActorSystem'а Akka'и — это просто "реактор", который занимается разрешением фьючеров в готовые значения.
            Немногим отличается от beam, который периодически дает процессорное время эрло-процессам. А процессы совершают редукции, пока не получат готовые значения.

            Да, Akka'у можно повесить вызвав, что-нибудь блокирующее из сторонней библиотеки. Но и beam тоже не оценит, вызов долгоиграющего NIF'а.

            И в том, и в другом случае, обязательно надо знать, что за функции вызываешь. Казалось бы, очевидно...

            Процессы, которые вызывают риск переполнения чьего-нибудь mailbox, выполняются медленнее, позволяя его разгрести.

            Ahem. Насколько мне известно, erlang:send/2 может заблокировать исключительно по причине большого размера сообщения или из-за того, что слать надо на другую ноду (отсюда и появился erlang:send_nosuspend/2).
            Проблемы консумера, неуспевающего прожёвывать свою очередь, положено решать отказывая в обслуживании (см. https://github.com/ferd/pobox).


            1. sumerman
              21.03.2016 18:19
              +3

              Мне кажется не стоит смешивать тёплое с мягким: в Akka любая библиотека, не умеющая в футуры (или хотя бы коллбэки) делает плохо. В Erlang/Elixir/Haskell поставить колом VM библиотекой только на этом языке (без вызовов в C) существенно тяжелее. Но есть общие для языков проблемы вроде "нужна библиотека которая не делает X, потому, что так нельзя под нагрузкой" (где X может быть специфично для языка или vm), которые внезапно выравнивают число юзабельных библиотек.


          1. RomkoGoofique
            21.03.2016 15:58

            Процессы, которые вызывают риск переполнения чьего-нибудь mailbox, выполняются медленнее, позволяя его разгрести.

            Если вы про sender punish, который вроде (к моему ужасу :( ) умолчально включен, то он не очень работает. В противном случае, при избыточном логгировании, система прекращала бы работать до того, как сожрет всю память очередью gen_event'а логгера.


            1. seriyPS
              21.03.2016 16:27
              +2

              Похоже она всё же есть
              https://github.com/erlang/otp/blob/maint/erts/emulator/beam/bif.c#L2105

              Если покопаться, то видно, что у процесса списывается редукций 4 * количество сообщений в мейлбоксе получателя.


              1. RomkoGoofique
                21.03.2016 17:09

                Обидно, наказывают, того, кто уже капнул сообщением. Тем самым предоставляя преимущество тем, кто только собирается усугубить положение процесса с переполненным мейлбоксом. :D


                1. potan
                  22.03.2016 13:42

                  Предсказывать такое желание процесса VM еще не научилась :-)


          1. Sirikid
            22.03.2016 10:28

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


          1. erlyvideo
            22.03.2016 10:53
            +2

            капля дегтя: этими нитками для асинхронной работы в эрланге нельзя управлять. Их количество нельзя менять в рантайме и из эрланга нельзя указать на каком пуле ниток запускать этот драйвер.


            1. seriyPS
              22.03.2016 12:24
              +1

              Можно, наверное, запускать с запасом, а в рантайме менять erlang:system_flag(schedulers_online, N).

              нельзя указать на каком пуле ниток запускать этот драйвер

              Так там всего один пул. Точнее, пул планировщиков и пул IO и dirty schedulers, но драйверы (если ниче не путаю) всегда на пуле планировщиков исполняются (явно скидывая IO в пул IO). Или что имеется в виду?


            1. potan
              22.03.2016 13:40

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


        1. erlyvideo
          22.03.2016 10:37

          общая куча — это не только stop the world, но и нереальная сложность в том, что бы устранить все утечки памяти.


      1. loz
        21.03.2016 15:27

        Я же говорю, там свои треды, свой планировщик, свои мейлбоксы опять же. Точно так же как на evm, создатели руководствовались своим опытом использования эрланга.


        1. cs0ip
          21.03.2016 15:36

          А можно пару слов про то, как там решается вопрос с переиспользованием потоков? По идее тут без кодогенерации не обойтись.


          1. loz
            21.03.2016 15:41

            У них в статейке основной это написано (мне лень переводить)):

            Of the three pieces, coroutines are the most challenging to implement on the JVM, as they require bytecode instrumentation. Essentially, every possibly-pausing method (the Quasar/Pulsar nomenclature is “suspendable”) must be inspected to find all invocations of other suspendable methods. Before the call to a suspendable method, code must be injected to push the caller’s local variables onto a stack object. Also, code must be injected to the beginning of every suspendable method, that upon resuming would jump to the instruction following the pause-point. This has to be done to all suspendable methods on the (OS thread) stack.


  1. dmitriid
    21.03.2016 13:35
    +1

    Можно сказать, эта статья — перевод двух моих rant'ов :)

    medium.com/@dmitriid/erlang-is-dead-long-live-e-885ccbcbc01f
    medium.com/@dmitriid/designing-for-anything-with-erlang-cfadb6833bc0


    1. Wargaming
      21.03.2016 15:43

      Да, Дмитрий, автор статьи и большинство наших эрланг-разработчиков читают тебя :)


  1. potan
    21.03.2016 14:21

    Синтаксис Erlang основан на его предке — языке Prolog. Когда-то этот язык было модно учить — относительно простой и выгладещий перспективно. Но развитие ИИ пошло в другую сторону, и современной молодежи такой синтаксис становится непонятным.

    ФП как раз наиболее полезно для новичков. Оно сильно мешает писать плохой код.


    1. Wargaming
      21.03.2016 15:21
      +5

      Та или иная парадигма сама по себе не защищает от плохого кода. И тут дело не в парадигме, а в том, что это редко используемая технология. Значит, ей интересуются любопытные разработчики, а нелюбопытные игнорируют.
      Вообще любопытство разработчика хорошо коррелирует с его способностью писать хороший код :)


    1. wsf
      21.03.2016 20:36
      +1

      На самом деле писать плохой код на эрланге проще простого, если нету понимания как работает этот язык. Никогда не забуду функцию проверяющую сложность пароля и состоящую из 15(!) вложенных операторов case of. То есть человек просто взял и написал код так как если бы он писал его на с. Переписанный код с применением pattern-matching занял в 4 раза меньше места.


      1. sumerman
        21.03.2016 21:03
        +2

        Ха ха, аналогично мне не развидеть код вроде:

        case [1 || my_option <- Options] of
          [] -> false;
          _ -> true
        end

        И использование process dictionary вместо аккумулятора в свертке.


  1. eoffsock
    21.03.2016 14:23
    +4

    А я уверен, что у Erlang прекрасный синтаксис, который идеально укладывается в (мое) видение языка. Я воспринимаю отдельную функцию как систему фильтров, через которые текут входные данные, и тут синтаксис Erlang идеален.

    И насчет веба я не совсем согласен. Есть же Chicago Boss. Он менее изящный, чем RoR или Phoenix, но он работает.


    1. Source
      21.03.2016 15:23

      Chicago Boss выглядит слегка заброшенным. Вроде начали делать версию 0.9 и при этом за 3 предыдущие месяца всего 3 коммита.
      А так, для веба ещё N2O есть.


      1. Wargaming
        22.03.2016 10:20

        N2O узкоспециализированная вещь. Для наших задач не подходит.


    1. Wargaming
      21.03.2016 15:43

      Это в значительной мере дело вкуса. Но кое-где есть объективные проблемы. Например, некоторым новичкам требуются значительные усилия, чтобы разобраться, где ставить точку, где точку с запятой, а где просто запятую. Достаточно понаблюдать немного за тем, как пишут код новички, чтобы понять, что проблема есть)
      Увы, мы не знаем никого, кто использует Chicago Boss. В этом отношении даже Elixir/Phoenix лучше. Ибо есть разработчики = есть у кого попросить о помощи в случае проблем.


      1. erlyvideo
        22.03.2016 10:56

        мы использовали, поиграли и отложили.

        Эрланг своей иммутабельностью делает кошмарным использование ORM в стиле рельс: никакого кеширования, мемоизации и прочего


  1. q1t
    21.03.2016 14:23

    Недавно еще lfe(https://github.com/rvirding/lfe) зарелизился. для любителей)


    1. nwalker
      21.03.2016 18:00

      При всем уважении к Вирдингу, lfe не вызывает желания на нем писать. clojure, для сравнения, вызывает.


  1. alkanenko
    21.03.2016 14:58
    +4

    Хотел бы поблагодарить автора за отличный курс на хекслете. Спасибо.


  1. Source
    21.03.2016 15:11
    +1

    По-моему, Elixir как раз устраняет большую часть перечисленных вами минусов.
    Я тоже в прошлом году проводил сравнение Go и Elixir для узких мест. В итоге победил Elixir.
    Наиболее удивительным для меня фактом было то, что экосистема Elixir оказалась более зрелой, чем у Go. Несмотря на то, что Go на несколько лет старше, в его экосистеме до сих пор преобладает хаос и анархия. И при выборе библиотек остаётся только удивляться насколько распространен среди гоферов NIH-синдром.


    1. Wargaming
      21.03.2016 15:45

      В теории да, поэтому мы его и пробуем. А как оно получится на практике — увидим)


      1. merc
        21.03.2016 15:49

        Кстати, начал делать эликсир core бывший-contributor Rails Jose Valim.


    1. IvanPanfilov
      22.03.2016 10:28

      Несмотря на то, что Go на несколько лет старше, в его экосистеме до сих пор преобладает хаос и анархия
      так пользуйтесь стандартной либой. зачем тащить всякое стороннее говно в проект когда почти все есть в стандартной библиотеке


  1. acmnu
    21.03.2016 15:45

    Несколько раз пытался разобраться в Erlang/OTP и кроме недостатка литературы, о котором сказано в статье, натыкался на то, что я совсем не понимаю, как работать с состояниями и вводом-выводом (допускаю, что я просто глуп).

    По какой-то причине большинство статей о функциональных языках описывают разнообразные простые понятия: чистые функции, рекурсия, частичное применение, каррирование и так далее. В лучшем случае, про Erlang напишут как использовать gen_server. Все это любопытно, но не является чем-то действительно необычным или сложным для человека, который писал на современных языках.

    А вот как правильно хранить состояние, когда оно реально необходимо? Знаю есть несколько способов, но какой лучше и в каких условиях? Может лучше вообще всегда отдавать состояние на сторону (в Redis, например)? Или как платформа переваривает обращение к «грязным» функциям из модуля file? Это же невозможно (точнее не гарантирован корректный результат) в чистом функциональном языке, значит есть какой-то обходной путь? Я нашел всего несколько статей на эту тему, они были полезны, но я по-прежнему не могу сказать, что эта тема стала мне понятна.


    1. Wargaming
      21.03.2016 16:04

      Вопрос больше касается чистых функциональных языков, например Haskell. А про Haskell рассказать кратко я не могу)
      Что касается Erlang, то в нем можно (и нужно) делать грязные функции, и с вводом-выводом никаких сложностей нет. Для хранения состояния — gen_server. Ну а в целом применение Erlang на практике хорошо изложено в книге Фреда Хеберта Learn you some Erlang. Ну и в моем учебном курсе тоже :)


      1. acmnu
        21.03.2016 17:06

        За ссылки почитаю, спасибо.

        Для хранения состояния — gen_server.

        В смысле возврат функции с новым состоянием? Что-то типа:

        server(State)
          receive
            {async, Message} ->
                NewState = do_something(State,Message),
                server(NewState);
          end.

        Это относительно понятный код. Хотя весьма любопытно что там под капотом. Я так понимаю, что на уровне машинных кодов мы здесь просто обновляем значения в стеке.


        1. RomkoGoofique
          21.03.2016 17:45

          Новосконструированные значения занимают новое положение на куче. Куча у каждого процесса своя.

          Периодически (при достижении fullsweep или при уходе в hibernate) срабатывает GC и проходится по всей куче, удаляя ненужные значения и уплотняя старые.

          Факт того, что все структуры данных в Эрланге персистентные (иммутабельные, функциональные) позволяет заявлять, что ссылки в куче "направлены в одну сторону": благодаря этому можно чистить кучу в один проход.

          Таким образом, в Эрланге есть stop-the-world, просто он у каждого процесса свой, и длится недолго :)


        1. Wargaming
          22.03.2016 10:21

          Нет, не совсем так. Это достаточно долгая история, в двух словах не объяснишь.
          Посмотрите здесь https://ru.hexlet.io/courses/erlang_101/lessons/practical_erlang_gen_server/theory_unit


    1. helions8
      21.03.2016 20:52

      Может лучше вообще всегда отдавать состояние на сторону (в Redis, например)?

      Если действительно нужно хранить стейт (я убежден, что во многих случаях без него можно обойтись, это больше психологическая проблема), то всегда доступен встроенный ETS.


      1. sumerman
        21.03.2016 21:05

        Отнюдь, stateful app + sticky sessions = приложение которому не надо ходить в базу на каждый чих. Очень полезно например в играх.


        1. helions8
          21.03.2016 22:55

          А почему ETS не подходит для stateful app, что не устраивает? Производительность вроде достаточная (если там большие бинари не хранить, конечно же).


          1. sumerman
            21.03.2016 22:56

            Он прекрасно подходит, это "отнюдь" было про "во многих случаях без него можно обойтись"


            1. helions8
              21.03.2016 22:58

              А, ясно. Не так понял изначально.


      1. nwalker
        21.03.2016 21:14

        ets не всегда нужен, иногда достаточно отдельного процесса с кэшем, который логически располагается между, условно, БД и процессом-обработчиком запроса.


        1. sumerman
          21.03.2016 21:18

          В приведённом примере он нужен как минимум чтобы найти процесс, отвечающий за стейт пользователя.


          1. nwalker
            21.03.2016 21:24

            Ну, этот ets спрятан в процессе-регистраторе, и его может и не быть. Может, в нем для простоты map хранится.
            А без процесса-регистратора все равно не выйдет, потому что без него получается классический race condition «регистрация после старта».
            Ладно, может и выйдет, но я не могу так навскидку представить, как это реализовать.


            1. sumerman
              21.03.2016 21:27

              Не очень понял про рейс "регистрация после старта".

              Как бы то ни было, без спец-процесса не выйдет — нужно де-регистривоать по 'DOWN', и ETS в любом случае завернут будет. Мой поинт однако в том, что ETS это инструмент, который активно пользуется и которым не стоит пренебрегать.


              1. nwalker
                21.03.2016 22:36

                Конечно, я не агитирую против использования ETS, я просто предостерегаю от необдуманого использования.

                Вкратце, про рейс — если старт регистрируемого процесса происходит не в процессе-регистраторе, мы можем попасть в ситуацию, когда мы стартовали два инстанса регистрируемого процесса, один успешно регистрируется, попытка регистрации второго приводит к ошибке already_registered. Решается, насколько мне известно, только синхронным запуском процессов через регистратор, причем, я не уверен, является ли gproc:reg_or_locate/3 правильной реализацией.


                1. sumerman
                  21.03.2016 22:41

                  Я бы не сказал, что это прям таки рейс — already_registered ничто не мешает обработать и отвалиться, под simple_one_for_one с temporary детьми это вполне нормально работать будет.


                  1. nwalker
                    21.03.2016 22:57

                    Ну, технически это все-таки рейс.

                    Конечно, тут все сильно зависит от того, что мы пытались стартовать, зачем, как это обработается клиентом, если он есть, etc.


                    1. sumerman
                      21.03.2016 23:02

                      A race condition or race hazard is the behavior of an electronic, software or other system where the output is dependent on the sequence or timing of other uncontrollable events.
                      en.wikipedia.org/wiki/Race_condition

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

                      Если вызывавшая сторона делала что-то вроде `where` то он, при корректной реализации, дождется победителя и вернет его или отвалится по таймауту.


                      1. nwalker
                        21.03.2016 23:08

                        По моему опыту, вызывающая сторона чаще всего делает не where, а find_or_start, в котором и прячется внезапный рейс. А так как вызвающая сторона — процесс, с которым общается клиент, это чревато получением клиентом ошибки.

                        Это примерно такая же частая ошибка новичков, как упомянутый в статье дедлок gen_server:call.


                        1. sumerman
                          21.03.2016 23:11

                          Да хоть как функцию назвать можно, лишь бы корректно работала :) Новичкам тут всё одно — они в равной мере могут забыть и про процесс регистратор, и про правильную реализацию where.


                1. erlyvideo
                  22.03.2016 11:03

                  вот мы себе специально для этого сделали в gen_tracker механизм вменяемого неблокирующего, но атомарного старта


    1. erlyvideo
      22.03.2016 10:57

      не читайте про эрланг как про функциональный язык.

      Эрланг — это язык + платформа для написания сетевых сервисов. Забудьте про каррирование, речь идет о том, как сделать код, который можно будет обслуживать в продакшне


  1. shybovycha
    21.03.2016 16:04
    +1

    Довольно интересный обзор языка. Может стоило немножко поделиться опытом, например, как именно решаются проблеммы со строками, драйвером MySQL и как обнаружить переполнение mailbox? Как любитель Erlang, с удовольствием прочитал бы!

    Смущает один момент: проблемма именования переменных «Var1, Var2, Var3» как-то смахивает на проблемму разработчика, а не языка. Или я не понял контекста?


    1. Wargaming
      21.03.2016 16:40

      Опытом делиться будем. Не обещаю, что прям завтра и начнем, но будем.
      "Проблема именования переменных" — тут надо бы на конкретных примерах. И тогда будет ясно, кто виноват, язык или разработчик )


  1. saw_tooth
    21.03.2016 16:04

    А Вы рассматривали как альтернативу Erlang — Haskell. Насколько известно, у него есть достаточно удачные проекты для web, тот же WARP, yesod.


    1. Wargaming
      21.03.2016 16:43

      С Haskell многие из нас знакомы, но как язык для разработки веб-сервисов мы его не рассматриваем. Основная причина — нет накопленного индустрией опыта разработки и поддержки крупных веб-проектов. Ну например, в 19:05 внезапно на 200% выросло время ответа сервиса на http-запросы клиентов. Что делать, как с этим разбираться? С Erlang и Python мы знаем что делать, а с Haskell нет.


  1. angru
    21.03.2016 16:37

    Erlang обладает ужасным синтаксисом, который заставляет страдать
    Интересное мнение. У меня получилось наоборот. Есть tsung — система нагрузочного тестирования, написанная на эрланге. Несколько лет назад писал плагин для него(он поддерживает основные сетевые протоколы, но у нас был свой собственный, бинарный). Основы языка действительно изучаются довольно быстро, он ну очень простой, проще разве что только луа, синтаксис очень понравился, но это конечно вкусовщина.

    строками пользоваться не нужно, а использовать только binary
    поддерживаю, благодаря паттерн матчингу разбирать бинарные данные одно удовольствие и выглядит все это очень декларативно. Кстати, почему не упомянули об этом в статье, ведь это одно из основных достоинств языка


  1. afiskon
    21.03.2016 16:47
    +4

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

    В Erlang меня очень беспокоила скорость выполнения кода. Рабочая версия, что был выбран неподходящий под задачу язык, нужно было реально много считать, а Erlang тут не блещет. Также мне лично очень не хватало статической типизации в языке. Dialyzer — не то, слишком медленный, слишком прожорливый, находит далеко не все, половина разработчиков на него забивают. Ну и библиотеки, конечно же.

    После Erlang писал на Scala+Akka (тут про впечатления). Хочу сказать, что боли заметно меньше, так как все названные проблему реально решены. Ну и как бонус область применения заметно шире, в частности коллеги вполне успешно разрабатывали на Scala под Android. Если найти сеньера, который будет бить по рукам за Scalaz, Akka Cluster и Shapeless, все будет хорошо. Больше всего неудобств доставляла необходимость изображать легковесные процессы вручную на футурах. Это не сложно, но по неопотности можно собрать кучу гонок и других косяков.

    В настоящее время думается что оптимальный вариант для прикладного программирования — это все-таки Go. В нем решены все озвученные выше проблемы, плюс область применения еще шире, плюс для программирования на Go не требуется тяжелой IDE, можно обойтись простым Sublime Text. И на выходе не получаются jar-ники по 100-200 Мб размером.


  1. phantomit
    21.03.2016 17:09

    Не нужно внешнее in-memory хранилище (Radis, memchached), можно держать кэш прямо в оперативной памяти сервиса.

    Можете подробнее обьяснить на счет этого?


    1. Source
      21.03.2016 17:55

      В стандартной библиотеке Erlang есть решения, позволяющие обойтись без внешних программ для хранения кеша: http://erlang.org/doc/efficiency_guide/tablesDatabases.html
      На Хабре тоже этой темы касались: https://habrahabr.ru/post/245135/


    1. Xergin
      21.03.2016 19:24

      я думаю речь идет о http://erlang.org/doc/man/ets.html


    1. nwalker
      21.03.2016 21:08
      +1

      Возьмем для простоты обработку HTTP-запросов с пользовательскими сессиями.

      Наиболее распространенная на сегодняшний день модель — у нас есть N воркеров в виде независимых процессов, по ним round robin распределяются запросы пользователей. Все вроде бы работает, но для хранения сессии между запросами нам нужно либо прикреплять пользователей к воркерам, что не очень удобно, либо иметь некое внешнее хранилище для всех сессий — собственно, redis или memcached.

      В erlang подход несколько другой — мы запускаем один процесс ОС, в рамках которого на N ОС-тредах выполняется куча легковесных процессов(акторов), которые и выполняют пользовательские запросы. Причем, на каждый запрос создается новый актор, который умирает после ответа. Акторы дешевые, мы можем себе позволить такую модель выполнения.
      При этом сессии мы храним
      — либо в одном отдельном акторе — плохо, потому что, ботлнек.
      — либо в отдельном акторе на каждую сессию, время жизни которого слабо связано с актором-исполнителем запроса. Акторы-сессии идентифицируются ключем сессии. Это метод хранения не-shared данных.
      — либо, advanced level, в ets — in-memory-таблице, позволяющей доступ из нескольких процессов. Это для shared данных, вроде кэшей. В случае возникновения data race добавить дополнительные акторы, разруливающие запросы на запись.
      Любой из перечисленных способов быстрее хождения за данными в другой ОС процесс.

      Если не вдаваться в подробности — все.


    1. Wargaming
      22.03.2016 10:22

      В языках Python/Ruby/PHP обработчик http-запроса имеет свою область памяти, которая актуальна только на время обработки запроса. Если нужно хранить какие-то данные между запросами, то приходится обращаться к внешним сервисам, таким как Radis.

      В Erlang обработчику доступна вся память узла (не напрямую, конечно), и хранилище данных можно организовать прямо внутри сервиса. Например, с помощью ets-таблиц.


  1. synergy
    21.03.2016 18:00

    Насчет конфигов — тот же ejabberd переехал со стандартных эрланговых туплов на yaml. Как раз при переезде bigworld-проекта на новый сервер под jessie столкнулся с этим.


  1. square
    21.03.2016 18:33

    Очень мало библиотек у ирла, а те, что есть, часто заброшены даже не дойдя до стабильной версии. При этом довольно много дублирующихся либ, у каждой из которых есть свои сильные и слабые стороны, о которых надо знать, вообщем боль )

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


  1. alekciy
    21.03.2016 19:05

    К минусам, но не самого языка, а экосистемы вокруг него, стоит добавить узкий рынок вакансий в контексте веба. Я даже помню, как в начале появлялась вакансия (в том же яндексе, если не ошибаюсь) и все сразу такие "ооо, требуется erlang программист".


    1. Wargaming
      22.03.2016 10:41

      У нас часто бывают вакансии для Erlang-разработчиков) Можете мониторить http://wargaming.com/ru/careers/vacancies/ или отправить ссылку на резюме/резюме в личку. Добавим в базу, если будут подходящие места — свяжемся.


    1. IvanPanfilov
      22.03.2016 13:05

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

      удел erlang — бекенды для игровых серверов, чатов и подобных коммуникационных систем.


      1. Wargaming
        22.03.2016 13:39
        +1

        Кто они? :)

        Мы (Wargaming) видим смысл в том, чтобы использовать эрланг для веба. При желании этот смысл можно посчитать в человекочасах разработки или деньгах. Один и тот же веб-сервис можно построить на Python, а можно на Erlang. И после определенного уровня нагрузки, которую должен держать сервис, второй вариант выходит дешевле в человекочасах и деньгах.


        1. IvanPanfilov
          22.03.2016 14:45
          -1

          вы в своем коменте сейчас сами речь ведете об web заточеном под определенный круг задач.
          и много компаний пишут веб на эрланге? нет
          почему? если все так "просто" и "дешево"?

          узкий рынок вакансий в контексте веба
          это не минус erlang а общепринятые требования для данного сектора
          в ерланге строк нету — собрались комон веб приложения писать. ага.


          1. seriyPS
            22.03.2016 15:13

            В Wargaming используется микросервисная архитектура, где разные сервисы взаимодействуют между собой как по HTTP-протоколу, так и по AMQP.

            Веб тоже разный бывает. Бывает в монолитном виде "отвалидировать форму, сходить в БД, что-то посчитать, провернуть какие-то бизнес-операции, выплюнуть HTML-ку с данными и менюшками". Обычно так на ранних стадиях проекта работает.
            А когда к проекту начинают прикручивать платёжную систему, рассылку нотификаций на различные устройства, множество методов авторизации, мобильную версию, API, мобильные приложения и пр, то эти задачи реализовываются уже в отдельных приложениях-микросервисах, которые принимают на вход JSON, возвращают JSON и всё. После этого "веб" превращается в тонкую прослойку — пользовательский интерфейс, который хоть на PHP можно писать. Валидатор форм и шаблонизатор.
            Вся остальная "мякота" именно в этих микросервисах.


  1. Tiendil
    21.03.2016 22:31
    -7

    «Мыши плакали, кололись, но продолжали грызть кактус»


  1. netslavehq
    21.03.2016 22:52

    Wargaming

    … нет веб-фреймворка

    Смотрели ли вы на N2O ?


    1. Wargaming
      22.03.2016 10:42

      Да, конечно. Это узкоспециализированный фреймворк. Он нам не подходит.


  1. mrjj
    22.03.2016 01:42

    Зачем нужен Twisted, когда есть Tornado? Простой как три палки, поддерживаемый и шустрый.


    1. IvanPanfilov
      22.03.2016 13:01
      +1

      aiohttp еще проще


  1. Begetan
    22.03.2016 09:07
    -6

    Интересная статья, редкий отзыв с живым впечатлением!

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

    Если брать пример со строками — то ка раз в Go отличная работа со строками, все нативно обрабатывается в UTF8, можете переменные на китайском или русском в коде писать, и как угодно обрабатывать строки без боли.

    Я не знаю как будет восприниматься Go после Pyton, но во всяком случае в каждом отзыве пишут что скорость разработки в 10 раз быстрее чем на C/C++. Это действительно очень простой язык и комфортный.

    Я бы его сегодня сделал учебным в школах.


  1. eryx67
    22.03.2016 10:07
    +4

    Erlang успешно использую уже лет 10. Для своих задач исключительно хорош, практически уникален.

    Небольшой пример из последних проектов — распределенный краулер BitTorrent DHT сети.
    40 тысяч DHT-нод на трех машинах, 200 тыс запросов в секунду.

    Без возможностей Erlang по runtime интроспекции и трассировке процессов пилил бы год-два,
    так как реальное поведение мировой DHTсети torrent-клиентов оказалось весьма далеким от
    первоначального представления.

    Erlang позволил запустить проект за полтора месяца.

    Итог — накопано 20 млн уникальных торрентов за два года, не считая другой полезной информации
    о состоянии сети.


  1. Nmaster
    22.03.2016 10:51
    +3


    Видео доклада с митапа func (by)


  1. stalkerg
    27.03.2016 00:25

    Спасибо за обзор, было очень интересно (часто писал/пишу на Python/Tornado/Pylons). Как обстоят дела с асинхронностью в Erlang?
    Есть ли возможность писать явно асинхронный код ака asynс из Python 3.5?


    1. develop7
      27.03.2016 10:38
      +2

      Ох. Мягко (и коротко) говоря — да.