I would say protocols are for datatype polymorphism, often to get similar behaviour from different input data. Behaviours are for functional polymorphism, often to get different implementation details from similar contracts. Both can solve similar problems but have their own idiomatic wood grains to cut with or against.
Both involve functions accepting dynamic input that changes how the program executes: just as a function that accepts dynamic data may invoke a protocol to get different results, a function that accepts a dynamic module may invoke a behaviour to get different results.
Generally, unless we are doing supervision stuff/adapter work, our applications want to act similarly on different data more than we want them to act differently on similar interfaces, which is more of a library concern. Additionally, the type system and dialyzer work well enough with dynamic data types but dynamic modules is currently more of a black box for editor experiences. So I’d say the average application has more reason to be concerned with protocols than behaviours in practice, despite both mechanisms being open for extension in some sense to application developers.
But I would disagree with the claim that behaviours are not a mechanism for polymorphism; just a mechanism with different and less-common aims. I think my first paragraph there is a half-remembered direct quote from José, I’ll see if I can find the source. Some keynote maybe?






















