Annual Open Tech Conference - ISsoft Insights 2021. June 19. Learn more.
×Закрыть

Elixir — мова для роботи з I/O. Синтаксис, документація та спільнота

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті.

Мова для роботи з I/O: саме так описує Elixir José Valim — автор мови програмування.

Elixir — відносно молода мова, її розробка почалася у 2011 році, а перша версія була вже у 2014. Про Elixir я почув вперше у 2016 році від співробітника. Зовсім незабаром, José Valim виступив у Львові на Pivorak meetup з доповіддю Phoenix Framework for the new web. От якраз перед цією доповіддю я познайомився з мовою та середовищем ближче. Ну так, в загальному, щоб розуміти максимум з доповіді :D І мені там це продали — 2 мільйони websocket з’єднань на одній машині, можливість бачити μs в логах, візуальна схожість з Ruby, функційне програмування. Під кінець року мені вже вдалося влипнути в написання продакшн коду на Elixir.

От, все, що ви чули файне про Erlang, про високу конкурентність, високодоступні системи, толерантність до відмов і т.д., наслідує й Elixir, власне як і будь-яка інша мова на Erlang VM. Але Elixir акцентує додатково увагу на тих речах, де Erlang кульгає — на активній спільноті, на доступності для новачків та на DX (Developer Experience).

Я б не хотів копіювати базову інформацію з Вікіпедії, тому просто раджу швидко ознайомитись самотужки Elixir (programming language), там лаконічно описано про основні фічі мови та є приклади коду. Натомість я опишу особливості, а відносити їх до плюсів чи до мінусів — вирішуйте самі.

Синтаксис

На жаль, в сучасному світі синтаксис мови програмування відіграє більшу роль, ніж цього хотілося б. Люди легше засвоюють речі, які подібні до того, що вже відомо і цей процес є закономірним. Візуальна схожість з Ruby притягує програмістів: згідно з опитуванням Elixir Survey 2020, близько половини людей прийшли з Ruby-спільноти. Це свого роду один з пунктів продажу мови: Elixir — то як Ruby, тільки трішки інший. Є шматки коду, де Elixir і Ruby не відрізниш, але є ще більше, де навпаки. Після детальнішого знайомства все стає на свої місця: Elixir — це Erlang, поданий під іншим соусом.

Багатьом людям дуже не подобається синтаксис Erlang, який бере своє коріння з Prolog. Зрештою, перша версія Erlang була на ньому написана.

José Valim хотів мати Ruby на віртуальній машині Erlang і у своїх перших версіях Elixir була об’єктно орієнтованою мовою і не працювала нормально. З того часу мова суттєво еволюціонувала, ось маленький приклад з викликом функції map:

% перший концепт Elixir
[1, 2, 3].map do (x)
  x * 2
end 
# Ruby
[1, 2, 3].map do |x|
  x * 2
end
# сучасний Elixir
Enum.map([1,2,3], fn x ->
  x * 2
end)
% Erlang
lists:map(fun(X) ->             	 
  X * 2
end,
[1,2,3]).

Можна відкрити старий readme і подивитися більше прикладів синтаксису з ідеями того часу. Ось тут є проміжний дизайн мови, яка стала наріжним каменем того, що ми маємо зараз.

Віртуальна машина та середовище виконання

Elixir як мова, мабуть, не була б такою привабливою, якби не BEAM. Це наразі основна імплементація віртуальної машини для Erlang, яка підтримується компанією Ericsson.

Чи не головною особливістю Erlang є те, що він розроблявся для проєктування систем реального часу, через це планувальник задач має змогу використовувати витискальну систему багатозадачності (preemptive multitasking). Це означає, що планувальник не буде чекати, поки процес добровільно погодиться на зміну контексту, а зробить це примусово по лічильнику редукцій (викликів функцій). Тобто навіть коли процесор повністю навантажений по всіх ядрах CPU-інтенсивними без I/O задачами, планувальник завжди знайде місце для ще одного маленького HTTP-запита на 5 мілісекунд і не доведеться йому чекати в черзі, поки інші завдання звільнять ресурси. Функційне програмування в Erlang — це практична необхідність, яка дозволяє реалізувати такий підхід.

Інструменти для зневаджування — це ще одна з сильних сторін в Erlang. Є можливість досить легко під’єднатися до запущеної машини в продакшені, знайти процес віртуальної машини, подивитися його стан та подивитися які виклики функцій він зараз робить. Насправді можна багато чого іншого ще там дізнатися та є досить багато інструментів для цього. Вас це зовсім не вражає? А от мене дуже вразило :) Особливо, коли знадобилося на практиці вперше. На одному з проєктів, де я брав участь, була ситуація, коли після 10 хвилин сесії вона в рідкісних випадках блокувалась — це було на вебсокетах і нові події з бекенду просто не приходили. Я під’єднався до машини, побачив, що процес, який відповідає за сесію живий, його стан не міняється, він щось там собі рахує, подивився що конкретно. Проблема була в такій функції:

left_rotate([1, 2, 3], 2) # [3, 1, 2]
left_rotate([1, 2, 3], 0) # [1, 2, 3]

вона відкушує голову списку і кладе в кінець стільки разів, допоки другий аргумент не зменшиться до 0. Ну і вгадайте що пішло не так? Після рефакторингу в другий елемент прилетіло від’ємне значення ¯\_(ツ)_/¯. Оскільки я в той день вже не мав змоги над тим працювати, то я спокійно собі продовжив роботу на наступний — нічого за той час не лягло, оперативка не закінчилась, не було ніяких інших проблем в сусідніх процесів, процес далі жив, проєкт працював. Я не звик до такої стабільності!

