One advantage I see with elixir’s protocols is consistent naming of functions with similar implementations. As in, there’s no guarantee that a datastructure will use the name
map for maps or
andThen for monadic binds, as is idiomatic within the elm community. Most people tend to favor consistent semantics. It makes swapping between choices of datastructure easier, not to have to remember the domain-specific name for that pattern
But it’s somewhat tangent. The only way polymorphism would apply to these typeclasses is the Java way of doing implicit castes. Say you have a datastructure
foo: Foo, with a protocol implementation for
toString. Then you could write
foo ++ " is a magnificient datastructure"
(toString foo) ++ "is a magnificent datastructure"
So the advantage would be that you don’t have to remember what name the
Foo module author used for the
But of course, reading the code, you may have no clue what method of
Foo specifically turns it into a
String. For example, say
Foo has the methods
toText. Which one does
foo ++ "a string" call? First of all, the module author is sorta dumb for doing this because the users have to just remember what the module author thinks the difference is between "words and “text”. This can get pretty nuanced, sort of like Elixirs distinction between “size” and “length” (I might have preferred “capacity” and “count”, but I digress). Second, the readers and writers have to look up the protocol implementation and remember, on top of these nuances, which the author picked as default. And this can vary from module to module. That’s a lot of cognitive burden!
This is why I, personally, feel differently about polymorphism; I like the explicit rules around type casting, I think explicit castes are the best way to predict how code will behave. It lowers the cognitive burden in cases where a datastructure does different things in different contexts by parameterizing the context, instead of leaving it implicit. Whether or not a language has protocols, it still relies on idiomatic naming to convey appropriate use. Without relying on implicit castes, the author’s previously complex distinction between
toText, although arbirtary, becomes an advantage. The user of the
Foo module has the freedom to choose either implementation to best suite the use-case, and is reminded by the compiler if they forget to make the choice.
But CMV! I’m sure you have a lot more first-hand experience with Elixir’s type system since I have none.