cobra
How have you used Metaprogramming in Elixir?
Has anyone tried exploring Metaprogramming before. I think it’s a really powerful technique. I was looking for someone who has implemented it on any feature he or she was building. This would help me further know where I can apply it. Thanks @chrismccord …this code is from your book , Metaprogramming Elixir ![]()
defmodule Translator do
defmacro __using__(_options) do
quote do
Module.register_attribute(__MODULE__, :locales,
accumulate: true,
persist: false
)
import unquote(__MODULE__), only: [locale: 2]
@before_compile unquote(__MODULE__)
end
end
defmacro __before_compile__(env) do
compile(Module.get_attribute(env.module, :locales))
end
defmacro locale(name, mappings) do
quote bind_quoted: [name: name, mappings: mappings] do
@locales {name, mappings}
end
end
def compile(translations) do
translations_ast =
for {locale, mappings} <- translations do
deftranslations(locale, "", mappings)
end
quote do
def t(locale, path, bindings \\ [])
unquote(translations_ast)
def t(_locale, _path, _bindings), do: {:error, :no_translation}
end
end
defp deftranslations(locale, current_path, mappings) do
# deftranslations("en", "", mappings)
# TBD: Return an AST of the t/3 function defs for the given locale
# * example of a mapping
# * flash: [hello: "Hello %{first} %{last}!", bye: "Bye, %{name}!"],
# * users: [title: "Users"]
for {key, val} <- mappings do
# e.g path = append_path("", flash) -> "flash"
path = append_path(current_path, key)
# append_path("flash", hello) -> "flash.hello"
if Keyword.keyword?(val) do
# deftranslations("en", "flash", [hello: "hello", bye: "bye"])
deftranslations(locale, path, val)
else
quote do
# t("en", "flash.hello", bindings)
def t(unquote(locale), unquote(path), bindings) do
unquote(interpolate(val))
end
end
end
end
end
defp interpolate(string) do
# TBD interpolate bindings within string
string
end
defp append_path("", next), do: to_string(next)
defp append_path(current, next), do: "#{current}.#{next}"
end
Most Liked
zachdaniel
We really need to document this better, but all of our DSLs (which support all kinds of useful things, are type validated, have an ElixirLS extension for customized autocomplete, can accept complex values like anonymous functions, etc.) are all built using spark: Spark — spark v2.7.2
Spark takes a declarative structure, and generates a DSL from that structure.
For a very simple example:
defmodule MyApp.Dsl.Extension do
@dsl %Spark.Dsl.Section{
name: :dsl,
schema: [
foo: [
type: :integer,
doc: "The amount of coolness to add",
default: 100
]
]
}
use Spark.Dsl.Extension, sections: [@dsl]
end
defmodule MyApp.Dsl do
use Spark.Dsl, default_extensions: [extensions: [MyApp.Dsl.Extension]]
end
defmodule MyApp.Dsl.Info do
use Spark.InfoGenerator, extension: MyApp.Dsl.Extension, sections: [:dsl]
end
Then you can use it like so:
defmodule Something do
use MyApp.Dsl
dsl do
foo 10
end
end
# and introspect it
MyApp.Dsl.Info.dsl_foo(Something)
# => {:ok, 10}
That example is just the tip of the iceberg. It looks like “macro magic” but in reality it’s a thin veneer over a data structure that gives you elixir-specific niceties and handles the complexity that comes with building these kinds of things.
So you can write a DSL without having to write a single macro, we write the macros for you
Macros writing macros, there are cases where it makes sense ![]()
zachdaniel
Spark — spark v2.2.35 Finally got around to providing an actual getting started for spark
it’s not amazing, but it’s something and I’m limited on time ![]()
mudasobwa
GenServeris a nice example of how to build a process code around a behaviour, hiding all the barebone actor model behind a client’s callbacks- my implementation of
stream/1comprehension is completely written in Elixir AST Telemetríauses metaprogramming to modify existing code injecting:telemetrycalls based on user-defined module attributesFinitomatais the same technique of exporting callbacks asGenServer, for distributed FSM
Popular in Discussions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance









