I've inherited someone else's code base; sometimes I forget that a
function returns a tuple and I up comparing it with an integer or
similar, and get weird behaviour and hard-to-find bugs as a result.
What's the best way to prevent this? I will try writing some unit
tests later today with EUnit, and I'm generating PLTs for Dialyzer as
I write this, but I was wondering if there are any other tools or
"best practices" that could help me to write more "type safe" code?
Thanks in advance! :)
_______________________________________________
erlang-questions mailing list
erlang-q...@erlang.org
http://erlang.org/mailman/listinfo/erlang-questions
The key here is always to check return types whenever you call a function.
You should be thinking 'if there is an error how do I make it appear
as quickly as possible?'.
So with function returns you don't want to pass them round in a mush
of generic variables until the error surfaces 20 fn calls from when it
appeared.
Also check for bad input to functions by using guards like:
fun(A, B, C) when is_list(A), is_integer(B), is_record(C, recC) ->
This will force errors to appear quicker if you do have a mushy
untyped variable error.
Always code the happy path and make everything else an error. If a fn
is designed as the second paramter should be an integer and not a
float then use pattern matching/guards to make it so...
Use the dialyzer to pick up cases when you have let this sort of thing
happen, and write specs to tell dialyzer, yourself and your co-workers
you intentions with functions:
http://www.erlang.org/doc/reference_manual/typespec.html
Gordon
--
Gordon Guthrie
CEO hypernumbers
http://hypernumbers.com
t: hypernumbers
+44 7776 251669
The following is a bit pedantic, but please do not offer use of the
is_record guard (esp. the one with arity two instead of three) as advice
to newcomers. I suggest that you try to forget about this guard's
existence; the only place where this guard is possibly needed is in
or/orelse contexts.
The above code is written better using pattern matching, as in:
fun(A, B, #recC{} = C) when is_list(A), is_integer(B) ->
Kostis
/Håkan
On Wed, Apr 13, 2011 at 12:36 PM, Kostis Sagonas <kos...@cs.ntua.gr> wrote:
> Gordon Guthrie wrote:
>>
>> ...
>>
>> Also check for bad input to functions by using guards like:
>>
>> fun(A, B, C) when is_list(A), is_integer(B), is_record(C, recC) ->
>
> The following is a bit pedantic, but please do not offer use of the
> is_record guard (esp. the one with arity two instead of three) as advice to
> newcomers. I suggest that you try to forget about this guard's existence;
> the only place where this guard is possibly needed is in or/orelse contexts.
>
> The above code is written better using pattern matching, as in:
>
> fun(A, B, #recC{} = C) when is_list(A), is_integer(B) ->
However, it doesn't seem to get the more insidious errors that arise
from comparing other types than was intended, i.e. accidentaly
comparing a string to an integer... but since it's completely valid
Erlang I guess it's not supposed to complain?
I will heed your advice and insert some type checking where
appropriate, thanks :)
The code is written in a very imperative style and I will try to
rewrite it to look more like Erlang as well, which I hope will solve a
lot of my problems.
Kind of off topic, but does anyone have experience with Haskell and
its type system? I keep hearing claims like "if it compiles, it
works!", and so on, which does sound quite nice... if it's true ;)
> > erlang-questi...@erlang.org
> >http://erlang.org/mailman/listinfo/erlang-questions
>
> --http://www.linkedin.com/in/torbenhoffmann
>
> _______________________________________________
> erlang-questions mailing list
> erlang-questi...@erlang.orghttp://erlang.org/mailman/listinfo/erlang-questions
The answer depends on the kind of comparison that is used.
If by comparison you mean case expressions or direct matching as in
case String of
Integer -> ...
end
or
String = Integer
then dialyzer will report these to you as impossible matches.
If by comparison you mean uses of =/=, <, ... then of course these are
allowed in Erlang and of course dialyzer will not warn you that you are
doing something wrong there. (*)
Kostis
(*) Though it will warn you that e.g. the 'false' clause is unreachable
in something like:
case String =\= Integer of
true -> ...
false -> ...
end
I have never read the documentation on is_record/2 before (didn't even
know there was is_record/3)
I *assumed* that the is_record guard checked that it indeed was a
record - it would appear it doesn't.
The documentation also explictly says to use is_record/2
> Note!
> This BIF is documented for completeness. In most cases is_record/2 should be used.
If you shouldn't use it then the documentation should probably say so.
I presume you are saying that using #rec{} in the function call is
correctly expanded by the preprocessor to {rec, _, _, } and is
therefore a better specification of the contract than is_record/2
which just says 'it is a tuple with first element of this atom'
Gordon
On 13 April 2011 11:36, Kostis Sagonas <kos...@cs.ntua.gr> wrote:
> Gordon Guthrie wrote:
>>
>> ...
>>
>> Also check for bad input to functions by using guards like:
>>
>> fun(A, B, C) when is_list(A), is_integer(B), is_record(C, recC) ->
>
> The following is a bit pedantic, but please do not offer use of the
> is_record guard (esp. the one with arity two instead of three) as advice to
> newcomers. I suggest that you try to forget about this guard's existence;
> the only place where this guard is possibly needed is in or/orelse contexts.
>
> The above code is written better using pattern matching, as in:
>
> fun(A, B, #recC{} = C) when is_list(A), is_integer(B) ->
>
> Kostis
>
--
Gordon Guthrie
CEO hypernumbers
http://hypernumbers.com
t: hypernumbers
+44 7776 251669
Thanks again for your advice!
> erlang-questi...@erlang.orghttp://erlang.org/mailman/listinfo/erlang-questions
== /= =< < >= > would only work on numbers
@== @/= @=< @< @>= @> would work on all terms
Note that the existing =:= =/= are the same as @== @/= in my scheme above.
We could add the full set of term comparison operators, but not change the existing operators to be only numeric comparison. The reaction if we did would be "interesting". :-)
Old sins cast long shadows.
Robert
The comparison operators as they are now were a mistake, almost a bad mistake. IMAO what we should have done was to have had two different sets of operators, one set of numeric comparisons (without type conversion) and one set of gerneral term comparisons (without type conversion). So for example:
== /= =< < >= > would only work on numbers
@== @/= @=< @< @>= @> would work on all terms
Note that the existing =:= =/= are the same as @== @/= in my scheme above.
We could add the full set of term comparison operators, but not change the existing operators
There are two somewhat different questions lurking here.
- what's the best way to ensure that such mistakes are caught?
You've mentioned EUnit and Dialyzer, and they are excellent
for this.
- what's the best way to avoid making such mistakes in the first place?
Clear, consistent, informative naming conventions.
Consistent "algebraic" interface design.
Well thought out policy on when to return "oops" as a result
and when to raise an exception -- this is still contentious,
the point here is that you want to *know* the choice a
function made without having to think too much about it.
Perhaps you could provide an example of a function that returns a
tuple that you want to compare with an integer.
> The comparison operators as they are now were a mistake, almost a bad mistake. IMAO what we should have done was to have had two different sets of operators, one set of numeric comparisons (without type conversion) and one set of gerneral term comparisons (without type conversion). So for example:
>
> == /= =< < >= > would only work on numbers
> @== @/= @=< @< @>= @> would work on all terms
Just like Erlang's predecessor Prolog!
Since the term/number distinction _was_ borrowed for equality and
inequality (although with the symbols switched around, which still
confuses me), I've often wondered why it wasn't borrowed for ordering.
Check out Number 6 in this list:
http://mazenharake.wordpress.com/2010/10/31/9-erlang-pitfalls-you-should-know-about/
I believe the #rec{} notation is preferred for clarity and I agree but
in my opinion is_record/2 is just as correct (literally)
/M
Did you read the note in the documentation for is_record/2?
As long as the RecordTag argument is a literal atom, the
compiler will essentially rewrite it to a call to is_record/3,
which will check the size too.
Therefore, we still recommend that you use is_record/2,
if you are going to use is_record() at all.
Historically, only is_record/2 existed and it was not a
BIF, but specially treated by the compiler. We added the
BIF versions so that it would be possible to use apply on
them, and for consistency with match specs. In most
circumstances, the BIFs will not be called as the compiler
tries to convert calls to is_record/{2,3} to pattern matching
and inline most of the remaining calls.
--
Björn Gustavsson, Erlang/OTP, Ericsson AB
> Did you read the note in the documentation for is_record/2?
I did, but in my defence I was confused by Kostis and in a fluster :(
I have re-read it now understand it properly. is_record/2 does what I
always assumed it did (without reading the documentation) and I stand
by my suggestion at the top of this thread.
Gordon
2011/4/14 Björn Gustavsson <bgust...@gmail.com>:
--
Gordon Guthrie
CEO hypernumbers
http://hypernumbers.com
t: hypernumbers
+44 7776 251669
> On 14/04/2011, at 3:52 AM, Robert Virding wrote:
>
> > The comparison operators as they are now were a mistake, almost a
> bad mistake. IMAO what we should have done was to have had two
> different sets of operators, one set of numeric comparisons (without
> type conversion) and one set of gerneral term comparisons (without
> type conversion). So for example:
> >
> > == /= =< < >= > would only work on numbers
> > @== @/= @=< @< @>= @> would work on all terms
>
> Just like Erlang's predecessor Prolog!
> Since the term/number distinction _was_ borrowed for equality and
> inequality (although with the symbols switched around, which still
> confuses me), I've often wondered why it wasn't borrowed for ordering.
Yes, my suggestion is taken directly from the Prolog operator names. I don't have a problem with that. :-)
Originally there was no problem as we did not have floating point numbers. It was only when they were added and we decided to do implicit type conversions that the problem arose. We needed an exact equality check for pattern matching so =:= was added (yes it was a bad name choice) and =/= just tagged along and slipped in.
I suppose I will have to make an eep which suggests the complete range of term comparison operators without type conversions. The problem is whether it is worth effort to add pure numeric comparisons as well.
Robert
> I suppose I will have to make an eep which suggests the complete range
> of term comparison operators without type conversions. The problem is
> whether it is worth effort to add pure numeric comparisons as well.
Not meaning to be an ass, but is it really worth it? Is it *that* big a
problem? Thinking about both in terms of your time and effort and the time
and effort of everyone else who has to change their code to use the new
operators, and also in the added complexity to the language and the
confusion caused by adding the new @ operators.
DBM