Робив я ще якось запити на внутрішній сервіс, написаний на мові X, з Elixir — це було звичайне завантаження файлів, але я якось незвично склав запит, мідлвара X не могла розібрати дані і сервер X стабільно злягав. Для мене це було несподіванкою. Я знав, що таке може бути, але не очікував, що в мене! Ну вже мали б ту бібліотеку на декілька тисяч зірочок вилизати! Я, звичайно, не зробив патч туди, позаяк був захоплений обсиранням X серед співробітників. Девопс все одно кубернетісами то обклав, недарма ж йому гроші платять. В роботі з BEAM такий випадок неможливий, треба навмисне написати щось типу кривого NIF, щоб система злягла.

Що я хотів цим сказати? Що стійкість до відмов як фундаментальна особливість надзвичайно важлива для хорошого сну.

Ще розвивається альтернатива — lumen — все як має бути — на Rust і щоб можна було в браузері запускати — вже навіть хеллоу ворлди працюють.

Модель акторів

Суперлегка модель для розуміння та використання, принаймні зараз так здається.

Пишеш синхронний блокувальний код і не хвилюєшся як ця функція має викликатися. Не треба бавитися в callback-hell, не треба в коді зазначати, що якась функція є особливою, позначаючи її ключовим словом async. Немає основного треду, блокування якого треба уникати. Процесів є багато і вони спілкуються між собою повідомленнями асинхронно.

Не раз чув як пітоністи в сусідній кімнаті обговорювали асинхронний код, наче якесь таїнство, для якого треба особливі навички. На Elixir проєкті в нас таких розмов навіть немає — ці проблеми вже вирішені за нас і ми займаємось більш високорівневими задачами. Високорівневою мовою програмування користуємось, як-не-як.

Документація і стандартна бібліотека

Окрім того, що на документацію приємно дивитися естетично hexdocs.pm/elixir/Kernel.html, вона в Elixir спільноті вважається не менш важливою ніж код, тому PR з покращеннями охоче вітаються. Документацію чи не до всіх бібліотек можна знайти на https://hexdocs.pm, вона не розкидана по wiki сторінкам на github, readme та самопальних вебсайтах. Останні не є мінусом, коли головне щоб хоч якась документація була, але стандартизація для таких речей це зручно.

Стандартна бібліотека не перевантажена, але і не вбога. Наприклад, вам не треба додавати додаткову залежність чи копіювати реалізацію функції Enum.uniq/1 з StackOverflow, не треба на String.starts_with?/2 чекати восьмої версії мови. Але тут немає JSON в стандартній бібліотеці як це є в Ruby, немає вбудованого модуля Oauth як це є в Crystal. Для цього потрібні сторонні рішення. Водночас величезний багаж різних модулів та функцій, поставляється відразу в Erlang/OTP, тому модуль для роботи з zip, для прикладу, все-таки доступний відразу. Варто зауважити, що для роботи з Elixir знання Erlang не вимагаються, а коли доведеться працювати з якимись Erlang модулями, то розібратися можна надзвичайно швидко, через те, що Erlang теж функційна мова і там різниця буквально лише в синтаксисі. Та і не треба ментально перемикатися на іншу парадигму. Clojure, на противагу, повинна вживатися якось в об’єктноорієнтовану екосистему JVM.

Функційне програмування

Так, ось тут воно є і тільки воно*. Однак José не рекомендує продавати мову як ФП, це не основна фішка.

Варто лиш зауважити, що ФП в Elixir це не те саме, що ФП в Haskell. Тут не обов’язково знати що таке аплікативний функтор та монада-трансформер. В усе це можна вдаритись, але сенсу особливого тут немає, через те, що йде всупереч тому, як пише вся спільнота, до того ж мова динамічно типізована.

В Elixir, як і в багатьох інших функційних мовах, для контролю потоку не використовують Exception Driven Development — помилки заведено обробляти як звичайні значення. Для зручного компонування таких виразів є конструкція with, її можна вважати певним аналогом do-нотації в Haskell.

* щоб накинути, процитую Alan Kay:

OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things.

Відповідно, можемо вважати Elixir справжньою ОО мовою, на відміну від Java та інших ;) Персонально мені здається, що написання пачки довгоживучих GenServer’ів в одному проєкті мені дало більше розуміння ООП шаблонів, ніж робота з Ruby протягом кількох років.

Метапрограмування

Якщо віртуальну машину можна вважати хлібом, то метапрограмування в Elixir то є масло. Я люблю метапрограмування, правда не так писати, як використовувати гарно спроєктовані інструменти з його допомогою. Elixir має потужну та зручну систему для метапрограмування, на яку суттєво вплинула Clojure.

Одним з моїх улюблених прикладів є стандартний фреймворк для тестування ExUnit. Візьмемо приклад коду з документації

defmodule StringTest do
  use ExUnit.Case, async: true

  describe "String.capitalize/1" do
    test "first grapheme is in uppercase" do
      assert String.capitalize("hello") == "Hello"
    end

    test "converts remaining graphemes to lowercase" do
      assert String.capitalize("HELLO") == "Hello"
    end
  end
end

