Комментарии 27
А может это любовь?
имхо навязывание новых, ненужных стандартов по силам только монополистам вроде microsoft и apple, а просто так это пустая затея
Промахнулся с веткой.
А может это любовь?
Определите значение слова «пустая».
Я уже десять лет за эликсир получаю суммы, которые меня устраивают (спойлер: Java/.NET и, тем более, питон/го — меня не устраивают), и — что неизмеримо важнее — пишу на языке, который меня радует.
WhatsApp использует все больше кода на эликсире (будучи написан на эрланге, он использует его нативно), IoT в современном мире не на эликсире пишут только упоротые дебилы, любой сервис на 100к пользователей с состоянием — вне мира BEAM долбится в базу на каждый чих и сто́ит примерно в 3-20 раз больше в эксплуатации, что как бы влияет на выбор. А у эрланга синтаксис не всем нравится, да и метапрограммирование там другое — по мнению многих, гораздо менее удобное.
Кстати, не существует ни одного языка от Apple младше ObjC, который бы по широте применения мог хотя бы близко подойти к эликсиру в последние 4, что ли, года.
Выполнил реинжиниринг макроса 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, насколько от сюда можно судить.
Критика чистого макроса