Однажды я устроился в проект на Erlang. Вообще мой профиль тогда был в основном Java и немного BigData. Но по результатам собеседования договорились что я попробую написать небольшое тестовое задание - и сам пойму нравится ли мне язык - и ребята оценят, гожусь ли я им. Ну и за выходные справился - язык непривычный но не очень сложный - и интересный, с необычными "фишками". Команде же, куда меня взяли, оказался удобен мой опыт в бигдате и амазоне.

Сейчас я расскажу на паре примеров про язык - он действительно по-своему классный (если вам нравится функциональное программирование - это для вас) - и про то как, однако, он оказался неудачным выбором для проекта (данного-конкретного).

Статья для широкого круга читателей, не знакомых с языком - знатоки же Эрланга в частности и ФП вообще возможно найдут неточности в моём повествовании - дело было лет 6 назад - так что можете смело поправлять и даже ругать при необходимости :)

Бэкграунд

Проект, куда я пришёл, разрабатывал полезные сервисы для тех, кто держит свои приложения в Амазоне. Например у вас интернациональный онлайн-магазинчик кормов для улиток - и вам нужно записывать логи транзакций для аудита - но тут вы обнаруживаете:

  • для европейских и для американских покупателей транзакции требуется сохранять в разные регионы амазона (в европейский и американский, это GDPR)

  • старые логи хорошо бы перебрасывать в "холодное" (более дешевое) хранилище

  • а ещё по ним может потребоваться (когда случится аудит) искать, скажем, за 7 лет

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

Основоположники команды были выходцами из телекома и прямо говорили "да, мы пишем на Эрланге потому что он клёвый и нам нравится". Иными словами это во многом была тоже БигДата, но в довольно непривычном виде.

Про сам Erlang

Эрланг это один из самых "чисто-функциональных" языков - быть может он уступает только Haskell, но в том из-за суперстрогой типизации мозг можно свихнуть - а здесь наоборот типизация довольно слабая. Сам язык интерпретируемый, хотя и компилируется в некий байт-код выполняемый виртуальной машиной (BEAM). Примерно как в Java.

Почему "чисто-функциональный"? А вот:

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

Также нет циклов! Вместо них используется рекурсия (преимущественно, хвостовая).

Все сложные объекты данных тоже иммутабельны! На базовом уровне там есть списки и кортежи (tuples). Например, в списке ничего поменять нельзя, но можно использовать этот список пририсовав к нему ещё один элемент - получится более длинный список для которого исходный является частью. Таким образом в исходном ничего не поменялось.

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

Треды и Мессаджи - очень похоже на то что сейчас есть в Go - только треды в Эрланге можно запускать миллионами штук - а мессаджи принимаются немного более хитроумно (например если первый мессадж в очереди не смэтчился ни с чем, будет проверен второй и далее).

Кроме того - всё это было придумано очень давно - Эрланг позволяет строить кластера из эрланговых инстансов и запускать приложения почти бесшовно взаимодействующие между этими инстансами. Т.е. внешняя очередь сообщений не требуется - его собственные мессаджи умеют ходить между инстансами. Впрочем, эта фишка в проекте не использовалась - в основном мы запускали эрланг на виртуальных машинах амазона или в его "лямбдах".

Пара Примеров

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

Двойной вложенный цикл с простой арифметикой - эта программа строит последовательность из "гипотезы Коллатца" (школьная задачка) для каждого числа от единицы до N - и считает сумму длинн этих последовательностей.

-module(collatz).
-export([start/0]).

collatz(N, R) ->
  if
    N == 1 -> R;
    N rem 2 > 0 -> collatz(3 * N + 1, R + 1);
    true -> collatz(N div 2, R + 1)
  end.

total(0, R) -> R;
total(N, R) -> total(N - 1, collatz(N, 0) + R).

start() ->
  N = list_to_integer(os:getenv("MAXN")),
  Res = total(N, 0),
  io:format("sum=~b, avg=~g\n", [Res, Res / N]).

