<div dir="ltr">Hi,<div><br></div><div>I'm a bit late to this party, but I have a suggestion I haven't seen coming up in the thread. So maybe it could be still useful for you or someone else:</div><div><br></div><div>Consider moving type specs out from your record definition completely! Use a custom type instead:</div><div><br></div><div><font face="monospace">-record(widget, {id, name, size}).</font></div><div><font face="monospace">-type widget() :: #widget{id :: binary(), name :: binary(), size :: binary()}.<br></font><br>This way you can explicitly tell in the function specs whether you're dealing with a properly typed <font face="monospace">widget()</font> or not. You may even define a different type for the parser, and have Dialyzer check against that:</div><div><br></div><div><span style="font-family:monospace">-type partially_parsed_widget() :: #widget{id :: binary()|undefined, name :: binary()</span><span style="font-family:monospace">|</span><span style="font-family:monospace">undefined</span><span style="font-family:monospace">, size :: binary()</span><span style="font-family:monospace">|</span><span style="font-family:monospace">undefined</span><span style="font-family:monospace">}.</span></div><div><span style="font-family:monospace">-spec parse_widget(proplists:proplist()) -> widget().</span></div><div><span style="font-family:monospace">-spec parse_widget(</span><span style="font-family:monospace">proplists:proplist</span><span style="font-family:monospace">(), partially_parsed_widget()) -> widget().</span></div><div><br></div><div>There are lots of cases when you want to use a record's structure with different type constraints than the "usual" use of that record. Generating property based tests and negative tests were already mentioned. But using records in match specifications is also very common. And in case of some simple records you may also use tuples that are not meant to be a record, but look like one (consider someone defining <font face="monospace">-record(error, {err_no :: integer(), msg :: string()})</font>. for example).</div><div><br></div><div>An additional benefit of separating record definitions from type specs is that Dialyzer would actually generate much more useful error messages on type violations. With types in records you may get something like this:</div><div><br></div><div><div><font face="monospace">foo.erl:10: Function test/0 has no local return</font></div><div><font face="monospace">foo.erl:11: Record construction #foo{bar::0} violates the declared type of field bar::pos_integer()</font></div></div><div><br></div><div>The problem is that the "has no local return" error is poisonous: it will propagate to all other functions calling <font face="monospace">foo:test/0</font>, and from there to their callers etc. You'll get a ton of error messages and it will be very hard to figure out the root cause.</div><div><br></div><div>On the other hand when using a separate <font face="monospace">foo()</font> type the error message would become:</div><div><br></div><div><font face="monospace">foo.erl:7: Invalid type specification for function foo:test/0. The success typing is () -> #foo{bar::0}</font><br></div><div><br></div><div>This error will not propagate and will be easy to debug.</div><div><br></div><div>Just my two cents. Cheers,</div><div><br></div><div>Daniel</div><div><br></div><div><div class="gmail_quote"><div dir="ltr">On Mon, 12 Nov 2018 at 11:58 Roger Lipscombe <<a href="mailto:roger@differentpla.net">roger@differentpla.net</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">I've got a record defined as follows (e.g., and very simplified):<br>
<br>
-record widget {<br>
    id :: binary(),<br>
    name :: binary(),<br>
    size :: integer()<br>
}.<br>
<br>
I parse that from (e.g.) a proplist:<br>
<br>
parse_widget(Props) -><br>
    parse_widget(Props, #widget{}).<br>
<br>
parse_widget([{name, Name} | Rest], Acc) -><br>
    parse_widget(Rest, Acc#widget { name = Name });<br>
% etc.<br>
<br>
Dialyzer isn't happy that my fields are initially set to 'undefined',<br>
even though this only occurs during the parsing step, and isn't a big<br>
deal.<br>
<br>
What can I do to deal with this? Either re-structuring my code or<br>
persuading dialyzer that it's OK would both be acceptable.<br>
_______________________________________________<br>
erlang-questions mailing list<br>
<a href="mailto:erlang-questions@erlang.org" target="_blank">erlang-questions@erlang.org</a><br>
<a href="http://erlang.org/mailman/listinfo/erlang-questions" rel="noreferrer" target="_blank">http://erlang.org/mailman/listinfo/erlang-questions</a><br>
</blockquote></div></div></div>