-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New Lesson: Protocols #825
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some suggestion changes, thoughts?
lessons/advanced/protocols.md
Outdated
lang: en | ||
--- | ||
|
||
In this lesson we are going to look at Protocols, and how to leverage them in Elixir. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this lesson we are going to look at Protocols, what they are, and how we use them in Elixir.
lessons/advanced/protocols.md
Outdated
{% include toc.html %} | ||
|
||
## What Are Protocols | ||
Protocols are the chosen method for polymorphism in Elixir. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So what are they? Protocols are a means of achieving polymorphism in Elixir. One pain of Erlang is extending an existing API for newly defined types. To avoid this in Elixir the function is dispatched dynamically based on the value's type. Elixir comes with a number of protocols built in, for example the String.Chars
protocol is responsible for the to_string/1
function we've seen used previously. Let's take a closer look at to_string/1
with a quick example:
lessons/advanced/protocols.md
Outdated
"foo" | ||
``` | ||
|
||
As you can see, we have called this on multiple types. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As you can see we've called the function on multiple types and demonstrated that it work on them all. What if we call to_string/1
on tuples (or any type that hasn't implemented String.Char
)? Let's see:
lessons/advanced/protocols.md
Outdated
``` | ||
|
||
As you can see, we get a protocol error. | ||
This is because there is no implementation for the Protocol in the tuples. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As you can see we get a protocol error as there is no implementation for tuples. In the next section we'll implement the String.Chars
protocol for tuples.
Implementing a protocol
We saw that to_string/1
has not yet been implemented for tuples so let's add it. To create an implementation we'll use defimpl
with our protocol, and provide the :for
option and our type. Let's take a look at how it might look:
defimpl String.Chars, for: Tuple do
def to_string(tuple) do
insides =
tuple
|> Tuple.to_list
|> Enum.map(&Kernel.to_string/1)
|> Enum.join(", ")
"{#{insides}}"
end
end
If we copy this into IEx we should be now be able to call to_string/1
on a tuple without getting an error:
iex> to_string({3.14, "apple", :pie})
"{3.14, apple, pie}"
lessons/advanced/protocols.md
Outdated
This is because there is no implementation for the Protocol in the tuples. | ||
To implement a protocol, `defimpl/3` is our tool of choice. | ||
|
||
## Working With Protocols |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about we use an example that can be applied to a few different types? Something like:
We know how to implement a protocol but how we do we define a new one? For our example we'll implement to_atom/1
, let's see how to do that with defprotocol
:
defprotocol AsAtom do
def to_atom(data)
end
defimpl AsAtom, for: Atom do
def to_atom(atom), do: atom
end
defimpl AsAtom, for: BitString do
defdelegate to_atom(string), to: String
end
defimpl AsAtom, for: List do
defdelegate to_atom(list), to: List
end
Here we've defined our protocol and it's expected method, to_atom/1
, along with implementations for a few types. Now that we have our protocol, let's put it to use in IEx:
iex> import AsAtom
AsAtom
iex> to_atom("string")
:string
iex> to_atom(:an_atom)
:an_atom
iex> to_atom([1, 2])
:"\x01\x02"
@ybur-yug thoughts on the feedback? |
@doomspork let me read over this all after work today and give another pass :) |
2f73c3d
to
48eb9db
Compare
Finished a cleanup of a first proposed draft with the whole lesson after initial start + feedback. Feel to comment/review etc, all. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ybur-yug looks good, one comment.
lessons/advanced/protocols.md
Outdated
:"\x01\x02" | ||
``` | ||
|
||
As we can see, protocols are a powerful way to achieve polymorphism, but we also want to be cautious with what features we spread between modules to avoid unintended results. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This part I'm not sure I get: ", but we also want to be cautious with what features we spread between modules to avoid unintended results."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you implement stuff that just catches all and returns something (possible, but stupid) you are going to have a bad time and not get errors that should be being raised etc. I guess maybe its not worth mentioning. Makes sense without it still, too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ybur-yug that isn't what that sentence conveys though, not to me at least.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, @ybur-yug!
I've proposed some improvements, looking forward to get your opinion on these.
lessons/advanced/protocols.md
Outdated
``` | ||
|
||
As you can see we've called the function on multiple types and demonstrated that it work on them all. | ||
What if we call `to_string/1` on tuples (or any type that hasn't implemented `String.Char`)? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
String.Char
→ String.Chars
lessons/advanced/protocols.md
Outdated
"{3.14, apple, pie}" | ||
``` | ||
|
||
We know how to implement a protocol but how we do we define a new one? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we do we
→ do we
lessons/advanced/protocols.md
Outdated
defimpl AsAtom, for: List do | ||
defdelegate to_atom(list), to: List | ||
end | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I am not mistaken Map
's protocol implementation will not work for structs.
Thus probably it's worth to extend this example by adding a protocol implementation for Map
and shortly explain how protocols work with structs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll have to look into this one. I'm going to give the lesson a second pass today at the airport/on my plane.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea @nscyclone 👍
48eb9db
to
d1c6e4e
Compare
lessons/advanced/protocols.md
Outdated
``` | ||
|
||
We know how to implement a protocol but how do we define a new one? | ||
For our example we'll implement `to_atom/1` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we're missing punctuation here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good @ybur-yug! I think we're missing punctuation for one sentence but this is otherwise good-to-go 👍
d1c6e4e
to
1c8d5cd
Compare
@doomspork incorporated + pushed |
Add Protocols Lesson
This is a first run at a lesson on Protocols for the advanced section from what I started with and finished with @doomspork's feedback.
TODO
Create issue for translation once reviewed/mergedTranslation Issue