The other thing about behaviours is... Especially if you have a synchronous API, you can mock it with a stateful system, for example calling out to a GenServer or calling over the network. This is why GenServer.call doesn't emit an error tuple; an network or interprocess error by default is considered to be unrecoverable. In most other systems you'll fumble around with colored functions transitioning between a sync system or async call, or, have to do annoying error handling, or even worse stack unwinding with an exception or get stuck in a panic.
In Erlang, a sync API behavior can safely have a failable async implementation, and that is powerful
In Erlang, a sync API behavior can safely have a failable async implementation, and that is powerful