Open falti opened 8 years ago
I have been considering this question myself as well.
One way that I have been leaning would be to use a naming convention similar to the one used for categories in Obj-C. It would have the module name and protocol name separated by a plus: lib/integer+protocol.ex
. This way you have the relevant information in the file name and it's easy to stick to the 'one module per file' convention already in place.
A good example of what Elixir's standard library does, is the Enumerable Protocol. I think we can use this as a good starting point.
Most protocols consist of three parts:
The Enumerable
protocol in Elixir, as well as the public-facing Enum
module built on top of it, as well as the implementations of Enumerable for List and Map are defined inside the enum.ex
file.
Depending on how large your protocol will be, it might be nicer if it is split.
When defining a new implementation of a protocol for some data type, I think it makes the most sense to specify that implementation inside the module where you define that data type, e.g.
This has as advantage that you don't need the for:
part in the call to defimpl
.
defmodule BinaryTree do
defstruct[:left, :right, :value]
defimpl Enumerable do
def reduce do
# some implementation here
end
end
# Other functions here
end
For built-in data types such as Lists, Maps or Strings, I think it makes the most sense if the protocol implementation is placed in the same file as the protocol specification itself.
When an implementation for a protocol is needed, but you don't have access to the module where the type is made because it is part of a dependency (or the core language), then it makes the most sense to me to put the implementation closely to the module where you're using the module implementation in.An example would be to define a currency_formatter implementation for the Decimal data type. Where to put this? Either in the file it is used, or in a file that is referenced by the file where it is used.
If you really want to put it in a separate file, I think that names like "#{datatype_name}_#{protocol_name}_impl.ex"
are the most describing. so decimal_currency_formatter_impl.ex
or something like that.
The rule I usually follow:
@michalmuskala @christopheradams @Qqwy - What would be the course of action here? Has there been an agreement as to what style to use?
I feel like for this one, it seems too broad and there may be too many ways to do this properly. It will be depending on your team to discuss?
Yes, there are multiple, equally valid (to be exact: which version is best depends on the precise situation) approaches. It might still be a good idea to list the multiple common approaches (which @michalmuskala summarized really well! :+1: ) side-by-side.
Yeah, I don't think I have anything to add over what I already wrote here.
@Qqwy @michalmuskala would anyone be keen to make an example of each use case? I don't have much experience with Protocols yet so not sure how to make examples like this one:
# not preferred
# bad protocol example here
# preferred
# not good example here
Or can we just list down @michalmuskala 's examples as is:
And maybe a link to this thread for further reading?
I'm curious as to how you guys test it? Do you defer the action to a Domain module function and test that module? Or do you test the protocol separately?
I think it would help to have a common place to put protocol definitions and also implementations. It would be in particular interesting if protocols are implemented for existing modules.
For discussion:
definition:
implementation for a new modules within the module.
implementation for an existing module (e.g. Integer) in:
or
?