Те, що блоки describe i test є макросами — то ясно як божий день. Я б хотів акцентувати увагу на assert — цей вираз складніший, ніж здається на перший погляд. Складність ховається у звітуванні про помилки, коли умова не справджується і тест завершується невдало. assert аналізує не просто кінцеве булеве значення, а весь вираз. Змінимо перший тест на таке:

assert String.capitalize("hello") == "Hell Yeah"

запустивши який, ми отримаємо наступний звіт:

Саме так, підсвітка різниці між лівою та правою частинами! Звичайно, це буде працювати не тільки з рядками, а й з іншими структурами даних.

Ось ще приклад із зіставленням зі зразком (pattern matching):

data = %{user: %{first_name: "John", age: 18}}
assert {:ok, %{age: 13}} = Map.fetch(data, :user)

результат:

Ну це є у всіх нормальних фреймворках, ви скажете, і це так, але для подібного функціоналу там є достобіса різних хелперів з різними закінченнями та префіксами. І все для того, щоб нормально описати що ж відбувається! А тут просто додаєте assert до звичних виразів. Я відчуваю когнітивний розслабон при написанні тесту assert 1 < 3 замість expect(1).toBeLessThan(3); (JS), assert 3 in 1..5 замість expect(3).to be_between(1, 5) (ruby), таких прикладів можна придумати безліч, думаю, ідею ви зрозуміли.

Гріх тут і не згадати про бібліотеку Ecto, позаяк чималій кількості Elixir розробників доведеться з нею працювати. LINQ з .NET суттєво вплинув на мову запитів, але через те, що Elixir має підтримку макросів, то щоб підтримувати такий функціонал не потрібно додаткових інтеграцій в мову, на відміну від C#. Ось не зовсім вже примітивна вибірка з бази даних з використанням Ecto:

query =
  from employees in Employee,
    select: %{
      employee: employees.name,
      salary: employees.salary,
      avg_salary_in_department:
        employees.salary
        |> avg()
        |> over(partition_by: employees.depname)
    },
    inner_join: companies in assoc(employees, :company),
    where: companies.size == ^:big,
    where: not is_nil(employees.updated_at) or employees.inserted_at > ago(3, "month"),
    order_by: employees.name

Repo.all(query)

Коли ознайомлювався з Ecto, то тішився як слон можливості написати запит з or і з дужками, типу (A or B) and (C or D), не кажучи вже про window функції та CTE. Звичайно, є випадки, коли доведеться написати сирий SQL, але ця межа зсунута досить далеко.

Наслідування та ось це все, що сьогодні йменують як ООП є у вигляді сторонньої бібліотеки oop, яку зробив один з учасників core команди Elixir.

import OOP

class Animal do
  var :name
end

class Dog < Animal do
  var :breed
end

snuffles = Dog.new(name: "Snuffles", breed: "Shih Tzu")
snuffles.name # => "Snuffles"

Звичайно, то є просто забавка на макросах і ніхто цю бібліотеку не використовує.

Web фреймворк Phoenix дуже і дуже швидко віддає користувачеві результат через використання стандартної системи шаблонування EEx і власного рушія, який працює з iodata. Якщо коротко, то iodata — це якісь дані, які можна вивести в I/O, при цьому при виводі списку він робиться плоским:

> IO.puts(["a", ["b", ["c"]], [], "d"])
abcd
:ok
> IO.puts(["a", ["b", ["c"]], [], "d"] |> List.flatten() |> Enum.join())
abcd
:ok

Обидва виклики IO.puts виводять однаковий результат, але в другому варіанті є але — це оверхед у вигляді конкатенації рядків, через те що віртуальна машина буде у пам’яті мати окремо виділене місце під кожен елемент списку + конкатенований результат. Phoenix.HTML.Engine використовує цю особливість для оптимізацій виводу уникаючи зайвої конкатенації.

Нехай у нас є ось такий шаблон:

<div>
  <%= user_name <- @user_names do %>
	<div><%= user_name %></div>
  <% end %>
</div>

теги тут є статичними елементами, відповідно, з цього шаблона під капотом генерується функція з приблизно ось таким вмістом:

iex> template |> EEx.compile_string(engine: Phoenix.HTML.Engine) |> Macro.to_string() |> IO.puts()
(
  arg0 = case(user_name <- @user_names do
	arg0 = case(user_name) do
  	{:safe, data} ->
    	data
  	bin when is_binary(bin) ->
    	Plug.HTML.html_escape_to_iodata(bin)
  	other ->
    	Phoenix.HTML.Safe.to_iodata(other)
	end
	{:safe, ["\n	<div>", arg0, "</div>\n  "]}
  end) do
	{:safe, data} ->
  	data
	bin when is_binary(bin) ->
  	Plug.HTML.html_escape_to_iodata(bin)
	other ->
  	Phoenix.HTML.Safe.to_iodata(other)
  end
  {:safe, ["<div>\n  ", arg0, "\n</div>\n"]}
)

Рядки з тегами віртуальна машина видає постійно з тих самих комірок пам’яті, таким чином ми маємо автоматичне кешування статичних елементів.

Спільнота

Навіть не знаю як словами то краще описати. Спільнота надзвичайно дружня та доброзичлива. Культура спілкування на дуже високому рівні.

