tim2CF

tim2CF

How to make Dialyzer more strict?

If we have function with typespec that always returns value with incorrect type

@spec hello(integer()) :: map()
  def hello(int) do
    case int do
      1 -> :foo
      _ -> :wtf
    end
  end

then Dialyzer generates warning, good. It is as expected.

done in 0m1.08s
lib/hello.ex:16: Invalid type specification for function 'Elixir.Hello':hello/1. The success typing is (_) -> 'foo' | 'wtf'
done (warnings were emitted)

But if

  @spec hello(integer()) :: map()
  def hello(int) do
    case int do
      1 -> %{hello: "world"}
      _ -> :wtf
    end
  end

then

done in 0m1.07s
done (passed successfully)

Why it not generates any warnings when there is clause where wrong value is returned from function?
And if it is feature, not bug - how to make behaviour more strict?
It would be perfect if there will be flag to generate warnings when at least one possible clause has incorrect type.

Marked As Solved

losvedir

losvedir

Came across this discussion when searching for Elixir types / dialyzer stuff. I think it’s worth mentioning that with the latest dialyxir (1.0.0-rc7) and Erlang/OTP 22.2, and the overspecs flag that Jose mentioned, dialyzer does catch the error as expected with the example:

lib/test_types.ex:15: The success typing for 'Elixir.TestTypes':hello/1 implies that the function might also return 'wtf' but the specification return is map()

Unfortunately, overspecs is noisy and fails, for example this function and spec:

  @spec hi(integer()) :: :foo | :bar
  def hi(n) do
    if n < 5 do
      :foo
    else
      :bar
    end
  end

with:

Type specification is a subtype of the success typing.

Function:
TestTypes.hi/1

Type specification:
@spec hi(integer()) :: :foo | :bar

Success typing:
@spec hi(_) :: :bar | :foo

You can fix it by adding def hi(n) when is_integer(n) do.

So it seems like overspecs might be something you could add when you’re starting a project, but hard to enable after the fact.

Also Liked

rvirding

rvirding

Creator of Erlang

The reason for this is that dialyzer was added afterwards to a very dynamically typed language, then Erlang and now Elixir, with the requirement that it was not allowed to change the language. At all! So it does what it can. You will find that it doen’t just type a function but type checks a function and the calls made to it together to see if they agree.

Also in some ways dialyzer basically ignores a function spec when type checking so you don’t really need one to get the type checking. If you give one it will check that it is consistent with what it can work out about the function and it will use it when reporting type errors, which can be very useful. Actually same with type defs. Though both are extremely useful for documentation.

The compiler always ignores type specs except for some basic syntax checks.

Depressing no? :smile:

12
Post #3
josevalim

josevalim

Creator of Elixir

Did you try the overspecs/underspecs flags? Erlang -- dialyzer

Dialyzer is fine for the trade-offs it chose to make. Having something more strict most likely means rejecting code that is completely valid today. Or in the best case scenario, forcing types to be explicit and not relying on inference.

@tmbb is correct here. There aren’t low hanging fruits when it comes to types. Adding a type system involves many compromises and the compromises you are willing to make change a lot when the language already exists.

If you want to have type inference, then many Elixir idioms will no longer be supported. Elixir code is based on unions (it returns {:ok, _} | :error) and intersections (different clauses of a function) and those are very expensive for an inference engine.

We could add requirements, such as obligatory typing of inputs, but that doesn’t solve the problem that we have both typed and non-typed code and we should likely check the values at the boundaries, adding runtime cost.

If you want to keep the language the same and support all of the existing idioms and also not worry about conversion costs: that’s Dialyzer.

Long story short: we could have other type systems but you need to start from scratch and you will have to compromise in many ways.

Apologies but I did not mean that Clojure Spec is the future. It is unlikely we will have something like Clojure Spec in Elixir per-se, especially because we have typespecs and guards and adding a third option is going to bring only marginal gains. If somebody wants to explore something like Clojure Spec as a package, then please do!

stefanchrobot

stefanchrobot

Question is: can this be improved in the future? TypeScript is doing a similar thing for JavaScript. On the bright side, the coverage of different use cases is increasing while keeping the compilation time fast. The bad thing is that IMO the type system is kind of exploding with complexity.

Seems like Elixir could add opt-in strict type checking.

gon782

gon782

Maybe Elixir could just be what it is and other languages on the BEAM can build a foundation much more suited to this kind of thing. I would rather have something designed from the get-go to be strict than trying to bolt it onto Elixir/Erlang.

What I mean is that I think you’ll see better results trying to design for something like this much earlier and languages like Alpaca are probably a better bet in this regard. If the people who really want a strongly, statically typed on the BEAM (myself included) simply tried to help with Alpaca, maybe we could end up with something that could even become our BEAM language of choice.

hypno

hypno

I’m not an expert on types but we already have type declaration syntax that the community actively using in Elixir. The problem is how Dialyzer interprets them (too loose). Maybe a stupid question but can’t we just build source code level (Elixir AST) analyzer as a hex package (archive?) that reads source code like Code.string_to_quoted |> Macro.expand, and uses the type specs AST to match them against the source code AST?

We are very interested of stricter typing in our company and are willing to put a remarkable bounty on this project, if it is viable option ofcourse :slight_smile:

Where Next?

Popular in Questions Top

Tee
can someone please explain to me how Enum.reduce works with maps
New
_russellb
I want to try my hand at web scraping. What tools/libraries do I need to use. I’m hoping to turn this into something professional so don’...
New
sacepums
Hey guys. I'm new to elixir and im really stocked about it. But I ran into a bit of problem - I need to convert a date sting, for examp...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
gonzofish
I’m currently trying to understand how to join three tables using Ecto. All the examples I’ve seen use 2, so maybe I’m just missing somet...
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
sabri
Can someone explain the settings of pool_size of Ecto in config file? and what is the recommend size? Thanks
New
idi527
I’ve been re-reading swift book again and noticed that multiline strings there don’t have a trailing line break, unlike in elixir iex(2)...
New

Other popular topics Top

sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
977 41022 311
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
_russellb
I want to try my hand at web scraping. What tools/libraries do I need to use. I’m hoping to turn this into something professional so don’...
New
grych
Hi folks, Few months ago I have announced the proof-of-concept of the library to manipulate the browsers DOM objects directly from Elixi...
639 49522 488
New
danschultzer
None of the current solutions worked well for me, so I went ahead and built a user management system from scratch. This project took far...
548 27727 240
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list....
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New
magnetic
Hey :wave:t3: Elixir community, I’ve been learning Elixir, and working on some side projects. My editor of choice is VSCode, and althoug...
New

We're in Beta

About us Mission Statement