Как динамически создавать и загружать модули во время выполнения в Elixir или Erlang?

Основной сценарий таков: мне нужно загрузить текст из базы данных, а затем превратить этот текст в модуль Elixir (или модуль Erlang), а затем сделать вызовы в него. Текст фактически такой же, как файл модуля. Так что это форма горячей загрузки кода. Хочу скомпилироватьфайл" и затем загрузите полученный модуль, затем сделайте вызовы в него. Позже яразгрузлю его. Разница лишь в том, что код существует в базе данных, а не в файле на диске. (и это нене существует в то время, когда яЯ пишу код, который будет загружать его.)

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

В Elixir есть несколько средств для оценки кода во время выполнения. Я'Я пытаюсь выяснить, как сделать это с ними, и документация немного скудна. "

Code.compile_string(string, "nofile")

возвращает список кортежей, где первый элемент - это имя модуля, а второй - его двоичный файл., Итак, теперь у меня есть имена модулей и их двоичные файлы, но я не знаю способа загрузить двоичные файлы в среду выполнения и вызвать их. Как бы я это сделал? (Там'в библиотеке кода нет функции, которую я вижу.)

Возможно, я мог бы затем использовать функцию Erlang:

:code.load_binary(Module, Filename, Binary)  ->
           {module, Module} | {error, What}

Итак, это возвращает кортеж с атомом "модуль» а затем модуль. Если строка, загруженная из базы данных, определила модуль с именем "Париж"как в моем коде я бы тогда выполнить

paris.handler([parameters])

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

Существует также:

eval(string, binding // [], opts // [])

Который оценивает содержимое строки. Может ли эта строка быть полным определением модуля? Похоже, нет. Я'Я хотел бы иметь возможность написать этот код, которыйоценивается таким образом, что он имеет несколько функций, которые вызывают друг друга - например, полная маленькая программа с заранее определенной точкой входа (которая может быть основной, такой какDynamicModule.handle ([параметр, список]) "

То есть'Модуль EEx, который имеет:

compile_string(source, options // [])

Что отлично подходит для создания шаблонов. Но, в конечном счете, кажется, что это работает только в том случае, когдаСтрока и тыВ него встроен код Elixir. Он оценивает строку в контексте параметров и создает строку. Я'Я пытаюсь скомпилировать строку в одну или несколько функций, которые затем я могу вызвать. (Если я могу сделать только одну функцию, котораяхорошо, эта функция может соответствовать шаблону или переключаться на выполнение других необходимых действий ...)

Я знаю, что это нетрадиционно, но у меня есть свои причины для этого, и они хорошие. Я'ищу совет о том, как это сделать, но нене нужно говорить "дон»не делай этого ", Кажется, что это возможно, Erlang поддерживает горячую загрузку кода, а Elixir довольно динамичен, но я просто неНе знаю синтаксис или правильные функции. Я'Я буду внимательно следить за этим вопросом. Заранее спасибо!

РЕДАКТИРОВАТЬ на основе первых ответов:

Спасибо за ответы, это хороший прогресс. Как показал Юрий, eval может определять модуль, и как Хосе указывает, что я могу просто использовать код eval для небольших частей кода с привязками.

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

Чтобы помочь, позвольте мне предоставить некоторый контекст. Предположим, яЯ строю веб-фреймворк. Код, загруженный из базы данных, является обработчиком для определенных URI. Поэтому, когда поступает HTTP-вызов, я могу загрузить код для example.com/blog/. На этой странице может быть несколько разных вещей, таких как комментарии, список последних сообщений и т. Д.

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

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

Что будет, если таммодуль уже определен? EG: Когда модуль изменяется, и уже есть процессы, вызывающие этот модуль.

В iex я могу переопределить модуль, которыйs уже определены:

iex(20)> Code.eval "defmodule A do\ndef a do\n5\nend\nend"
nofile:1: redefining module A

Что произойдет, если я переопределю модуль во время выполнения для всех процессов, в данный момент вызывающих этот модуль? Кроме того, будет ли это переопределение работать вне iex, при нормальной работе?

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

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

Тем не менее, это неКажется, работает:

iex(31)> q = "f = function do
...(31)> x, y when x > 0 -> x+y
...(31)> x, y -> x* y
...(31)> end"
"f = function do\nx, y when x > 0 -> x+y\nx, y -> x* y\nend"
iex(32)> Code.eval q
{#Fun,[f: #Fun]}
iex(33)> f
** (UndefinedFunctionError) undefined function: IEx.Helpers.f/0
    IEx.Helpers.f()
    erl_eval.erl:572: :erl_eval.do_apply/6
    src/elixir.erl:110: :elixir.eval_forms/3
    /Users/jose/Work/github/elixir/lib/iex/lib/iex/loop.ex:18: IEx.Loop.do_loop/1

iex(33)> f.(1,3)
** (UndefinedFunctionError) undefined function: IEx.Helpers.f/0
    IEx.Helpers.f()
    erl_eval.erl:572: :erl_eval.do_apply/6
    erl_eval.erl:355: :erl_eval.expr/5
    src/elixir.erl:110: :elixir.eval_forms/3
    /Users/jose/Work/github/elixir/lib/iex/lib/iex/loop.ex:18: IEx.Loop.do_loop/1

Я также попробовал:

    iex(17)> y = Code.eval "fn(a,b) -> a + b end"
{#Fun,[]}
iex(18)> y.(1,2)
** (BadFunctionError) bad function: {#Fun,[]}
    erl_eval.erl:559: :erl_eval.do_apply/5
    src/elixir.erl:110: :elixir.eval_forms/3
    /Users/jose/Work/github/elixir/lib/iex/lib/iex/loop.ex:18: IEx.Loop.do_loop/1

Итак, в итоге:

Можно ли переопределить модули, используя Code.eval, когда есть процессы, вызывающие их?

Можно ли использовать Code.eval для создания функций, область действия которых связана с процессом, в котором вызывался Code.eval?

Если вы понимаете, что это яя пытаюсь сделать, вы можете предложить лучший способ сделать это?

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

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

Большое спасибо!

Ответы на вопрос(3)

Ваш ответ на вопрос