For example, how does Kernel
‘s to_string
“know” that it belongs to String.Chars
?
If I do a defimpl String.Chars, for: MyMod
– how does to_string
know
to call my implemented function?
Just curious.
For example, how does Kernel
‘s to_string
“know” that it belongs to String.Chars
?
If I do a defimpl String.Chars, for: MyMod
– how does to_string
know
to call my implemented function?
Just curious.
The module String.Chars
has a function of to_string
, and the kernel to_string
just calls String.Chars.to_string
.
What a protocol does it auto-generate the protocol functions from a list of definitions. So when you defimpl String.Chars, for: MyMod
or whatever the syntax was then it defines a module, and at protocol consolidation time it scans all the compiled modules to see what is impl’d (just an attribute marker is all it is, of protocol_impl
, so for your example it would have an attribute on the module of protocol_impl: [protocol: String.Char, for: MyMod]
), and it uses those to build functions that just test, so in your example it would generate a function of:
defmodule String.Chars do
# ... other stuff
def to_string(%MyMod{}=v), do: String.Chars.MyMod.to_string(v)
# ... other stuff
end
I have an enhanced Protocol library that you can see the code of how it works if curious too. Can also look at the elixir code as well.
This does only explain it roughly, but should be just enough to understand it.
Kernel.to_string/1
is a macro, expanding to String.Chars.to_string/1
String.Chars.to_string/1
matches on its argument, and without protocol consolidation it dispatches to String.Chars.xxx.to_string/1
where xxx
is the qualified alias of the struct or elixirs internal type. With consolidation those xxx
implementations are integrated directly into String.Chars.to_string/1
.With consolidation it might look like this (simplified):
defmodule String.Chars do
def to_string(list) when is_list(list), do: List.to_string(list)
def to_string(%Foo{bar: bar}), do: "#Foo<bar: #{bar}>"
end
without consilidation more like this:
defmodule String.Chars do
def to_string(list) when is_list(list), do: List.to_string(list)
def to_string(%struct{} = item), do: :"Elixir.#{__MODULE__}.#{struct}" |> apply(:to_string, [item])
end