На StackOverflow є якась активність, але я, як і більшість, зависаю на форумі https://elixirforum.com, та і гугл якось частіше туди відправляє, як мені здається. Можливо, саме тому Elixir цього року вже не потрапив до Stack Overflow Developer Survey 2020 :D. Ще є простір в Slack, >29 тисяч учасників в #general каналі на момент написання статті. Взагалі активність в Slack має суттєвий негативний фактор — історія повідомлень не зберігається і по ній ніяка пошукова система не робить індексацію, відповідно знання втрачаються в тому потоці повідомлень.

Готові Elixir розробники на ринку, звичайно, є, але працедавцю треба бути готовим до розвитку Elixir розробників/розробниць самотужки або до співпраці з іноземними спеціалістами. На щастя це є легка мова (куди легша ніж ruby, javascript та купки інших) і більшість програмістів зможе писати на ній досить швидко, головне мотивація :).

Elixir та Erlang існують в симбіозі — Elixir приносить свіжу кров в екосистему, а Erlang дає хороший фундамент для розвитку.

Динамічна типізація

Значним мінусом для багатьох є відсутність статичної типізації. Хоч Elixir і динамічно типізована мова, але все одно має чимало статичних перевірок на етапі компіляції. Є ще dialyzer, але він дуже повільний і часто від нього толку як від козла молока. Є інші мови на BEAM, типу alpaca, gleam, elmchecmy, purerl (erlang backend для purescript), hamler (форкнутий purescript командою EMQ X), erlang backend для idris 2, але їх поширеність в продакшені прямує до нуля. Нещодавно було також анонсовано, що WhatsApp веде розробку типізованого Erlang, обіцяють результати якісь показати в листопаді цього року. Чи це буде мати якийсь вплив на Elixir — невідомо.

Продуктивність на CPU-інтенсивних задачах

Якщо коротко — то вона тут не велика. Коли таке реально знадобиться, то одним зі способів розв’язання таких проблем є NIF (Native Implemented Functions) і якась мова з ручним управлінням пам’яті. Це є надзвичайно поширений спосіб покласти віртуальну машину через свою криворукість. Щоб так не сталося, говорять багато про використання rust.

Час компіляції

Компілятор не може похвалитися надзвичайною швидкістю. Через наявність метапрограмування компіляція займає більше часу, ніж могло б бути без нього. Зовсім не складно відстрілити собі ногу зв’язавши код так, що при зміні одного дрібного файлу буде ще сотня залежних перекомпільовуватись. Але це не складно виправляється і є інструменти для інспекції залежностей такого роду.

У версії 1.11 (остання стабільна) суттєво покращили це питання. Також в master гілку Erlang/OTP додали JIT-компіляцію. Ці 2 фактори суттєво покращують ситуацію.

В загальному я б схарактеризував швидкодію як «нормальна».

Підтримка редакторами

Елементарна підтримка типу підсвітки коду, відступи, перехід на місце оголошення функції і т.д. звичайно є, а щось просунутіше може ще кульгати. Особисто в мене elixir-ls не стабільно у зв’язці з neovim працює (може то я просто криворукий, але хочеться просто включити і щоб працювало), тому в основному використовую alchemist.vim останній комміт куди був пів майже пів року тому. Плагіни для перевірки синтаксису та показу інших попереджень теж у мене вимкнені, тому що щоб перевірити чи функція існує, треба скомпілювати весь проєкт, а наявність системи макросів робить це потенційно небезпечним, бо ти, типу, хочеш перевірити синтаксис, а там код запускається, генеруються функції + на це ще може накладатися попередній пункт про час компіляції.

Стабільність мови

Зараз Elixir має мажорну версію 1, розробка нових великих фіч не ведеться і наразі немає на них планів. Також немає планів на мажорний Elixir v2 реліз. Чому так? По-перше, в дизайн було інвестовано багато часу і зусиль, щоб не треба було кілька разів переробляти. Elixir має достатньо механізмів, щоб будувати потужну екосистему без необхідності змін безпосередньо в самій мові. По-друге, багато роботи зроблено і досі робиться в Erlang/OTP. Тому Elixir 2 якщо і вийде, то лише з остаточним видаленням API який зараз вже позначений як застарілий. Принаймні зараз така риторика.

👍НравитсяПонравилось4
В избранноеВ избранном10
Подписаться на автора
LinkedIn

Похожие статьи

Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Хто хоче писати на Elixir, приходьте в державне підприємство ІНФОТЕХ!
Стек платформи: erp.uno

В рейтингу „TIOBE Index for February 2021” Elixir попав в TOP 50, тепер на 48 місці :)

Класна стаття
як ex-erlang dev, скажу, що ecto ОРМка, легко реалізуємий повноцінний CRUD з тестами і всим-всим за декілька годин, купа тулів і живе комьюніті робить еліксир дуже вдалим інструментом для підтримки і розробики, вдалішим, ніж ерланг, імхо

Симпатично написано, надихає.

Але мучить пара питань — цікавить власне думка людини, яка працює з технологією і може розказати з практичної т.з.

1 — дивний синтаксис
З прикладу ecto вище

where: companies.size == ^:big,
З моїх тестових кусочків, зліплених на коліні, які роблять одне і те ж, тільки різними способами
case :ets.lookup(cache, n) do
        [{^n, value}] -> value
        [] -> 
* * *
    value = Map.get(cache, n)
    if value == nil do
    value = :array.get(n, cache)
    if value == :undefined do
Чому і звідки ці символи ^ ?
Чому для доступу до стандартних ets та array ми звертаємось до них як до атомів?
а до Map — як до модуля? (це модуль же ж?)
Є десь описані ці особливості чи це «просто треба запам’ятати»?


