This example shows how @:protocol and @:impl can define a shared typed contract and multiple implementations.
A skeptical Elixir question is: "Why not just write the polymorphic modules directly in Elixir?" This example demonstrates when the Haxe layer helps and what tradeoff comes with it.
cd examples/07-protocols
haxe build.hxmlexamples/07-protocols/src_haxe/protocols/Drawable.hxexamples/07-protocols/src_haxe/implementations/StringDrawable.hxexamples/07-protocols/src_haxe/implementations/NumberDrawable.hxexamples/07-protocols/lib/protocols/drawable.ex
Without this abstraction layer, you would maintain one contract module and each implementation module manually, keeping arities and value-shapes in sync yourself.
@:protocol
class Drawable {
public function draw(value:Term):String {
throw "Protocol method should be implemented";
}
}
@:impl
class StringDrawable {
public function draw(value:String):String {
return "Drawing string: " + value;
}
}defmodule Drawable do
def draw(_, _) do
raise Reflaxe.Elixir.HaxeThrow, [value: "Protocol method should be implemented"]
end
end
defmodule StringDrawable do
def draw(_, value), do: "Drawing string: #{value}"
end- The contract and implementations share typed signatures in one authoring layer.
- Refactors that change argument/return shapes fail earlier in compile-time checks instead of drifting across modules.
- Adding another implementation reuses the same typed contract surface.
This compiler currently emits contract/implementation modules, not native defprotocol/defimpl macros. If protocol-macro semantics are required in a specific boundary, keep that boundary explicit and minimal while the rest remains Haxe-authored.