Как стать автором
Обновить

Комментарии 27

имхо навязывание новых, ненужных стандартов по силам только монополистам вроде microsoft и apple, а просто так это пустая затея

Промахнулся с веткой.
А может это любовь?

Определите значение слова «пустая».

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

WhatsApp использует все больше кода на эликсире (будучи написан на эрланге, он использует его нативно), IoT в современном мире не на эликсире пишут только упоротые дебилы, любой сервис на 100к пользователей с состоянием — вне мира BEAM долбится в базу на каждый чих и сто́ит примерно в 3-20 раз больше в эксплуатации, что как бы влияет на выбор. А у эрланга синтаксис не всем нравится, да и метапрограммирование там другое — по мнению многих, гораздо менее удобное.

Кстати, не существует ни одного языка от Apple младше ObjC, который бы по широте применения мог хотя бы близко подойти к эликсиру в последние 4, что ли, года.

Не вопрос. Меня тоже все устраивает. Даже более того - я вижу пути развития положительных качеств Elixir.

Выполнил реинжиниринг макроса while на базе конечной рекурсии.

Вы бы код показали, я мог бы предметнее сказать, в чем именно вы ошиблись; скорее всего, забыли либо про хвостовую оптимизацию (которая невозможна для функций, бросающих throw), либо про остановку снаружи. Запустите вашу имплементацию и подождите минуту-пять: если вылетит по stack overflow — первое.

Я не поленился и написал об ошибке […]

О какой именно ошибке? Я не вижу никакой ошибки, например.

встроить pid в тело макроса не удастся, так как он является результатом порождения процесса и является внешней ипостасью […] после «убития» процесса через send pid, :stop, у нас исчезает само тело процесса, которое может обработать условие прототипа while `Process.alive?(pid)`.

Да сможет оно всё, заграбастать свой pid через вызов self/0 изнутри бесконечного цикла и сохранить его в ProcessDictionary — большого ума не надо. Крис выносит его на публику, чтобы можно было ему сообщения засылать.

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