2 — робота з більш-менш складними структурами даних
Чи важко працювати з практичними помірно складними структурами даних — наприклад, ордерами з Шопіфая чи якимись іншими ecommerce системами?
Приклад JSON структури з шопіфая — shopify.dev/...​orders/order#show-2020-10 — треба натиснути View Response, щоби показало структуру
Тобто, витягнути якісь дані по ключу, перебрати масив, звернутись до довіьних елементів JSON масиву, зконвертувати в інший формат і т.д.


3 — інтеграція з іншими мовами і системами
Про NIF було сказано, але це для бінарніків, які написані на сі, расті чи чомусь подібному.
Але чи варто звенути увагу на порти?
Чи можна мати «хост» програму на phoenix elixir, але якісь кавалки запускати на python чи php через порти?
Або взагалі погнати собі і зробити «стаб» на пітоні, який з одного боку буде приймати дані з ерлангового порта, а з іншого — слугувати WSGI хостом?


4 — швидкодія Phoenix, Elli та компанії на techempower
Вони там пасуть задніх.
Це особливість тестування на techempower? (чув скарги, можна пошукати)
Чи автори конкретних прикладів просто забили на них? (і таке часто буває)
Чи дійсно це така швидкодія?


Ці питання не для того, щоби накинути на вінтілятор, але дійсно питання, які так чи інакше виникають, коли роздивляєшся elixir та erlang.

1. ^ - це є pin оператор. В загальному він дозволяє використовувати вже оголошену константу там, де вона може бути перевизначена.
Ось випадок зі звичайним зіставленням зі зразком — тут просто буде помилка, якщо значення не збігаються

iex> a = 1
1
iex> %{value: a} = %{value: 2}
%{value: 2}
iex> a
2
iex> %{value: ^a} = %{value: 3}
** (MatchError) no match of right hand side value: %{value: 3}

iex> %{value: ^a} = %{value: 2}
%{value: 2}
Але використовують не тільки для того, щоб впало:
iex> name = "Ivan"
"Ivan"
iex> ["Petro", "Ivan", "Ivanna"] |> Enum.map(fn
...> ^name -> "IVAN"
...> name -> String.downcase(name)
...> end)
["petro", "IVAN", "ivanna"]
Це виглядає дивним, тому що в багатьох мов таких фіч немає.
Використання ^ у вашому прикладі з ETS особливого сенсу не має, оскільки там зіставлення по ключу і логічно там завжди n на виході буде рівне тому, по якому шукали. Можна просто обійтись {_n, value}.

У випадку з Ecto там в DSL суть та ж — вставити значення змінної, яка поза видимістю запиту. В цьому прикладі users використовується як рядок поза запитом, а всередині запиту вже посилається на таблицю. Імена в запиті не конфліктують з тими, що зовні, тому що в запиті своя область видимості.

iex> users = "name"
"name"
iex> from users in "users", where: users.name == ^users
#Ecto.Query<from u0 in "users", where: u0.name == ^"name">
iex> users = "name"
"name"
якщо ж в запиті буде users.name == users, то буде і відповідний SQL згенерований як WHERE (u0.«name» = u0), що не те, що нам треба. В останніх версіях Ecto для атомів вже не обов’язково використовувати ^.

Назви модулів в Erlang та Elixir це атоми. Map — це також атом, правда з синтаксичним цукром. Map є псевдонімом до :"Elixir.Map".

iex> :"Elixir.Map".new()
%{}
iex> is_atom(Map)
true
iex> alias :ets, as: ETS
:ets
iex> ETS.new(:buckets_registry, [:set, :protected])
#Reference<0.3063609617.867827713.241774>
Це десь таки має бути описано, зараз не скажу де :)

2. Та ні, не складно. Я б не сказав, що структури Shopify я якимись складними. JSON легко конвертується в терми Elixir, аналогічні типи присутні. Можна працювати просто з отриманими типами з допомогою get_in/put_in/for/Enum, можна додатково конвертувати їх у свої за допомогою того ж Ecto та embdedded_schema. Є ось ще приклад обгортки над GitHub API у вигляді адаптера для Ecto github.com/wojtekmach/github_ecto. Було б легше порівнювати якби ви написали код якоюсь мовою програмування і я зможу показати як це в Elixir можна зробити.

3. Про NIF було згадано в контексті швидкодії коду. Це найшвидше, що є. Звичайно варто звернути увагу на порти. Зокрема, порти використовують і для описаних вами сценаріїв. На рахунок WSGI нічого не скажу з пітоном слабо знайомий.

4. Колись, пам’ятаю, була проблема в самих бенчмарках на techempower, зараз не знаю на скільки це актуально, мабуть, код і поправили. Рекомендую поглянути на цей stressgrid.com/...​ing_go_vs_node_vs_elixir

^ - це є pin оператор. В загальному він дозволяє використовувати вже оголошену константу там, де вона може бути перевизначена... Це виглядає дивним, тому що в багатьох мов таких фіч немає.

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

1> X = 1.
1
2> X = 2.
** exception error: no match of right hand side value 2
а в эликсире — запросто
iex(1)> x = 1
1
iex(2)> x = 2
2
Поэтому и паттерн матчинг ведет себя немножко не так. В Эрланге
1> X = 1.
1
2> {X, Y} = {2, 3}.
** exception error: no match of right hand side value {2,3}
а в эликсире разумеется такое прокатывает
iex(1)> x = 1
1
iex(2)> {x, y} = {2, 3}
{2, 3}
iex(3)> x
2
что не всегда является желательным результатом. Вот там где в эликсире нужно как в эрланге — с иммутабельными байндингами — и нужен pin. Так фича? Или все же костыль?

