Defining Behaviour with Protocol requirement

Are there ways enforcing a module to implement protocols ?

I’m using behaviour to check implementing functions now. It seems like:

defmodule Vehicle do
  @callback name() :: String.t()

defmodule Car do
  @behaviour Vehicle

  defstruct details: ""

  @impl true
  def name(), do: "Car"

  defimpl String.Chars do
    def to_string(%{details: details}), do: "introduction #{details} outroduction"

When name() does not defined in Car module, the compiler warns it.
But if String.Chars.to_string() is missing, no warnings are produced.

I think it’s good to be able to warn not implemented protocols. How can you do that?

No warning is produced because it’s not a mandatory protocol to implement so I don’t see why the compiler would warn you about it.

Then, my question is if there is a way enforcing the module to always implement protocol like behaviour.

For example, can I define protocol implementation as callback? like:

defmodule Vehicle do
  @callback String.Chars.to_string(t) :: String.t()

(This produced invalid type specification error.)

It’s doable if you write a __using__ macro that encapsulates the @behaviour (like use GenServer does) that also has an @after_compile hook that manually checks for the existence of the defimpl. You might not want to bother, though.

Using macro was an idea, and @after_compile is interesting, I did not think about it. But I feel these are too much as you say.

Thank you!

yeah, elixir just isn’t rigourously a statically-typed language. I do think it’s neat that Elixir lets you drop in static-ish things in if you don’t mind putting in a little bit of effort. (I think it’s worth making highly critical things statically checked)… Of late, I’m doing a lot of static typechecking by putting my deploy configs in as verified, compile-time artifacts, which prevents me from attempting to deploy stupid things, and it’s been great.

How are you doing that?