Dialyzer confusion over equal opaque and transparent types

Dániel Szoboszlay dszoboszlay@REDACTED
Thu Feb 27 00:38:13 CET 2020

On Wed, 26 Feb 2020 at 00:24, Kostis Sagonas <kostis@REDACTED> wrote:

> Right.  Since you have figured out what the problem is, why don't you
> use some solution along those lines?

I figured out a *workaround* for a bug in Dialyzer. That's lucky, but I
would strongly prefer the bug to be fixed. Or actually, multiple bugs:

   - The warning produced by Dialyzer is wrong. A tool simply cannot check
   my code and tell me "this line is incorrect, you should change it to X"
   where X is a verbatim copy of what I have there already. If a tool tells me
   this, it is a bug and should be fixed.
   - If there is any kind of problem Dialyzer should warn me about, it is
   within the foo module, and not in bar. If the type spec of foo:f/1 is
   correct, the type spec of bar:f/1 must be correct as well - I hope we can
   agree on this one at least. But it implies that Dialyzer shall give a
   warning on foo:f/1 too if it gives a warning on bar:f/1. It does not, so
   this is a bug that should be fixed.
   - And finally, there's a question about whether foo:f/1 does something
   wrong or not. Dialyzer believes it does, I believe it does not. I'm not
   sure who's right, that's what I wanted to ask in my first email. If I'm
   right, and foo:f/1 doesn't have any type errors, than that's the third a
   bug in Dialyzer.

> Note that x/1 is a module-local function, so there is really no good
> reason to declare a spec for it, let alone declare that it is called
> with some opaque term.

First of all, I think there's a good reason for declaring specs for
module-local functions too, namely that the developer can read them and
know how to use the functions.
But my real problem with this advise is this: what would happen if foo:x/1
would be an exported function?

> You are probably missing a very subtle difference between -type and
> -opaque declarations. (This is not your fault, because I think that the
> reference manual does not explain this very well.)  Opaque declarations
> are actually of the form TypeName :: TermStructure, i.e., the right hand
> side declares the structure of the opaque terms of that type name.
> They do not declare type aliases (as e.g., the -type int() :: atom().
> declaration does).

This doesn't seem to match the reference manual:

New types are declared using -type and -opaque attributes as in the

  -type my_struct_type() :: Type.
  -opaque my_opaq_type() :: Type.

The right hand side is documented to be a Type in both cases, and both
attributes are documented to declare new types.

Also, I don't understand how opaque declaring a TermStructure would explain
the confusion of Dialyzer. Consider the below two functions (in module foo)
for example:

-spec g(x) -> ok.
g(X) ->

-spec h(x) -> ok.
h(X = x) ->

-spec i(x | integer()) -> ok.
i(X) when is_integer(X) ->
i(X) ->

Calling g/1 from a different module triggers the warning, but calling h/1
or i/1 is OK. Why? The success typing of g/1 is ((ext()) -> ok), but why is
the caller checked against this type, when the spec clearly restricts the
argument to a single, transparent atom? The success typing of i/1 is
((integer() | ext()) -> ok) as well, but the caller is checked against the
declared type spec this time (even though the success typing of the caller
still contains ext() instead of the atom x).

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20200227/5cc7af0b/attachment.htm>

More information about the erlang-questions mailing list