В первой строчке мы придумываем название для модуля, во второй описываем единственную доступную наружу функцию start c 0 аргументов.

Функция start в 14-й строчке делает следующее - получает значение N из переменных среды, вызывает вычисление (функция total ) и печатает результат.

Функция total это внешний цикл, бегущий от 1 до N (точнее наоборот) - она имеет два аргумента - до скольки считать - и "аккумулятор" для результата - типичная особенность функций с "хвостовой рекурсией". Мы каждый раз вызывая эту функцию рекурсивно будем передавать в этот параметр сосчитанное на данном шаге плюс полученное с предыдущих шагов - а на самом последнем по вложенности вызове просто вернём этот параметр как результат.

И вот она описана двумя строчками - тут используется "мэтчинг" по параметрам - если её вызвали для N=0, то нужно просто вернуть результат из второго аргумента. В противном случае нужно вызвать функцию collatz для текущего значения N чтобы узнать собственно длину последовательности - её мы прибавим к текущему значению результата - и совершим рекурсивный вызов с уменьшенным значением N-1.

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

Наконец функция collatz - она очень похожа, но "мэтчинг" здесь осуществляется за счет конструкции if-end - она больше похожа на switch из других языков - проверяются перечисленные условия - и какое "сыграет" - соответствующий результат и будет значением if-end (и всей функции).

Если вы решите поиграться с Эрлангом, возможно вы некоторое время потратите на то чтобы понять как программа запускается. Там нужно при запуске указать вызываемую экспортированную функцию - в общем, лучше подсмотрите в файле run.sh репозитория.

Второй пример - на использование массива - Простые Числа - здесь мы генерируем (простым способом trial division) массив простых чисел. Причем массив используется и для поиска делителей следующих чисел. Здесь нас ожидает небольшая засада связанная с "функциональностью" языка. Как уже говорилось, для последовательностей в нём есть списки (list) но к списку можно добавлять элементы только с одной стороны - и итерировать потом по списку тоже можно только с этой стороны. А нам-то надо добавлять новые найденные числа в конец, а делители перебирать каждый раз от начала. То есть нужен не односвязный список а очередь или массив. Что же, массивы в эрланге есть, хотя и с нюансом - давайте посмотрим.

-module(primes).
-export([start/0]).

divfound(P, I, A) ->
  D = array:get(I, P),
  if
    D * D > A -> false;
    A rem D == 0 -> true;
    true -> divfound(P, I+1, A)
  end.

primes(N, P, A) ->
  S = array:size(P),
  if
    N == S -> P;
    true -> case divfound(P, 0, A) of
      true -> primes(N, P, A+2);
      false -> primes(N, array:set(array:size(P), A, P), A+2)
    end
  end.

start() ->
  N = list_to_integer(os:getenv("MAXN")),
  io:format("primes[~b] = ~b~n", [N, array:get(N-1, primes(N, array:from_list([2, 3, 5, 7]), 9))]).

Здесь в подробности углубляться уже не придётся - структура программы похожа на предыдущую - две внутренних рекурсивных функции primes и divfound - обеспечивают нам вычисление в два вложенных цикла. Основное что мы заметим - "аккумулятор" теперь это не скаляр (число) а массив, к которому добавляются числа. Причем каждый раз когда мы что-то добавляем или меняем в массиве - мы получаем новый массив (это достигается за счет мудрёной внутренней структуры на кортежах) - который передаём в следующую итерацию цикла - то есть в нижележащий рекурсивный вызов.

Разобрать логику я осмелюсь оставить вам на самостоятельное изучение (кому интересно) - но главное заметим, с массивом мы работаем через функции специального модуля array - как я уже сказал, массивы на самом деле это деревья из кортежей - и добавляя число мы получаем новый кортеж с одной из ветвей которого "свисает" старое дерево, как-то так.