Могли зробити як в Erlang, але не зробили навмисно, тому фіча. Але якщо називати милицею функціонал, який вам не подобається, тоді так, можна й милиця.

вы совершенно разные вещи сравнили с визуально похожим синтаксисом.
В эрланге нет приствоения как такового, есть СОПОСТАВЛЕНИЕ. Вот все что со знаком равно вы сопостявляете один раз. В еликсире, то что со знаком равно, это присвоение, так что сравнивать опертор равно в ерланге и оператор равно в еликсире все равно что головку сыра с полным месяцем. Похоже, но абсолютно разное

Давайте я вам кое что покажу:

iex(1)> ExToErl.elixir_source_to_erlang_source("x = x + 1")
"_x@2 = _x@1 + 1\n"
Вот так даже еще нагляднее:
iex(2)> ExToErl.elixir_source_to_erlang_abstract_code("x = 1")
{:match, 1, {:var, 1, :_x@2}, {:integer, 0, 1}}
Так что под капотом что там, что там иммутабельный байндинг и паттерн матчинг. Никакого присваивания там и в помине нет, просто в эликсир завезли немножко синтаксического сахарку.

Ну так и про C можно сказать, что в нём присвоения, а есть иммутабельный байндинг, потому что GCC и Clang сейчас одинаково делают выходной SSA :)

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

Тут кстати флейм в erlang-questions по поводу EEP с оператором ^ уже обсуждали? Там много годной еды :)

да вы что, капитан очевидность, а то я не знала, что еликсир на вмке ерланга.
«оце так новина»
Для конечного кодера (считайте пользователя) еликсира, который ни сном ни духом что там под капотом, как уже написал Valentin Nechayev, это синтаксис присвоения. Который вы сравнивали с чистым сопоставлением. Так можно и перегруженный оператор равно в сишке взять для кастомного класса и рассказывать что это не присвоение

это синтаксис присвоения.
iex(1)> x = 1
1
iex(2)> 2 = x + 1 # <--- это синтаксис присвоения?
2

x = 1 # <--- это синтаксис присвоения для конечного пользователя
x = 2  # <---и  это синтаксис присвоения для конечного пользователя
3 = x + 1 <--- а это уже явное сопоставление
Зачем задавать вопросы, на которые сам знаешь ответы?
x=3
y=2
^x =y+2   <--- а это уже явное сопоставление
x = y+2 <--- это синтаксис присвоения для конечного пользователя
в ералнге так не прокатит. в эрланге все сопоставление.
Вот и отличие пинов, костыля нет, пины работают как явное сопоставление, присвоение (то, что мы условно называем присвоением), неявное.

Язык, безусловно, классный, но работы на нем маловато ИМХО.

Он был бы очень даже ничего, если бы осилили его нормально типизировать.

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

с рантаймом, иначе будут проблемы.

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

elixirforum.com/...​-elixir/27192/13?u=fuelen
посилання на коментар від одного з авторів Erlang чому було прийняте таке рішення

Суперлегка модель для розуміння та використання, принаймні зараз так здається.

Это только от части так. Модель акторов достаточно простая для поверхностного ознакомления, но писать на ней что-то прикладное да и большое очень сложно. Скалисты вон уже убедились из юзеджа акки во всех местах, что акторы на самом деле — сложно(вы сразу, с порога превращаете свою систему либо в совокуность timed automata либо hybrid automata, которые даже симулировать проблематично, не то что проверять на работоспособность). Актор очень простая штука, она просто каким-то образом линеаризует сообщения, при том эти сообщения не типизированные. Это все что актор делает, но на этом не исчерпываются все задачи которые приходится решать в рамках работы с многопоточкой, да и вообще, потому что кроме акторов в эрланге/эликсире нет ничего.

Повністю погоджуюся, мені спочатку акка здавалася чудовим інструментом для багатопоточних програм, потім зрозумів, що інструментами скали можна зробити простіше і надійніше. Звісно є перевага у розподілених системах з akka remote. Typed Akka з інтеграцією typelevel стеку (що вже суперечить політиці lightbend) могла ще б трохи реанімувати становище

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

У вас интересное определение слова «норм». Т.е. вам норм, когда вы беглым взлядом на код(не держа всю систему в глове) не можете гарантированно определить куда уйдет какое сообщение и к какому изменению глобального состояния приведет? Голые акторные модели не дают никаких средств по этому поводу. Там необходимо воротить очень много инжиниринга поверх акторов.

не можете гарантированно определить куда уйдет какое сообщение и к какому изменению глобального состояния приведет?

Это и была изначальная концепция акторов, которую Кей и реализовал в виде ООП в Smalltalk

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

Само же ООП так и осталось даже в учебниках — способом избавления от сложности управления общим состоянием системы.
Сообщения Страуструп преватил в методы, и т.д.

и

все задачи которые приходится решать в рамках работы с многопоточкой

— это далеко не все вообще задачи, и точно не те задачи которые призвано было решать ООП

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

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

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

отсюда и холивары о красном и соленом. с претензией, естественно, на абсолютность.

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

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

Это и была изначальная концепция акторов, которую Кей и реализовал в виде ООП в Smalltalk

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

