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/1String.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