Из этого следует один неприятный момент - такие массивы, конечно, не работают так же быстро как обычные. В терминах алгоритмической "сложности" их быстродействие приблизительно сравнимо с логарифмом от количества элементов - то есть O(log(N)).

Грузить вас прочими структурами данных и работой с тредами и мессаджами мы здесь не будем (скорее всего вы легко найдете подходящие тьюториалы - и возможно оцените насколько это увлекательно реализовано). Перейдём к заключительной части нашей эпопеи.

И как же мы сели в лужу?

До смешного, или до обидного просто. Всегда странно, когда оглядываешься назад, как же можно было упустить такую банальную вещь. Но обо всём по порядку.

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

Накапливающиеся от кастомера логи складываются в бинарные пакеты довольно сложной конструкции и все это хранится в S3 хранилище амазона. Чтобы работать с пакетами в хранилище эффективно мы обычно использовали "лямбды" - это в амазоне есть такой сервис - ты можешь программно, буквально одним щелчком запустить на короткое время (до 5 мин) большое количество (до 1000 кажется) небольших виртуалок, чтобы запроцессить требуемый объём данных.

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

Я в это время был занят другим небольшим сервисом - он копировал (тоже с помощью лямбд) пакеты в "холодное" хранилище для удешевления хранения старых данных. И вот я с этим справился - и меня зовут присоединиться к группе ребят которая занимается поиском.

Потому что там небольшая трабла которую надо поресерчить - работает медленно.

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

  1. Erlang работает медленно сам по себе - немного быстрее CPython (по тем временам). Это не удивительно - язык интерпретируемый. Сейчас ситуация заметно лучше - в 24-й версии добавили JIT-компилятор - но тогда он был доступен только как сайд-проект.

  2. Дополнительно тормозят операции с иммутабельными структурами данных. Пакеты в основном были представлены как binary - грубо говоря иммутабельная строка байт, которую можно индексировать или слайсить. Любое изменение требует, естественно, скопировать весь binary (то есть, возможно, весь пакет на несколько килобайт).

Ну, что значит "медленно"? С точки зрения пользователя это выглядело так, что просто запускается в 100 раз больше лямбд (потому что пакеты разбираются в 100 раз медленнее чем хочется) - и в результате один поисковый запрос стоит не 5 центов допустим а 5 долларов. Боль.

Заключение

В общем, хотя мы потратили ещё 2-3 месяца на попытку так и сяк ускорить работу, переписать что-то на Си - исправить ситуацию решительно не удалось. Либа для разбора наших кастомных пакетов была написана (на Эрланге же) отцами основателями, доки на формат не было и переделать бы её саму - но всем страшно.

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

В то же время, речь не идёт о том что Эрланг плохой!

Действительно, в большом количестве проектов быстродействие самого языка не имеет такого критического значения - зачастую основные потери времени - на операциях ввода/вывода, передачи по сети. Конкретно здесь - на bulk-processing данных - типичная для BigData ситуация - действительно это был не лучший вариант. Кто заглядывал в недра Hadoop, наверняка замечал, например, что он хоть и написан на Java - но очень "не по-java-нски" - в частности иммутабельных данных там избегают (иногда это оказывается сюрпризом).

В общем надеюсь по крайней мере энтузиасты ФП у кого до сих пор руки до Erlang не доходили - заинтересуются его попробовать. Можно не возиться с установкой, а использовать докер-образ.

