# Dialyzer cannot recognize error in function using polymorphic types

**URL:** <https://forum.devtalk.com/t/dialyzer-cannot-recognize-error-in-function-using-polymorphic-types/40944>\
**Category:** Backend Questions/Help\
**Tags:** elixir, types\
**Created:** [16 February 2022 16:35 UTC](https://forum.devtalk.com/t/dialyzer-cannot-recognize-error-in-function-using-polymorphic-types/40944 "2022-02-16T16:35:31Z")\
**Posts on this page:** 1\
**Showing post:** 9

<div class="post-metadata">

**Author:** ![Fl4m3Ph03n1x](https://forum.devtalk.com/user_avatar/forum.devtalk.com/fl4m3ph03n1x/32/3808_2.png) [@Fl4m3Ph03n1x](https://forum.devtalk.com/u/Fl4m3Ph03n1x)\
**Post date:** [18 February 2022 13:51 UTC](https://forum.devtalk.com/t/dialyzer-cannot-recognize-error-in-function-using-polymorphic-types/40944/9 "2022-02-18T13:51:55Z")

</div>

## Answer

After talking with many folks I have come to understand that as long as 1 path in my code leads to a successful execution that is compatible with the specs I provide, dialyzer will not complain.

There may be many paths that break, but as long as 1 works, dialzyer is happy.

In my specific case, because I have a branch that returns `[{:some, String.t}]` dialyzer does not complain, because I have 1 branch that succeeds.

This can be better summarized in the quote from [Type Specifications and Eralng](https://learnyousomeerlang.com/dialyzer):

> The other option is then to have a type system that will not _prove_ the absence of errors, but will do a best effort at detecting whatever it can. You can make such detection really good, but it will never be perfect. It’s a tradeoff to be made.

In fact, the article mentioned above, has an example case very similar to my own:

```erlang
main() ->
    X = case fetch() of
        1 -> some_atom;
        2 -> 3.14
    end,
    convert(X).

convert(X) when is_atom(X) -> {atom, X}.

```

Which also does not trigger dialyzer. According to the article:

> From our point of view, it appears that at some point in time, the call to `convert/1` will fail.  
> (…)  
> Dialyzer doesn’t think so. (…) because there is the _possibility_ that the function call to `convert/1` succeeds at some point, Dialyzer will keep silent. No type error is reported in this case.

This is quite enlightening and I am convinced this is what is happening in my case.

## Will dialyzer ever understand what is going on?

To be fair, this error can be caught by dialyzer if we use some flags, namelly `--overspecs` (for this case) and its sister `--underspecs` (which we don’t need for this case).

After some research I was able to find a mailing list that details the behaviour of these flags in a mathematical format:

- [[erlang-questions] Dialyzer underspecs and overspecs options](https://erlang.org/pipermail/erlang-questions/2017-March/091997.html)

From it:

> Let SpecIn be a set of @spec inputs and RealIn be a set of inputs as inferred by Dialyzer from real code, then:
> 
> - The Input Contract is satisfied when SpecIn \<= RealIn (where \<= is a non-strict subset operation). See over\_in in demo code below.
> - The Input Contract violation is detected by -Wunderspecs option when SpecIn \> RealIn. See under\_in below.
> 
> It is easy to see in the code:
> 
> - It’s OK for over\_in to declare that it only accepts :a and :b while it also happens to accept :c. Maybe suboptimal, but fine.
> - It’s NOT OK for under\_in to claim that it accepts :a, :b and :c and break if :c is passed. Rejecting :c would break the caller.
> 
> Let SpecOut be a set of @spec outputs and RealOut be a set of outputs as inferred by Dialyzer from real code, then:
> 
> - The Output Contract is satisfied when SpecOut \>= RealOut (where \>= is a non-strict superset operation). See under\_out below.
> - The Output Contract violation is detected by -Woverspecs option when SpecOut \< RealOut. See over\_out below.
> 
> It is easy to see in the code:
> 
> - It’s OK for under\_out to declare that it returns :a, :b and :c, while currently it only returns :a and :b. Maybe future implementations will return :c as well.
> - It’s NOT OK for over\_out to declare that it returns :a and :b, but to also return :c sometimes. Returning :c would break the caller.

And indeed if I run `mix dialyzer --overspecs` with this sample, dialzyer does complain becasue:

> The Output Contract violation is detected by -Woverspecs option when SpecOut \< RealOut. See over\_out below.

Where RealOut is `[] | [t] | nil ` and SpecOut is `[] | [t]`.  
Therefore, a contract violation is detected.

Error shown:

```auto
lib/test.ex:6:missing_range
The type specification is missing types returned by function.

Function:
Test.validate_name/1

Type specification return types:
[{:some, binary()}]

Missing from spec:
nil

```

This was a wild ride through Dialyzer, and a revision I quite honestly needed. During the whole ordeal I learned about a couple of dialyzer flags and I refreshed my memory on success typing (which I absolutely needed to).

Thank you everyone for participating !

---

_[View the full topic](https://forum.devtalk.com/t/dialyzer-cannot-recognize-error-in-function-using-polymorphic-types/40944)._
