I am reading with interest the online book "learn you some erlang" and trying some exercises to check my understanding.
I made some modification on the fifo example, in the chapter Type Specifications and Erlang, trying to define a "typed_fifo(T)" (a fifo where all elements must be of the same type T)
my type specification are:
-type typed_empty_fifo() :: {fifo, [], []}.
-type typed_nonempty_fifo(A) :: {fifo, nonempty_list(A), list(A)} | {fifo, [],nonempty_list(A) }.
-type typed_fifo(A) :: typed_empty_fifo() | typed_nonempty_fifo(A).
and when I use it in the following function spec:
-spec empty (typed_empty_fifo()) -> true;
(typed_nonempty_fifo(_)) -> false.
empty({fifo, [], []}) -> true;
empty({fifo, A, B}) when is_list(A), is_list(B) -> false.
Dialyzer tells that it will ignore the specification because of overlapping domain.
Can somebody tells me where I make a mistake?
I have another point, before trying to define typed fifo I had a version that worked nicely, An Dialyzer show me that nothing prevent the usage of improper lists. Surprizingly, I do not find a simple way (that I can use in a guard) to test for proper/improper character of a list.
It is really strange, because when I use the bif length/1, it is able to fail with the reason badarg!
23> L=[1,2|3]. ==> [1,2|3]
24> is_list(L). ==> true
25> length(L). ==> exception error: bad argument
in function length/1 called as length([1,2|3])
Thanks
Nothing is wrong with your types and spec. The problem is that the datatype which is used in Dialyzer for the representation of types does not keep as much precision as you are providing. Specifically, the union: {fifo, nonempty_list(A), list(A)} | {fifo, [], nonempty_list(A)}
is "crushed" into {fifo, list(A), list(A)}
, as the tuples have the same arity (3) and first atom element (fifo
). Dialyzer generally does over-approximations (as you can also see here) to make the type analysis more efficient. You can safely ignore this warning.
For your second question, is_list/1
only checks whether the first constructor of the term that is passed as its argument is a cons cell. Even is_list([1|2])
returns true
.
If you want to ensure that an argument is a proper list you can use a custom function in a case
expression like this:
case is_proper_list(L) of
true -> ...;
false -> ...
end
is_proper_list([]) -> true;
is_proper_list([_|L]) -> is_proper_list(L);
is_proper_list(_) -> false.
This can't be placed in a guard however. In guards you can use the one that you suggest in your comment below (length(L) >= 0
).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With