cobra

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 :blush:

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

zachdaniel

Creator of Ash

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 :laughing: Macros writing macros, there are cases where it makes sense :person_shrugging:

zachdaniel

zachdaniel

Creator of Ash

Spark — spark v2.2.35 Finally got around to providing an actual getting started for spark :slight_smile: it’s not amazing, but it’s something and I’m limited on time :sweat_smile:

mudasobwa

mudasobwa

Creator of Cure
  • GenServer is 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/1 comprehension is completely written in Elixir AST
  • Telemetría uses metaprogramming to modify existing code injecting :telemetry calls based on user-defined module attributes
  • Finitomata is the same technique of exporting callbacks as GenServer, for distributed FSM

Where Next?

Popular in Discussions Top

thojanssens1
It would be nice to be able to define a redirect from one route to another from the router.ex file. E.g.: redirect "/", UserController, ...
New
crispinb
On reading dhh’s latest The One Person Framework it strikes me that Phoenix with LiveView is already pretty much this. However, never hav...
New
Fl4m3Ph03n1x
Background This question comes mainly from my ignorance. Today is Black Friday, one of my favorite days of the year to buy books. One boo...
New
arpan
Hello everyone :wave: Today I am very excited to announce a project that I have been working on for almost 3 months now. The project is...
New
AstonJ
If a newbie asked you about Phoenix Contexts, how would you explain the basics to them? Feel free to be as concise or in-depth as you li...
New
mbenatti
Following https://github.com/tbrand/which_is_the_fastest |&gt; https://raw.githubusercontent.com/tbrand/which_is_the_fastest/master/imgs...
New
Qqwy
I would like to spark a discussion about the static access operator: .. For whom does not know: it is used in Elixir to access fields of...
New
klo
Got a question about when to concat vs. prepending items to list then reversing to achieve appending. So i know lists boil down to [1 | ...
New
opsb
We’re considering our architecture from a viewpoint of scaling our traffic heavily over the next 6 months. Our current deployment is runn...
New
Markusxmr
Since Drab has been developed for a while in the open, introducing the Liveview functionality in a way it happend appears to undermine th...
New

Other popular topics Top

JeremM34
Hello, how can I check the Phoenix version ? Thanks !
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list. ...
New
Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
JakeBecker
TL;DR: I’ve just released an implementation of Microsoft’s IDE-independent Language Server Protocol for Elixir. It adds language support ...
1144 54250 245
New
AstonJ
Seen any cool LiveView demos, sample apps or examples? Please post them here! :003:
New
dokuzbir
I want to highlight html closing tags when i click a html tag. That works in .html files but doesnt work for html.eex templates. How can...
New

We're in Beta

About us Mission Statement