[erlang-questions] dialyzer: user-defined types just synonyms?

2 views
Skip to first unread message

Dmitry Belyaev

unread,
Apr 22, 2010, 7:42:24 AM4/22/10
to erlang-questions
I want to define my type as a synonym for built-in type. But I want
dialyzer to warn me if a function is called with wrong type.
I have following code and dialyzer says everything is nice. But I want
it to warn about "StrFromStr = my_to_str(Str)" line.
Can I make it? How?

I remember Haskell's "newtype" would be of desired behaviour.

-module(test).

-export([start/1]).

-type my_str() :: string().

-spec start(string()) -> tuple().
start(Str0) ->
MyStr = str_to_my(Str0),
Str = my_to_str(MyStr),

MyFromMy = str_to_my(MyStr),
StrFromStr = my_to_str(Str),

{Str, MyFromMy, StrFromStr}.

-spec str_to_my(string()) -> my_str().
str_to_my(Str) ->
Str.

-spec my_to_str(MyStr) -> string() when is_subtype(MyStr, my_str()).
my_to_str(MyStr) ->
MyStr.


________________________________________________________________
erlang-questions (at) erlang.org mailing list.
See http://www.erlang.org/faq.html
To unsubscribe; mailto:erlang-questio...@erlang.org

--
You received this message because you are subscribed to the Google Groups "Erlang Programming" group.
To post to this group, send email to erlang-pr...@googlegroups.com.
To unsubscribe from this group, send email to erlang-programm...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/erlang-programming?hl=en.

Dmitry Belyaev

unread,
Apr 22, 2010, 9:18:26 AM4/22/10
to erlang-questions
And one more question. I have a code:
-module(test).

-export([start/0]).

-spec start() -> ok.
start() ->
%%check_atom(bad),
case check_atom(good) of
ok -> ok;
error -> error
end.

-spec check_atom(good | nice) -> ok | error.
check_atom(Atom) ->
ok.

Dialyzer shows:
test.erl:10: The pattern 'error' can never match the type 'ok'
I'd like it to pay more attention to -spec than to function's body so I
wouldn't get this message. Is it possible?

I understand that I can filter these messages with grep but this way I
may throw away something important.

Kostis Sagonas

unread,
Apr 22, 2010, 9:47:41 AM4/22/10
to Dmitry Belyaev, Erlang
Dmitry Belyaev wrote:
> And one more question. I have a code:
> -module(test).
>
> -export([start/0]).
>
> -spec start() -> ok.
> start() ->
> %%check_atom(bad),
> case check_atom(good) of
> ok -> ok;
> error -> error
> end.
>
> -spec check_atom(good | nice) -> ok | error.
> check_atom(Atom) ->
> ok.
>
> Dialyzer shows:
> test.erl:10: The pattern 'error' can never match the type 'ok'
> I'd like it to pay more attention to -spec than to function's body so I
> wouldn't get this message. Is it possible?

You've defined two functions in your example, one that returns either
'ok' or 'error' and you give it a spec that says it only returns 'ok'
and one that clearly returns 'ok' only and your spec claims it returns
'error' also. Why on earth you would you want to do that in your
program? If you want obfuscated code, write in C instead.

Dialyzer has detected that you are clearly an evil programmer :-)
Why should it be nice to you?

Kostis

Dmitry Belyaev

unread,
Apr 22, 2010, 9:57:09 AM4/22/10
to Kostis Sagonas, Erlang
Cause I may extend this function in the future. Or got this version
after refactoring and don't want to change code that uses it.

Kostis Sagonas

unread,
Apr 22, 2010, 9:58:27 AM4/22/10
to erlang-questions
Dmitry Belyaev wrote:
> I want to define my type as a synonym for built-in type. But I want
> dialyzer to warn me if a function is called with wrong type.
> I have following code and dialyzer says everything is nice. But I want
> it to warn about "StrFromStr = my_to_str(Str)" line.
> Can I make it? How?

