troelsim

troelsim

Dialyzer and defoverridable: Warning if overrided function does not return all possible values

Hello

I’m struggling coming up with a good solution to this problem. I have a modules useing a generic module to override some default behaviour, very much like the following boiled-down example:

defmodule Handler do
  defmacro __using__(_opts) do
    quote do
      @behaviour Handler

      def delegate(args) do
        case handle(args) do
          :ok ->
            :ok

          {:error, reason} = result ->
            do_something(result)
            :ok
        end
      end

      defp do_something(_) do
        # Log stuff or whatever
      end

      defoverridable Handler
    end
  end

  @callback handle(any) :: :ok | {:error, any}
  @optional_callbacks handle: 1
end

defmodule Implementation do
  use Handler

  @impl true
  def handle(_) do
    :ok
  end
end

defmodule OtherImplementation do
  use Handler

  @impl true
  def handle(_) do
    {:error, "something went wrong"}
  end
end

The Implementation and OtherImplementation modules each use the Handler module, which provides for example some common error handling by matching the output from the handle function that each implementation implements. This works fine. The problem is the dialyzer warnings.

Since the delegate function is basically copied into each of the implementation modules, dialyzer will complain that return values of each of the handle functions “can never match the type” of one of the patterns in the case statement:

lib/dialyzer_test.ex:30:pattern_match
The pattern can never match the type.

Pattern:
_ = {:error, _}

Type:
:ok

________________________________________________________________________________
lib/dialyzer_test.ex:30:unused_fun
Function do_something/1 will never be called.
________________________________________________________________________________
lib/dialyzer_test.ex:39:pattern_match
The pattern can never match the type.

Pattern:
:ok

The thing is, that a given implementation does not necessarily return all possible return values, which to dialyzer looks like there is unreachable error handling code in each of those modules.

The code works fine, but the warnings are bugging me. I know that I can suppress them, but is there a better way to implement this type of behaviour that makes dialyzer less grumpy? Is overriding functions this way a too OOP way of thinking? For context, the pattern comes up in a plug_rest application where a number of REST resources use a Default module for common things like authentication and error handling.

Any thoughts?

First 3 of 3 Posts! Switch mode

LostKobrakai

LostKobrakai

Imho it is: Why duplicate the delegate functionality if it’s not implementation dependant:

defmodule Handler do
  def delegate(impl, args) do
    case impl.handle(args) do
      :ok ->
        :ok

      {:error, reason} = result ->
        do_something(result)
        :ok
    end
  end

  defp do_something(_) do
    # Log stuff or whatever
  end

  @callback handle(any) :: :ok | {:error, any}
  @optional_callbacks handle: 1
end

defmodule Implementation do
  @behaviour Handler

  @impl true
  def handle(_) do
    :ok
  end
end

defmodule OtherImplementation do
  @behaviour Handler

  @impl true
  def handle(_) do
    {:error, "something went wrong"}
  end
end
troelsim

troelsim

Good point. In my provided example, it is definitely cleaner to not override functions and just use behaviours. Maybe the question makes more sense in the full context: All of the implementations have to implement a certain behaviour (in my case it is PlugRest.Resource), and having a “default” implementation which different modules can use and override to their liking is an easy way to reuse code. But I am starting to feel that these dialyzer warnings are an unavoidable cost of trying to use “inheritance” in elixir…

LostKobrakai

LostKobrakai

If implementations fall back to an default then I’d still make that explicit:

def SomeImpl do
  @behaviour PlugRest.Resource

  def some_callback(args), do: DefaultImpl.delegate(args)
  
  def another_callback(_) do
    {:error, "something went wrong"}
  end
end

Where Next?

Trending in Questions Top

jonnycharles
I’m in search of an Elixir library that offers PDF generation capabilities similar to Ruby’s Prawn. While there have been discussions abo...
New
spammy
I’m looking to build a personal workflow to quickly deploy web applications written in elixir/phoenix, for local consumption (ie not on t...
New
silverdr
Using Phoenix.LiveView.TagEngine as an EEx.Engine is deprecated! To compile HEEx, use Phoenix.LiveView.TagEngine.compile/2 instead. Sta...
New
saveman71
Hello ! We want new/edit form pages to POST/PUT to their own URL rather than the resources REST defaults (post /things, put /things/:id)...
New
dli
Before I dive in myself, did anyone successfully sprinkle Hologram into their existing LiveView app? Looking for hints regarding: Addi...
New
bottlenecked
Hi all, I wanted to ask how the community is dealing with post-release steps. Today we have Ecto migrations, which make sure that the db...
New
michallepicki
I am using Oban and occasionally, shortly after a deployment, a handful of jobs can fail because of dependency on other parts of the syst...
New

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve. They are GUI (Emerge) and State management (S...
New
ausimian
Emily is an Elixir library that runs Nx computations on Apple’s MLX. Install it as the default Nx backend and Nx, defn, Axon, Nx.Serving,...
New
type1fool
I just stumbled on a newly redesigned elixir-lang.org. :tada: It looks like @Software_Mansion did the work, and I think it is generally a...
New
akoutmos
@hugobarauna and I (Alex Koutmos) have been hard at work on writing a book on Nerves that takes you from simply blinking LEDs to building...
New

We're in Beta

About us Mission Statement