Само же ООП так и осталось даже в учебниках — способом избавления от сложности управления общим состоянием системы.

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

Сообщения Страуструп преватил в методы, и т.д.

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

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

 в _любой_ задаче где есть IO-bound задачи вам нужен какой-то достаточно эффективный механизм передачи управления другой задаче которой ждать не нужно, иначе ваша программа будет то и делать что находится в ожидании, гуи будет подвисать при походе на диск и в веб сервисы и так далее. Т.е. любое приложение которое веб сервер, да и вообще как-либо систематически работает с тормозным вводом — выводом имеет необходимость в таком механизме, и ваша «практика» тут ничего не доказывает. Да и на любой мало-мальски хорошо оплачиваемой дажва или скала вакансии вас точно будут спрашивать про JMM, happens-before, волатайлах, синхронайзедах, атомиках, ещё нескольких фреймах про многопоточке, как оно все это работает и так далее. На синьорных позициях это вообще маст-хэв.

вы экзамен что-ли здаете?

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

а мы тут — по металоведению принимаем. прикладному. для будущих сварщиков.

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

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

это о я своей бирочке «JMM, happens-before, волатайлах, синхронайзедах, атомиках».
не бог весть какая ессно. другой просто нет :)

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

и это — не бог весть какая наука, чтобы сварщику необходимо в нее лезть — в настоящий computer science
хочет? конечно пусть лезет.
дело личное.

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

и это — не бог весть какая наука, чтобы сварщику необходимо в нее лезть — в настоящий computer science
хочет? конечно пусть лезет.
дело личное.

Так и есть. Это все просто инструменты

..., с которых верующие, которых лишили Бога — лепят себе другого :)
ну и предают анафеме еретиков, молящихся другим богам :D
это — суть большинства холиваров среди сварщиков и науковерцев, для которых, всех их, наука стала магией, а не рациональным способом миропонимания.

спрашивали меня. рассказывал.
попросили написать что-то по транзакционному инкременту.

Ваше знание джавы не ставилось под сомнение.

а есть сварщики, которым достаточно владеть инструментами. и им — респект и уважуха

 В нашем случае владение инструментом без того что вы называете

настоящий computer science

не будет настолько высоким, и когда вот такие «сварщики»-программисты без задней мысли программируют в своей неповторимой манере как в песенке из НТР, ничего из этого хорошего не происходит. Если не обращать внимания на проблемы, они от этого не исчезают, они больно бьют по затылку.

в настоящий computer science
хочет? конечно пусть лезет.

Пусть лезет защищать PhD, там «настоящие актуальные CS». Содержимое классических учебников — это не «настоящие CS», это хорошо известные факты, которые УЖЕ открыты.

Проблемы бьют по затылку вас, а не сварщиков. И так бьют, что вам мерещится что их волнует cs так же как вас.
В науку бы вам. В сварке вы все равно нуб :) и вам и не будет интересно.

а не сварщиков

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

потому что это ваши галлюцинации, что их — это бьет :)

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

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

или, классика ж, 2004 год
Автор: Dr. Joseph M. Newcomer
Перевод: Андрей Лягусский
Источник: «A Brick Science Degree»
Специальность — каменщик

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

rsdn.org/...​e/career/BrickScience.xml
рекомендую тоже, внимательно, спокойно перечитать

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

Вступительный по физике в Одесский политехнический институт.

Принимают два преподавателя — препод постарше и молодой только из аспирантуры. Абитуриент билет рассказал, задачу решил, но коряво — борется между «4» и «5». Давно уже борется. Часика полтора. Вопросы задает тот, что постарше.
Молодой откровенно скучает.
Наконец-то ему окончательно все это надоело, да и старший утомился.

— Ладно, — говорит молодой препод. — Последний вопрос, отвечаете правильно, ставлю «5» — С какой скоростью должна двигаться кошка, чтоб не слышать звука консервной банки, привязанной к ее хвосту?
— Со скоростью звука, — не задумываясь выпаливает абитуриент, — 340 м/с.
— Идите, юноша, «4».
— ???
— ВУЗ у нас политехнический и мы тут инженеров готовим, а не теоретических ученых.
Тут думать практичнее нужно — кошка с такой скоростью двигаться не может.
То есть правильный ответ — скорость должна нулю быть равна.
Сидеть кошка просто должна!

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

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

В науку вам надо.
Практическое программирование не для вашего склада ума.

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

 Я то как раз знаю. А кроме этого знаю как избегать такой необходимости, можно ведь немножечко напрячь мозги и сэкономить себе немного времени. «сварщик» даже не выбирает между двумя вариантами, любая проблема в его понимани всегда должна решаться по месту, даже если это ведет к сильной трате ресурсов в будущем. В понимании «сварщика», выполнение задачи «как следует», это всегда криминал, и его никогда не оправдывает усилий. Обычно это выглядит так: -«если вы сделаете вот так а не вот так, у вас не будет вот таких проблем, это даст экономию n часов» — «да ну не, не вылезет, когда вылезет поправим, инжиниринг ради инжиниринга, и.т.д.» Далее проблема вылазит и требует решения. Если «сварщики» голову на плечах имеют, после какой-то итерации они приходят к мысли, что надо с этим что-то делать. Если нет — так и продолжают заливать воду в бочку без дна.

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

В науку вам надо.
Практическое программирование не для вашего склада ума.

Дядь, откуда вы знаете кто я такой?

Со скоростью звука,

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

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