You cannot make it. The notion of equality between types is structural,
not nominative. (http://en.wikipedia.org/wiki/Structural_type_system)

Kostis

Dmitry Belyaev

unread,
Apr 22, 2010, 10:16:01 AM4/22/10
to Tobias Lindahl, erlang-questions
I thought type specs are made for external typechecks. Ok. Now I have
this code:
-module(test).

-export([start/0]).

-spec start() -> ok.
start() ->
AtomBad = get_atom(1),
AtomGood = get_atom(2),
check_atom(AtomBad),
case check_atom(AtomGood) of
ok -> ok;
error -> error
end.

-spec get_atom(any()) -> good | bad.
get_atom(1) ->
bad;
get_atom(_) ->
good.

-spec check_atom(good | nice) -> ok | error.
check_atom(Atom) ->
ok.

It doesn't warn me about check_atom(AtomBad).

Tobias Lindahl wrote:
> 2010/4/22 Dmitry Belyaev <rumata...@nm.ru>:
>
>> And one more question. I have a code:
>> -module(test).
>>
>> -export([start/0]).
>>
>> -spec start() -> ok.
>> start() ->
>> %%check_atom(bad),
>> case check_atom(good) of
>> ok -> ok;
>> error -> error
>> end.
>>
>> -spec check_atom(good | nice) -> ok | error.
>> check_atom(Atom) ->
>> ok.
>>
>> Dialyzer shows:
>> test.erl:10: The pattern 'error' can never match the type 'ok'
>> I'd like it to pay more attention to -spec than to function's body so I
>> wouldn't get this message. Is it possible?
>>
>
> Once again, no. You cannot widen a success typing of a function using
> a spec. The type of the function will be the intersection of the type
> in the spec and whatever type Dialyzer infers for the function. In
> this case it finds that the error clause is unreachable since the
> function unconditionally returns ok.
>
> Tobias

Dmitry Belyaev

unread,
Apr 22, 2010, 10:19:32 AM4/22/10
to Kostis Sagonas, erlang-questions
So is it impossible to distinguish plain string came from user and
string prepared for database (escaped)?
I don't want to put it in some container like tuple.

Kostis Sagonas

unread,
Apr 22, 2010, 10:18:58 AM4/22/10
to Dmitry Belyaev, erlang-questions, Tobias Lindahl
Dmitry Belyaev wrote:
> I thought type specs are made for external typechecks. Ok. Now I have
> this code:
> -module(test).
>
> -export([start/0]).
>
> -spec start() -> ok.
> start() ->
> AtomBad = get_atom(1),
> AtomGood = get_atom(2),
> check_atom(AtomBad),
> case check_atom(AtomGood) of
> ok -> ok;
> error -> error
> end.
>
> -spec get_atom(any()) -> good | bad.
> get_atom(1) ->
> bad;
> get_atom(_) ->
> good.
>
> -spec check_atom(good | nice) -> ok | error.
> check_atom(Atom) ->
> ok.
>
> It doesn't warn me about check_atom(AtomBad).

It does; see below. You are using an older Erlang/OTP.
Upgrade to R13B04.

Kostis

test.erl:12: The pattern 'error' can never match the type 'ok'
test.erl:21: The specification for test:check_atom/1 states that the
function might also return 'error' but the inferred return is 'ok'

Dmitry Belyaev

unread,
Apr 22, 2010, 10:26:09 AM4/22/10
to Tobias Lindahl, erlang-questions
It would be nice if it would intersect spec parameters and check spec
result contain what dialyzer infers.

So, if I have
-spec function(A) -> B.
and dialyzer finds that real type as function(C) -> D then it would
check that C contains A and B contains D and its final type would be
function(C) -> B.

Tobias Lindahl wrote:
> 2010/4/22 Dmitry Belyaev <rumata...@nm.ru>:
>
>> And one more question. I have a code:
>> -module(test).
>>
>> -export([start/0]).
>>
>> -spec start() -> ok.
>> start() ->
>> %%check_atom(bad),
>> case check_atom(good) of
>> ok -> ok;
>> error -> error
>> end.
>>
>> -spec check_atom(good | nice) -> ok | error.
>> check_atom(Atom) ->
>> ok.
>>
>> Dialyzer shows:
>> test.erl:10: The pattern 'error' can never match the type 'ok'
>> I'd like it to pay more attention to -spec than to function's body so I
>> wouldn't get this message. Is it possible?
>>
>
> Once again, no. You cannot widen a success typing of a function using
> a spec. The type of the function will be the intersection of the type
> in the spec and whatever type Dialyzer infers for the function. In
> this case it finds that the error clause is unreachable since the
> function unconditionally returns ok.
>
> Tobias
>
>


Dmitry Belyaev

unread,
Apr 22, 2010, 10:32:18 AM4/22/10
to Kostis Sagonas, erlang-questions, Tobias Lindahl
Yes, I use R1303.
But the desired warning is about line 11: check_atom(AtomBad) would fail
because AtomBad type is 'bad'. Or at least ('good'|'bad').

Dmitry Belyaev

unread,
Apr 22, 2010, 11:13:17 AM4/22/10
to Tobias Lindahl, erlang-questions
I saw those options already but they don't do what I'd like to see.

I have very complex system. And I am _heavily_ refactoring it. And
thought dialyzer would be of great help for me.
Now I see many functions that long ago had (and may will have in the
future) wider result type than now dialyzer infers. I get many messages
like those (currently I grep them from output):

.*: The pattern .* can never match since previous clauses completely
covered the type .*
.*: The pattern .* can never match the type .*
.*: The variable .* can never match since previous clauses completely
covered the type .*

I don't want to change this code (it may be needed in future). I'd
better make result type of function wider, so dialyzer won't warn about
superflous (by this moment) code.

Tobias Lindahl wrote:
> 2010/4/22 Dmitry Belyaev <rumata...@nm.ru>:
>
>> It would be nice if it would intersect spec parameters and check spec result
>> contain what dialyzer infers.
>>
>> So, if I have
>> -spec function(A) -> B.
>> and dialyzer finds that real type as function(C) -> D then it would check
>> that C contains A and B contains D and its final type would be
>> function(C) -> B.
>>
>>
>
> Dialyzer checks that there is a non-empty intersection between the
> spec type and the infered type. In other words if the spec is possible
> at all. If not, you will get a warning.
>
> You might want to have a look at the options -Wunderspecs and
> -Woverspecs. They give you some more information about how the specs
> relate to the inferred types. They are off by default since you
> typically get a lot of warnings from them.

Dmitry Belyaev

unread,
Apr 22, 2010, 11:35:56 AM4/22/10
to Tobias Lindahl, erlang-questions
Good point about unused=untested.
Ok, but what about the other problem?
http://pastebin.com/q7muyTnz

There must be warning on line 9. I get nothing with R13b4.

Tobias Lindahl wrote:
> 2010/4/22 Dmitry Belyaev <rumata...@nm.ru>:
>
>> I saw those options already but they don't do what I'd like to see.
>>
>> I have very complex system. And I am _heavily_ refactoring it. And thought
>> dialyzer would be of great help for me.
>> Now I see many functions that long ago had (and may will have in the future)
>> wider result type than now dialyzer infers. I get many messages like those
>> (currently I grep them from output):
>>
>> .*: The pattern .* can never match since previous clauses completely covered
>> the type .*
>> .*: The pattern .* can never match the type .*
>> .*: The variable .* can never match since previous clauses completely
>> covered the type .*
>>
>> I don't want to change this code (it may be needed in future). I'd better
>> make result type of function wider, so dialyzer won't warn about superflous
>> (by this moment) code.
>>
>
> I'm sorry that I can't help you with this.
>
> However, if you are refactoring your code and creating a lot of dead
> code as you do it, it might not be a bad idea to remove the dead code.
> The argument that you might need it in the future is a bit weak since
> the code will be lying around and being untested until that.
>
> Personally, I would have removed the code since it then becomes clear
> that whatever functionality it implements has not been needed, nor
> tested, since the refactoring.
>
> Tobias

Dmitry Belyaev

unread,
Apr 23, 2010, 4:10:48 AM4/23/10
to Tobias Lindahl, erlang-questions
So it will warn only if the code will always fail, but will not if it
just _may_ fail. And this behaviour is what it was made for.

I see, thank you.

Tobias Lindahl wrote:
> 2010/4/22 Dmitry Belyaev <rumata...@nm.ru>:
>
>> Good point about unused=untested.
>> Ok, but what about the other problem?
>> http://pastebin.com/q7muyTnz
>>
>> There must be warning on line 9. I get nothing with R13b4.
>>
>
> The return type for get_atom is 'bad' | 'good'.
>
> The analysis is not strong enough to find that the call at line 7 is
> returning 'bad', so when the return value is used in the call on line
> 9, Dialyzer finds that the intersection between the argument type and
> the success typing is 'good' which means that the call can succeed and
> no warning is emitted.
>
> The behavior is correct, but one could always whish that the analysis
> was stronger. Remember that Dialyzer is conservative in its warnings.
> It will only warn when it can find that something definitely goes
> wrong.
>
> Tobias
Reply all
Reply to author
Forward
0 new messages