<div dir="ltr">It seems Garrett and Loic missed your actual question. The problem is in prompt_user/1. How did I know that? Well, "has no local return" just means that something that function calls will likely crash, so until you fix the root problem you can ignore those. Likewise, "will never be called" can often be an indicator that something else is wrong.<div><br></div><div>So let's look at prompt_user/1. The error says "call ... breaks the contract (string()) -> string()". This means that the caller of this function is not passing the correct type. Dialyzer thinks the type of the argument in the call on line 89 is [[1..255,...],...]. Herein lies the error, the string() type is an alias for [char()], where char() is 0..16#10ffff, or basically the Unicode character space. Why is this type erroneous? Because you are passing lists of lists to the function, not *flat lists* of characters. That is indeed the case in number_from_user/1 (and possibly elsewhere):</div><div><br></div><div><span style="font-size:12.8000001907349px"> Prompt = ["Enter ", Name, " > "],</span><br style="font-size:12.8000001907349px"><span style="font-size:12.8000001907349px"> to_positive_number(prompt_user</span><span style="font-size:12.8000001907349px">(Prompt)).</span><br style="font-size:12.8000001907349px"></div><div><span style="font-size:12.8000001907349px"><br></span></div><div><span style="font-size:12.8000001907349px">Instead of string() as the argument type, I'd use iodata(), or less desirably, call lists:flatten/1 before passing the prompt.</span></div><div><span style="font-size:12.8000001907349px"><br></span></div><div><span style="font-size:12.8000001907349px">Now, that may solve your issue with dialyzer. However, look at the type of io:get_line/1 (<a href="http://erlang.org/doc/man/io.html#get_line-1">http://erlang.org/doc/man/io.html#get_line-1</a>):</span></div><div><span style="font-size:12.8000001907349px"><br></span></div><div><span style="font-size:12.8000001907349px">get_line(Prompt) -> Data</span><br></div><div><span style="font-size:12.8000001907349px"> ...</span></div><div><span style="font-size:12.8000001907349px"> Data = string() | unicode:unicode_binary()</span></div><div><span style="font-size:12.8000001907349px"><br></span></div><div><span style="font-size:12.8000001907349px">The return value might be a string, or it might be a unicode_binary() if the device is in binary mode! This is not likely for standard input, but you might need to match on that case.</span></div><div><span style="font-size:12.8000001907349px"><br></span></div><div><span style="font-size:12.8000001907349px">However, first fix the spec for prompt_user/1, then see if dialyzer still finds errors.</span></div><div><span style="font-size:12.8000001907349px"><br></span></div><div><span style="font-size:12.8000001907349px">What I like about this exercise of adding specs is it brings up a good point about using dialyzer. Generally, I try NOT to add specs to internal functions unless they are especially complex -- even then it might be a sign I need to refactor. Oftentimes, dialyzer will discover any static errors even without them, especially if I have added specs to my exported functions. There are also many other things that dialyzer uses to infer the types of parts of your program that don't require specs, for example, library functions that you call or destructuring pattern matches. Specs are most useful as documentation and for narrowing the inferred types so that external callers will be encouraged (forced?) to follow the contract. Unlike Hindley-Milner type systems, it can't guarantee that your types are correct, but it can ensure that you don't make a bunch of silly errors.</span></div></div><div class="gmail_extra"><br><div class="gmail_quote">On Mon, Aug 10, 2015 at 7:00 AM, Roelof Wobben <span dir="ltr"><<a href="mailto:r.wobben@home.nl" target="_blank">r.wobben@home.nl</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Hello,<br>
<br>
I try to document this function which I found on another topic.<br>
<br>
%% @author Roelof Wobben <<a href="mailto:r.wobben@home.nl" target="_blank">r.wobben@home.nl</a>><br>
%% @doc Function to calculate the area of a rectangle, a ellipse or a triangle<br>
%% @reference from <a href= "<a href="http://shop.oreilly.com/product/0636920025818.do" rel="noreferrer" target="_blank">http://shop.oreilly.com/product/0636920025818.do</a>" >Introducing Erlang</a>,<br>
%% O'Reilly Media, Inc., 2012.<br>
%% @copyright 2012 by R.Wobben<br>
%% @version 0.1<br>
<br>
-module(ask_area).<br>
<br>
-export([area/0]).<br>
<br>
-spec(area() -> 'ok' | 'error').<br>
<br>
%% @doc a function which looks if the function collect_data<br>
%% outputs a number which resambles the area of the shape the<br>
%% user has choosen with the dimensions which the user has entered<br>
%% if the user entered a wrong shape or not a number on the dimension<br>
%% part , there will be a right error message to tell what went wrong<br>
<br>
area() -><br>
try collect_data() of<br>
Area -> io:format("The area is ~p~n", [Area])<br>
catch<br>
error:Err -> io:format("~s~n", [error_msg(Err)])<br>
end.<br>
<br>
-spec(collect_data() -> number() ).<br>
<br>
%% doc Function which controls the programm.<br>
%% First the shape is asked on the user.<br>
%% After that if a valid shape is entered the dimensions<br>
%% are asked , and if they are valid the area is being calculated.<br>
<br>
collect_data() -><br>
Shape = shape_from_user(),<br>
{X, Y} = dimensions_from_user(Shape),<br>
geom:area(Shape, X, Y).<br>
<br>
<br>
<br>
-spec (shape_from_user() -> 'ellipse' | 'rectangle' | 'triangle' ) .<br>
<br>
%%doc Here the right prompt is made so the user<br>
%% can choose the shape they want.<br>
<br>
shape_from_user() -><br>
Prompt = "R)ectangle, T)riangle, or E)llipse > ",<br>
char_to_shape(prompt_user(Prompt)).<br>
<br>
-spec (prompt_user(string()) -> string()).<br>
<br>
%% doc Here the prompt is displayed and send to<br>
%% the strip function so the newline is stripped.<br>
<br>
prompt_user(Prompt) -><br>
strip_lf(io:get_line(Prompt)).<br>
<br>
-spec (strip_lf (string()) -> string() ).<br>
<br>
%%doc Here the newline is stripped from the userinput.<br>
<br>
strip_lf(Str) -><br>
string:strip(Str, right, $\n).<br>
<br>
<br>
<br>
char_to_shape("R") -> rectangle;<br>
char_to_shape("r") -> rectangle;<br>
char_to_shape("T") -> triangle;<br>
char_to_shape("t") -> triangle;<br>
char_to_shape("E") -> ellipse;<br>
char_to_shape("e") -> ellipse;<br>
char_to_shape(_) -> error(bad_shape).<br>
<br>
dimensions_from_user(rectangle) -><br>
numbers_from_user("width", "height");<br>
dimensions_from_user(triangle) -><br>
numbers_from_user("base", "height");<br>
dimensions_from_user(ellipse) -><br>
numbers_from_user("major axis", "minor axis").<br>
<br>
numbers_from_user(XPrompt, YPrompt) -><br>
X = number_from_user(XPrompt),<br>
Y = number_from_user(YPrompt),<br>
{X, Y}.<br>
<br>
number_from_user(Name) -><br>
Prompt = ["Enter ", Name, " > "],<br>
to_positive_number(prompt_user(Prompt)).<br>
<br>
to_positive_number(Prompt) -><br>
positive_number(to_number(Prompt)).<br>
<br>
positive_number(N) when N > 0 -> N;<br>
positive_number(_) -> error(bad_number).<br>
<br>
to_number(Str) -><br>
try_number([fun list_to_float/1, fun list_to_integer/1], Str).<br>
<br>
try_number([Method|Rest], Arg) -><br>
try<br>
Method(Arg)<br>
catch<br>
error:badarg -> try_number(Rest, Arg)<br>
end;<br>
try_number([], _Arg) -><br>
error(bad_number).<br>
<br>
error_msg(bad_shape) -> "Please enter a valid shape";<br>
error_msg(bad_number) -> "Please enter a positive number".<br>
<br>
<br>
but as soon as I run dialyzer I see this output :<br>
<br>
Checking whether the PLT /home/nitrous/.dialyzer_plt is up-to-date... yes<br>
Proceeding with analysis...<br>
ask_area.erl:34: Function collect_data/0 has no local return<br>
ask_area.erl:75: Function dimensions_from_user/1 has no local return<br>
ask_area.erl:82: Function numbers_from_user/2 has no local return<br>
ask_area.erl:87: Function number_from_user/1 has no local return<br>
ask_area.erl:89: The call ask_area:prompt_user(Prompt::[[1..255,...],...]) breaks the contract (string()) -> string()<br>
ask_area.erl:91: Function to_positive_number/1 will never be called<br>
ask_area.erl:94: Function positive_number/1 will never be called<br>
ask_area.erl:97: Function to_number/1 will never be called<br>
ask_area.erl:100: Function try_number/2 will never be called<br>
done in 0m0.51s done (warnings were emitted)<br>
<br>
hecking whether the PLT /home/nitrous/.dialyzer_plt is up-to-date... yes<br>
Proceeding with analysis...<br>
ask_area.erl:34: Function collect_data/0 has no local return<br>
ask_area.erl:75: Function dimensions_from_user/1 has no local return<br>
ask_area.erl:82: Function numbers_from_user/2 has no local return<br>
ask_area.erl:87: Function number_from_user/1 has no local return<br>
ask_area.erl:89: The call ask_area:prompt_user(Prompt::[[1..255,...],...]) breaks the contract (string()) -> string()<br>
ask_area.erl:91: Function to_positive_number/1 will never be called<br>
ask_area.erl:94: Function positive_number/1 will never be called<br>
ask_area.erl:97: Function to_number/1 will never be called<br>
ask_area.erl:100: Function try_number/2 will never be called<br>
Unknown functions:<br>
geom:area/3<br>
done in 0m0.51s<br>
done (warnings were emitted)<br>
<br>
<br>
<br>
Can someone explain what these messages mean.<br>
This is the first time I use dialyzer and this confuses me.<br>
<br>
Roelof<br>
<br>
<br>
---<br>
Dit e-mailbericht is gecontroleerd op virussen met Avast antivirussoftware.<br>
<a href="https://www.avast.com/antivirus" rel="noreferrer" target="_blank">https://www.avast.com/antivirus</a><br>
<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><br></div>