The big difference I see between Elixir and (example) Java or Ruby is that everything is an object in those languages but a big Elixir application has only a few GenServers, where keeping a state matters. Everything else is passed as argument, which by the way it's what a GenServer does internally with an endless tail recursion.
If you look at the entire breadth of OO languages and see what they have in common, there's really only two things that are truly common: object identity, and some form of dynamic dispatch based on that. Everything else - inheritance, encapsulation etc - is optional.
> there's really only two things that are truly common: object identity, and some form of dynamic dispatch based on that. Everything else - inheritance, encapsulation etc - is optional.
That's a broader definition than I had in mind, but I can see where you're coming from.
(Certainly moreso than something like C++, which wouldn’t look out of place on a Melanesian island. Never could figure why C++ and all its typed kin would choose to dispatch [dynamically] on only one argument instead of on all. Playing to their weaknesses, no?)
Can't say I know a lot about multiple-dispatch though.
I imagine some syntax sugar to help with the delegation to make it less cumbersome.
If a language doesn't support inheritance, in what sense is it an object-oriented programming language?
It's true that you can use OOP principles in languages that don't have language-level support for OOP. The canonical example of this is the GObject library for C. That doesn't tell us how we should use the term object-oriented programming language though. I don't see why we'd use a more expansive definition.