Programming defeatism, ещё никогда и ни к чему хорошему это не приводило.

достаточно раз в сутки его перезагружать.

а если перезагружать нельзя? Или если оно колбасит данные с такой скоростью что оно ловит ООМ за 20 минут а надо ему работать минимум 8 часов?

откуда вы знаете кто я такой

понятия не имею кто вы

читаю посты, и обсуждал с вами пару тем.
поэтому просто понятен ваш ход мысли

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

а если перезагружать нельзя?

в подаляющем большинстве случаев — можно.

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

в науку вам надо =
человеку с таким складом ума, интересами, направлением мысли в решении задач
а не в «политех»

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

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

ну ок, такая трактовка вполне ожидаема.
удивился бы — если б ее не было :)

Какого инжиниринга? Да, с беглого взгяда, при нормальной реализации кода можна определить, куда уйдет меседж. Если конкретно наг*внокожено, и неясно, какой следующий обработчик, это уже вопрос к конкретной реализации

к какому изменению глобального состояния

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

Там необходимо воротить очень много инжиниринга поверх акторов.

— окей, поверх голых класов тоже надо много инжиниринга воротить, пока не все в облачных faas
И да, в отличии от оопшного подхода, нет состояния объекта, когда «во время пути собака смогла подрасти»

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

Вот я и говорю, чтобы построить нормальную реализацию поверх актеров нужен инжиринг. На голом Any => Unit без трассировки никак нельзя понять что этот Any => Unit делает, только если в навернете пачку абстракций, которые гарантируют что месседж уйдет куда попало, заруинить такую конструкцию не просто, а очень просто.

хендлер меседжа не приводит к глобальному изменению состояния апликейшена

 По большому счету таки приводит к изменению состояния рантайма, и многократному за время обрабобтки сообщения. И если actorRef ! mesaga вызывает в процессе более одного другого актора — то таки да это именно то о чем я писал — каскад изменений локальных состояний акторов(ЧТО И ЕСТЬ ИЗМЕНЕНИЕ ГЛОБАЛЬНОГО СОСТОЯНИЯ), который без трассировки нельзя отследить. Ну а если вам не особо нужен висящий стейт в приложении, а несколько обработчиков накинуть — актеры тут не осбо то и нужны, какая-нибуть фьючка или конкаррент эффект для этой задачи будет в несколько раз более простым решением.

Если конкретно наг*внокожено

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

нет состояния объекта, когда "во время пути собака смогла подрасти"

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

окей, поверх голых класов тоже надо много инжиниринга воротить, пока не все в облачных faas

 К счастью оно уже наворочено, zio называется, или например cats-effect, monix там какой-нибуть.

говнокодят все и всегда

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

Состояние актера и конфиграция актор системы, оно все есть

 — во оопехе тоже самое с обьектами

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

 у вас екстра странное предствавлении об том, как пишется основная масса кода. Обычный шаблон — один меседж один актор, если у вас не специфично навороченная архитектура.

ифчик -трайчик в код

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

построить нормальную реализацию [..] нужен инжиринг

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

Честно, пробовала скалу, для меня она стала в какой-то момент конкретной наркоманией с ее реализацией функциональщины. Там действительно все плохо, как вы описываете.

ее всякий уважающий себя джавист пробовал :)
я тоже пробовал, году не помню в каком, 12ом чтоли, или 10ом...

ну вот, попробовали джависты, смотрим на тренды скалы...

Хочу добавить несколько пунктов:
— Сейчас сильно форсят/продвигают LiveView. Эта фича дает возможность писать реактивный фронтэнд без написания JS, благодаря тому, что Elixir/Phoenix вывозит много WS соединений и благодаря той же акторной модели. Есть ряд минусов. hexdocs.pm/...​iew/Phoenix.LiveView.html
— Elixir очень хорош в IoT сфере. Так же, есть крутое решение для создания UI в IoT. github.com/boydm/scenic
— Много удаленной работы. Достаточно легко получить контракты напрямую работая с компаниями с других стран
— Один из самых оплачиваемых ЯП
Это все на вскидку.

Еще бы добавил, что многие топовые компании используют Elixir: Pepsi, Toyota, Cars.com, Discord, Apple (видел вакансию туда), WhatsApp (Facebook / Erlang), HelloSign, Klarna и многие другие

Ну и русскоговорящий чатик в телеге t.me/proelixir
И митап, который я вместе с другими организаторами, делаем в онлайне: t.me/ru_elixir_meetup_online

Много удаленной работы.

Где лучше искать если что? (на том же SO/jobs ее можно сказать что нет)

— elixirjobs.net
— Elixir Forum
— Elixir Slack — #jobs #looking_for_contract. Этот канал самый эфективный на мой взгляд

Ще публікуються вакансії в Elixir Radar elixir-radar.com/jobs

Так, хороше доповнення.

Про використання ще можна прочитати на офіційному вебсайті, недавно почали додавати статті в блог, виділили окрему сторінку для цього elixir-lang.org/cases.html

Зачіпаючи тему спільноти, є www.elixirkyiv.club і чати в телеграмі t.me/elixirclubchat та t.me/ElixirClubUA

Чудова оглядова стаття! Варто б її перенести в розділ «Стрічка» — там мало такого матеріалу.

О, спасибо ДОУ за расстановку тегов под подобным. Сильно помогает.

Хотя я далёк от всего этого, от прочтения получил удовольствие). Живо написано.

Подписаться на комментарии