fireproofsocks

fireproofsocks

I’m confused… when grooming the docs for https://elixir-lang.org/getting-started/typespecs-and-behaviours.html I got the feedback that multiple function clauses require multiple @specs. But now that I’m digging into dialyzer, I’m getting errors in places where I’ve done that. E.g.

Overloaded contract for MyApp.some_function/1 has
overlapping domains; such contracts are currently unsupported and
are simply ignored.

Consider this module:

defmodule MyApp.Helpers do
  
  alias Absinthe.Blueprint.Input.String, as: AbsintheString
  alias Absinthe.Blueprint.Input.Integer, as: AbsintheInteger
  alias Absinthe.Blueprint.Input.Float, as: AbsintheFloat
  
  @spec parse_value(AbsintheString.t() | AbsintheInteger.t() | AbsintheFloat.t() | any()) :: {:ok, String.t()} | {:error, any()}
  def parse_value(%AbsintheString{value: value}) do
    {:ok, value}
  end
  
  def parse_value(%AbsintheInteger{value: value}) do
    {:ok, Integer.to_string(value)}
  end
  
  def parse_value(%AbsintheFloat{value: value}) do
    {:ok, Float.to_string(value)}
  end


  def parse_value(_) do
    {:error, "Invalid value"}
  end
end

Dialyzer seems happy when it has only a single combined @spec, but readability is better when each function clause has its own @spec, something more like this:

  @spec parse_value(AbsintheString.t()) :: {:ok, String.t()}
  def parse_value(%AbsintheString{value: value}) do
    {:ok, value}
  end

  @spec parse_value(AbsintheInteger.t()) :: {:ok, String.t()}
  def parse_value(%AbsintheInteger{value: value}) do
    {:ok, Integer.to_string(value)}
  end
  # ... etc...

Which way is correct?

Showing Posts 1 to 6

NobbZ

NobbZ

Both are correct.

Use whichever you prefer, though having multiple specs can lead to “overlapping” specs, which are not allowed.

Also ... | ... | any can be simplified to any.

Last but not least, dialyzer will combine the second version into the first anyway.

fireproofsocks

fireproofsocks OP

What exactly are “overlapping specs”?

ityonemo

ityonemo

In the example you gave the two typepecs are quite literally identical, so the preimage and range of both “functions” trivially overlaps. Here’s a nonoverlapping typespec with associated function:

@spec foo(a :: :give_me_a_string) :: String.t
@spec foo(a :: :give_me_a_list) :: list
def foo(:give_me_a_list), do: 'foo'
def foo(:give_me_a_string), do: "foo"

I prefer to group my typespecs for distinct function entry points (this is rare) together, and I personally organize spec, doc, function bodies. But these are stylistic choices.

fireproofsocks

fireproofsocks OP

AbsintheString and AbsintheInteger (or any disparate structs) are identical?

ityonemo

ityonemo

whoops, that detail missed me.

michallepicki

michallepicki

You’re only getting this warning because of the fallback function clause with any. If types for all function arguments overlap, Dialyzer will show a warning (not an error AFAIK) and (I think?) it will use only the last version.

Multiple specs are sometimes useful when you can have different argument types and maybe different return types for them. Separate @spec can look cleaner than a big one with many | ... | ....

But if specs are the same (or overlapping) I would probably use just one. Notice what ex_doc generates for the same specs: it will just list the same thing multiple times.

— All posts loaded —

Where Next? Top

Trending in Questions Top

stjefim
Hello! Suppose you are building workflow (order / task / payment) processing system with the following requirements: Each workflow con...
New
water
I’m aware of 2 such projects, are there others? git: GitHub - team-alembic/realworld: A fullstack Phoenix LiveView application with back...
New
peck
Hi everyone. My team and I have been working on a fairly modest app based around video streaming and chat, but we’ve landed a customer t...
New
bradley
I really like the adapter patterns that ecto, nebulex, waffle, etc. use and would love find something similar for a key management servic...
New
unaware8150
Hello folks! So at work, we are seeing some situations where we have to define some “fixed” strings that are used across the codebase in...
New
Alvinkariuki
How Can I Optimise Compile Time Dependencies I have been building an elixir application for about 2 years now. Many modules and files ha...
New
leoschweizer
Hi, we run different LLM models across a cluster, with each node loading a configurable subset. Model-specific queues let nodes consume ...
New

Other Trending Topics Top

mudasobwa
I fully migrated to my own harness from Anthropic/Gemini and I think it’s time to share it. Welcome DSH, the DeepSeek Harness, fully writ...
New
KristerV
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
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
juhalehtonen
There has been a thread to discuss the Stack Overflow Developer Survey on this forum every year since 2018, so here’s yet another one for...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New
budgie
I love Elixir. It’s one of 2 programming languages I’ve ever fallen in love with. But I don’t use it anymore. Serverless was the promis...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews