Hier ändern kann ist das, was ich versuche zu tun:einen DSL mit verschachtelten Elixir Makros erstellen, den Block Kontext
%Cake{
name: "Chocolate",
topping: %Topping{
name: "Butter cream",
sweetener: "Agave"
}
}
:
defmodule ArbitraryContext do
use Cake
def make_cake do
cake do
name "Chocolate"
topping do
name "Butter cream"
sweetener "Agave"
end
end
end
end
Ich mag würde ArbitraryContext.make_cake/0
zu verschachtelten Strukturen entlang der Linien zu erzeugen
Ich habe Metaprogramming Elixir und einige andere Ressourcen gelesen, aber ich kann nicht in der Lage sein, einige der DSL-Flexibilität zu reproduzieren, die ich in Ruby in Elixir gewohnt bin. Das scheint falsch, denn Elixir scheint grundsätzlich flexibler zu sein.
Ich habe mit dem "HTML DSL" Beispiel in Metaprogramming Elixir gefolgt. Das HTML-Beispiel ist wesentlich einfacher, da nur ein Modul im Spiel ist - ein Tag -, so dass sein Kontext während der gesamten Verschachtelung gleich bleiben kann. In meinem Fall könnte es Dutzende von Kontexten geben. Ich injiziere Cake-Makros in den Kontext, der die %Cake{...}
mit einem Namen erfolgreich produziert, aber wenn der Block für Topping nicht quoted ist, um %Topping{...}
zu produzieren, ist der Kontext immer noch Cake
. Egal, was ich mache, ich finde keinen sauberen Weg, diesen Block in einem neuen Kontext zu betreiben.
defmodule Cake do
defstruct name: nil
defmacro __using__(_) do
quote do
import Cake
end
end
defmacro cake(do: block) do
quote do
# agent-y stuff to maintain state while building the cake. not
# super important at this time
{:ok, var!(pid, Cake)} = %Cake{} |> start_state
# here's where the cake is no longer a lie and the name is set
unquote(block)
out = get_state(var!(pid, Cake))
:ok = stop_state(var!(pid, Cake))
out
end
end
defmacro topping(block) do
quote do
# oh no! block gets evaluated here. even if I double quote
# block, it still ultimately gets the Cake scope even though I'm
# passing it into Topping, which is very similar to Cake... meant
# to build up a Topping struct.
#
# I want to:
# 1) get block into Topping.topping without unquoting it
# 2) have the block unquoted in Topping's context, once in there
Topping.topping(unquote(block))
end
end
end
In Ruby ich dies wie Topping.class_eval
mit etwas behandeln würde ... Sie mit name
und sweetener
von Topping
und auf der anderen Seite würden am Ende würden Sie mit einem neuen Topping Klasseninstanz enden.
kann ich lösen, wohl viel sauberer, nur durch Gebäude die Strukturen vor-geschachtelt ohne DSL und alle Makros, aber ich möchte verstehen, wie das gewünschte Ergebnis mit Elixir-Makros zu bekommen.
Ich hoffe, ich habe diese Frage gut genug kommuniziert!
Können Sie die Quelle von "Topping" veröffentlichen? Ich denke du willst 'Topping.topping (do: unquote (block))' aber da du die komplette Quelle nicht gepostet hast, kann ich es nicht versuchen. – Dogbert
@Dogbert Der Topping-Code ist nicht wirklich so wichtig oder anders. Das Beispiel, das Sie angegeben haben, hebt den Block in Cakes Topping-Makro auf, bevor das Topping-Makro erreicht wird. Aber ja, auf hoher Ebene, wenn die beiden APIs die gleichen wären, würden Sie das Argument als Keyword-Liste mit "do" formatieren. – onyxrev
Oh. Ich denke du willst das Makro außerhalb des Zitats dann aufrufen, wie: 'defmacro topping (block) topping.topping (block) end'. Dies nennt "Topping.topping/1" mit dem zitierten AST und injiziert den zitierten AST zurück an die Stelle, die 'Cake.topping/1' genannt wird. – Dogbert