Из основных минусов я бы добавил только что язык немного архаичен (по-моему до сих пор исходные файлы невозможно разложить по вложенным директориям например) - и у него сравнительно небольшое коммьюнити, что может осложнять поиск какой-то специфической инфы. Тем не менее документация подробна и её много. Улучшения появляются стабильно - и ненулевое число популярных проектов на нём написаны (наверное наиболее на слуху Rabbit MQ).

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


  1. heiheshang
    10.10.2024 14:10

    Вот интересно, а можно было без лямбд обойтись ? В чем необходимость такого решения ?


    1. RodionGork Автор
      10.10.2024 14:10

      Смысл лямбд простой - они эластичны. Не нужно держать ресурсы пока у вас нет задачи. Ну и большинство "эластичных" сервисов амазона на это расчитаны. То есть платите только за "машинное время" потраченное на ваши вычисления. И то что их можно запустить больше или меньше в зависимости например от объёма логов - это облегчает получение результата за примерно ожидаемое время.

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

      Так что лямбды тут (тем более что весь проект для сервисов на амазоне) - вполне обусловлены.


    1. Shatun
      10.10.2024 14:10

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

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


  1. Dart55
    10.10.2024 14:10

    Либа для разбора наших кастомных пакетов была написана (на Эрланге же) отцами основателями, доки на формат не было и переделать бы её саму - но всем страшно.

    Дело было не бабине Erlang...


    1. RodionGork Автор
      10.10.2024 14:10

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

      Вообще ситуация не уникальная. В spark например можно на питоне писать (удобнее) а можно на scala (быстрее). Интерпретируемые языки в бигдате надо с осторожностью юзать.


  1. 1nd1go
    10.10.2024 14:10

    Можешь поделиться тестовым заданием, которое выдали чтобы самому разобраться и ознакомиться с языком?


    1. RodionGork Автор
      10.10.2024 14:10

      Не проблема :) валяется у меня на гитхабе - в ридми постановка задачи как мне ее прислали.

      https://github.com/RodionGork/erlang-movavg

      Там видно что задачка не специфична для эрланга.Если захочется попробовать сначала на более обычном языке - то я как раз недавно воссоздал ее вот тут (это я планирую сделать сайт с разными заданиями собесов и т.п. но пока вещь в зачаточном состоянии) https://ca4pro.com/index/task_view/moving-average-on-metrics


  1. napolskih
    10.10.2024 14:10

    Рекомендую взглянуть на язык Elixir. Это просто праздник какой-то!


    1. RodionGork Автор
      10.10.2024 14:10

      Я краем глаза заглядывал - помесь Эрланга с Руби или что-то в этом духе. В принципе пошёл бы поработать - но проекты что на него что на Эрланг появляются буквально по нескольку штук в год.


  1. Tzimie
    10.10.2024 14:10

    И что было дальше? Всем спасибо за сотрудничество, до свидания?


    1. RodionGork Автор
      10.10.2024 14:10

      Ну как обычно в таких случаях часть команды от скуки разбегается (и я в том числе) а проект (в лице оставшихся, менеджеров и пр) отряхивается от гуана, как-то оправдывается и через год-другой снова дымит как ни в чем не бывало. Это всё ж не самый критичный фэйл был - просто не успели к заявленным срокам, это было к амазоновской ежегодной конференции приурочено и т.п... В общем, никто не умер.


  1. Sly_tom_cat
    10.10.2024 14:10

    Тут недавно попался на эрлане один легаси проектик на починку. Пошел читать - что за зверь такой.....

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

    Ну реально, все должно возвращать результат. А раз так - в цикл - никак - только рекурсия. Вместо if определяется пачка функций с одной сигнатурой, но разным сочетанием входных параметров, часть из которых заменяется константами. А, да, if то все-таки есть, точнее он не if, а почему-то case???. Мессаджинг ну да, есть, но тоже какой-то загадочный и т.к. ... да, да все должно возвращать результат..... ну во общем и обмен сообщениями тоже изрядно через задний проход.

    Как же хорошо, что оказалось, что там не в коде проблема, а провайдер API погасил, который этот проект использовал. Ну новый API есть только ни разу не совместим со старым и с-но надо новое писать практически с нуля. Поэтому erlang пошел в топку. RIP.

    И, да, поглядев на проект первое, что пришло в голову "да ну его на..., проще на go переписать".


    1. RodionGork Автор
      10.10.2024 14:10

      и поразился сумрачности разума того, кто такое придумать смог.

      судя по Вашим возмущениям ФП по большей части обошло Вас стороной :) ну это необязательно плохо, хайп по нему сейчас маленько поутих - куда-то провалилась 3-я скала которую так ждали, сгинул 2020-й хаскель - зато очень далёкий от ФП Golang наоборот на коне. Ну я не говорю что это плохо - скорее любопытно рассматривать как меняются интересы сообщества, качаются туда-сюда. Лет дцать назад один тип в фидо с пеной у рта доказывал что скоро всё программирование будет функциональным, а компьютеры - квантовыми. Ну пока не случилось.

      проще на go переписать

      ну так всегда кажется что на языке Х переписать с нуля проще. пока не переписал и не устроил очередное болото :)


      1. Sly_tom_cat
        10.10.2024 14:10

        Да нет, например Lisp мне в студенческие годы зашел так хорошо, что я даже дипломную работу сделал на пару с однокурсником: типа Turbo Lisp (на мне интерпретатор, на нем IDE на основе библиотеки от Borland, в которой был сделан Turbo Pascal). Ох и давно это было, сейчас мало кто вспомнит эти названия.....

        Да и другой всякой экзотики я пробовал много. Там fortran, и forth, и prolog, и даже java самая первая...

        Но вот erlang не зашел. Видимо - не мое.

        А go у меня, можно сказать, основной инструмент, ну еще иногда немного python, bash и прочее. Так что переписать на хорошо знакомом иногда действительно проще чем перелопачивать чужой код да еще на довольно загадочном языке.


        1. RodionGork Автор
          10.10.2024 14:10

          Ну сам по себе Лисп - он не очень функциональный, он вполне позволяет в императивном стиле писать... А вот упомянутый пролог (если мы из времён Борланда - то наверное Турбо-Пролог вспомним) - он же кстати очень-очень подозрительно на Эрланг похож - все эти списки, мэтчинг... Правдоподобно звучит предположение что Эрланг возник как упрощённая и более приближенная к реальности переделка Пролога.


          1. gBear
            10.10.2024 14:10

            Правдоподобно звучит предположение что Эрланг возник как упрощённая и более приближенная к реальности переделка Пролога.

            первые две строки :-)


          1. Sly_tom_cat
            10.10.2024 14:10

            Ну prolog (чисто по моему мнению) все же сильно дальше от понятия функционального программирования чем Lisp. Я не пробовал на нем что-то рабочее писать и от для меня остался надстройкой над Lisp (его интерпретатор на Lisp реализуется в 20 или 30 строк, точно уже не вспомню) и это уже по сути экспертная система - то самое на что, в определенную эпоху, ставили как на самую правильную реализацию AI. И тут статья была про одного Чудака (с большой буквы), который до последних дней сам и с последователями набивал фактами экспертную систему которую начал создавать именно в ту эпоху, когда казалось что вот еще чуть-чуть докинуть фактов в экспертку и комп будет умнее человека.


            1. RodionGork Автор
              10.10.2024 14:10

              Ну Вы правы что он не для ФП, он же для почти гипотетического "логического программирования" - но если на нём писать "что-то рабочее" - то как раз код будет сильно напоминать то ли Lisp/Scheme, то ли Erlang - те же рекуррентные определения, списки и т.п. Хотя действительно это немного не то для чего он придуман...


      1. 1nd1go
        10.10.2024 14:10

        Я склоняюсь к мнению, что ФП все таки для промышленного программирования не очень подходит и изза этого хайп поутих. Мощные и компактно написанные конструкции тяжело обратно понять при чтении кода. Проблема с таким ФП кодом есть наглядная демонстрация цитаты Кернигана "Отладка кода вдвое сложнее, чем его написание. Так что если вы пишете код настолько умно, насколько можете, то вы по определению недостаточно сообразительны, чтобы его отлаживать", только к расширению и изменению это тоже относится. Когда система начинает разрастаться, новые люди подключаются к команде - все это выстреливает и код поддерживать становится очень сложно. А дальше "часть команды от скуки разбегается".

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


        1. RodionGork Автор
          10.10.2024 14:10

          На мой взгляд промышленное программирование очень разное ведь бывает :) где нет вот этого bulk-processing, где не критична голая производительность - ФП более менее заходит. Ну даже польза есть (насчет паралеллизма и т.п.) - так-то если вдуматься - работа веб-сервиса во многом хорошо ложится на ФП - принимаем данные реквестом, отдаём респонзом - каждый эндпойнт можно функцией описать. То есть ФП хорошо в какой-то "фреймворк" обернуть.

          Как мне кажется, порой беда с ФП не в языках а в программистах. Не в том смысле что они плохие - но вот именно как Вы описываете - стараются разрабатывать ну так умно, что никто не готов связываться с их творчеством. Наглядный пример по-моему - применение Haskell в компании Biocad. Теоретически логично - чем строже типизация - тем лучше ошибки на этапе компиляции отлавливаются. Но на деле выливается в то что и разработчиков (не то что знающих, но даже желающих изучить этот довольно мозголомный язык) трудно сыскать - и всевозможных библиотек под него написано не так много, и приходится писать самим то и сё, тратя на это в общем-то бизнес-ресурс или свободное время.


  1. gBear
    10.10.2024 14:10

    При всём уважении, это - какой-то сумбур :-(

    Начиная от "функциональный" и "довольно слабой" типизации c "присвоением", и до примеров кода, который ужасен. Даже разбирать не хочется.

    Но...

    Любое изменение требует, естественно, скопировать весь binary ...

    Уверены, что проблема была именно в этом? Что bin_opt_info выдавало? Dialyzer что показывал?

    Ну просто, чтоб заставить BEAM копировать binary - это прям специально стараться надо... и то, не факт, что получится.


    1. RodionGork Автор
      10.10.2024 14:10

      Уверен, хотя не думаю что смогу вам привести подробности 6-летней давности.

      По остальному... Ну можете сами написать статью, тьюториал, что угодно. Или возьмите второй пример и сделайте так чтобы он работал в 5-10 раз быстрее. Это будет конструктивно и интересно. Я безусловно многого не помню, многого не знаю об Erlang - так что ничего не имею против узнать как с ним работают профи.


      1. gBear
        10.10.2024 14:10

        Уверен, хотя не думаю что смогу вам привести подробности 6-летней давности.

        А на чём базируется ваша уверенность?! Я - грешным делом - с Erlang знаком хорошо. И ваше утверждение что проблема в "копировании binary" - лично для меня выглядит ну очень странно.

        Binary (это вот "такое" <<>>... вы же про него?) - отдельный, специальный тип данных. Он - by desing - сделан так, чтоб избежать копирования в пределах VM. И то, для чего Erlang и был придуман, как бы и есть обработка "непрерывного потока" binary. Поэтому - "странно всё это" (с)

        Или возьмите второй пример и сделайте так чтобы он работал в 5-10 раз быстрее. Это будет конструктивно и интересно

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

        Канонически, тут "поиск простых чисел" делается - вот буквально - на "решете" и ленивых последовательностях. И память не жрет, и работает быстро.


  1. Gutt
    10.10.2024 14:10

    Интересно, вообще затык «жрёт в 100 раз больше CPU, чем планировалось» не выглядит как причина не выкатить проект в срок. Даже если реверсить код лень, то не храните архивные логи в проприетарном бинарном формате, возьмите какой-нибудь стриминговый компрессор. А после релиза можно и формат разобрать, и быструю разворачивалку запилить.


  1. jbourne
    10.10.2024 14:10

    Вот главные фишки Erlang от его создателей в ролике 40-летней давности:

    https://www.youtube.com/watch?v=xrIjfIjssLE&ab_channel=uncertainyesterday

    Видно на чем делали упор и интересующие проблемы того времени.

    Просто как дополнение к вашей статье.


    1. RodionGork Автор
      10.10.2024 14:10

      спасибо, сам с любопытством гляну :)