Это тоже, конечно, но не главное. Макросы нужны в осовном для модификации AST. Посмотрите на реализацию GenServer вот хотя бы: там нет вычислений, но макросы заменяют кучу бойлерплейта (а не было бы готового эрланговского gen_server — заменяли бы три кучи.

#реализация макроса while в технике концевой рекурсии

defmodule Loop do

defmacro while(expression, do: block) do
quote do
loop(unquote(expression), unquote(block))
end
end

defmacro fun_block(block) do
quote do: unquote(block)
end

def loop(expression, block) do
try do
if expression do
fun_block(block)
else
Loop.break
end
catch
:break -> :ok
end
loop(expression, block)
end

def break, do: throw :break
end

На мой инженерный взгляд формальная ошибка в фразе:

• The first element is an atom denoting the function call, or another tuple, representing a nested node in the AST.

Ну не может в первом элементе содержаться "another tuple, representing a nested node in the AST."

iex|🌢|1 ▶ quote do: IO.puts(:ok)
{{:., [], [{:__aliases__, [alias: false], [:IO]}, :puts]}, [], [:ok]}

Что касается макроса выше, ну вы бы хотя бы попробовали запустить этот код. И даже если его поправить (вызов из макроса должен быть remote, потому что в контексте того модуля, куда мы встраиваем этот AST, никакой функции loop/2 нет), с false в качестве первого аргумента он ведет себя странноватенько.

По вашей просьбе запустил ещё раз. Подождал пять минут. Процесс действительно упал:-(
Буду думать и анализировать.

К сожалению, ваш пример не понял. Зато нашёл на stack overflow разбор родственного примера. https://translated.turbopages.org/proxy_u/en-ru.ru.89c95398-6766b1dc-f0097d79-74722d776562/https/stackoverflow.com/questions/60151752/recursive-macro-elixir

Там рекомендация очень выразительная:

суть в том, чтобы подумать, что вы делаете, чтобы избежать вызова макроса внутри quote do [....] end.

У меня в макросе как раз опосредованный вызов макроса в блоке quote do [....] end.
Из объяснения я понял, что это ведет к бесконечному циклу на этапе компиляции. Это так?

Еще интересно, что в Scheme такой вызов макроса в макросе допускается?!

Если вызов макроса внутри quote do [....] endне возможен, то и рекурсия в макросе невозможна.

Я правильно понял?

Да сможет оно всё, заграбастать свой pid через вызов self/0

Это понятно, но в конце концов нет тела процесса - нет и дела, т.е. некому обрабатывать проверку... Process.alive?(pid)

Не спорю. Я вообще не спорю, а выражаю частное мнение, дополняющее мнение Dave Thomas
Получается, что я переадресовываю к Thomas.

некому обрабатывать проверку

Эта проверка выполняется не процессом, а виртуальной машиной.

дополняющее мнение Dave Thomas

Я искренне уважаю и Дейва, и Брюса, но они все-таки уже давно в бо́льшей степени популисты, а Крис — настоящий разработчик, такой, который код пишет каждый день, а потому знает, где макросы нужны, а где — не очень.

Очень хорошо. Но тезис:

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

остается правильным.

проверка выполняется не процессом, а виртуальной машиной

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

Я не увидел вверху никакого кода, в котором бы вызывалось Process.alive?/1 изнутри запущенного процесса. Покажите — расскажу, как его переписать без потери общности, не выплевывая наружу pid.

Кроме того, VM — не просто молотилка, она хранит у себя состояния всех процессов, например (иначе hot reload бы не работал, я уж молчу про деревья супервизоров). VM вставляет точки передачи выполнения для вытесняющей многозадачности. Она много чего делает, помимо выполнения байт-кода, это не джава же ж.

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

Я вернулся:-)
Мне кажется дело в спецификациях на макрос, которые есть у Криса.

Одна проектная:

while Process.alive?(pid) do...

Другая, полученная в результате:

defmacro while(expression, do: block) do...

Т.е. в конечном итоге pid процесса остался снаружи. И это корректная модель. Если процесс уже отработал по каким-то причинам, то посылка сообщения в "некуда" возвратит «холостой» вызов оператора send. И пользователь может понять, что процесса уже нет.

Так я понял Криса.

Это одна и та же спецификация.

Мы проектируем макрос while/2. Он принимает expression (в данном случае — Process.alive?(pid) — и do-блок.

—————

В моём примере выше — я показал вполне годное дерево, сформированное штатными средствами эликсира (quote/1), у которого в первом элементе первой туплы — тупла.

Что касается рекурсии, разумеется она возможна. Просто надо понимать, что макрос будет развернут во время компиляции в AST, а не выполнен. Смотрите:

iex|🌢|1 ▶ defmodule M do
...|🌢|1 ▶   defmacro recursive(n) do
...|🌢|1 ▶     if n - 1 > 0, do: quote(do: unquote(n) * M.recursive(unquote(n - 1))), else: 1
...|🌢|1 ▶   end
...|🌢|1 ▶ end

iex|🌢|2 ▶ defmodule Test do
...|🌢|2 ▶   require M
...|🌢|2 ▶ 
...|🌢|2 ▶   def five_factorial_compile_time, do: M.recursive(5)
...|🌢|2 ▶ end

iex|🌢|3 ▶ Test.five_factorial_compile_time()
120

Факториал здесь вычисляется во время компиляции, если дизассемблировать BEAM обратно в эликсир, вы увидите, что код модуля Test выглядит так:

def five_factorial_compile_time, do: 120

AST разворачивалось, пока было, чему разворачиваться. Рекурсия, как она есть :)

Просто изнутри quote/1 вызов макроса выполнится с передачей контекста в качестве аргумента, а не значения — взятие значения будет отложено на рантайм, поэтому там рекурсия и не заканчивается.

—————

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

Мы проектируем макрос while/2. Он принимает expression (в данном случае — Process.alive?(pid) — и do-блок

Обязательно попробую, когда перезагружусь в рабочую OS. Формально должно сработать. Крис в качестве expression прямолинейно передавал константу true. Да, это должно работать!

Но смысл? Интроспекция какая-то!

И тем не менее, если процесс по каким-то причинам упал, то и развернутогоexpression в коде уже нет, и самого кода нет.

 в первом элементе первой туплы — тупла

Буду изучать.

Сижу, разбираю ваш код. Было бы полезно посмотреть код функции five_factorial_compile_time...

Попытался дизассемблировать модуль Test при помощи :compile.disassemble("Test.beam")

В результате получил сообщение, что отсутствует функция disassemble. Копать дальше не стал, т.к. реальный интерес направлен на содержание функции five_factorial_compile_time

Интерес к дизассемблированию не праздный: у меня не падает процесс:

iex(19)> :calendar.local_time();
{{2024, 12, 24}, {12, 21, 10}}
iex(20)> send pid, :hello
:hello
iex(21)> :calendar.local_time();
{{2024, 12, 24}, {12, 39, 15}}
iex(22)> Process.alive? pid
true

Все по-честному. Что было перед этим, не знаю.

Код выше приведен же целиком. Еще есть Macro.to_string/1 https://hexdocs.pm/elixir/Macro.html#to_string/1

Для работы с BEAM-файлами есть модуль :beam_lib https://www.erlang.org/doc/apps/stdlib/beam_lib.html#chunks/2; вот еще: https://rocket-science.ru/hacking/2017/09/01/unveil_erlang_code_of_your_elixir_project

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

И почитайте, наконец, про синтакс маркдауна: оформление кода трем бэктиками.

И почитайте, наконец, про синтакс маркдауна

Чувствую, что давно пора, да руки не доходят. Извините.

пока не увижу, откуда взялась локальная переменная pid

Все по Крису:

iex>
pid = spawn fn ->
while true do
receive do
:stop ->
IO.puts "Stopping..."
break
message ->
IO.puts "Got #{inspect message}"
end end end

С новым годом, учитель, и пожелание всех благ!

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

Process.alive? pid
показывал true, а
посылка сообщений уходили в некуда.

Почему так, я не разобрался.

Наверное, вы имели в виду, не популизм, а популизаторство. Это архиважно!

У меня сложилось впечатление, что вы лично знакомы с Дейвом, Брюсом (Tate?) и Крисом.

Если это так, то передайте привет из страны Севера.
Еще здесь очень ценят вклад Криса в real time в Elixir, насколько от сюда можно судить.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации