principle of least surprise

classic Classic list List threaded Threaded
14 messages Options
Reply | Threaded
Open this post in threaded view
|

principle of least surprise

Martin Bjorklund-2
After the obfuscation contest we now know that parentheses are
important in guards...

I have a datatype foo which is either an atom or a tuple of size 2.

It would be nice with a macro to test if a certain value is a foo,
e.g.

  -define(is_foo(X), (is_atom(X) or (is_tuple(X) and (size(X) == 2)))).

Then I could use this test in guards,

  f(X) when ?is_foo(X) -> yes;
  f(X) -> no.

Isn't this reasonable?  Anyone can read and understand this code.

The problem is that this won't work; if I call f(foo) it will return
no.  The reason is that all expressions in my guard will be evaluated,
and that failure in a boolean expression will fail the guard which is
interpreted as false. (and in this case size(foo) fails).

So I tried some alternatives:

  -define(is_foo(X), (atom(X) or (tuple(X) and (size(X) == 2)))).

not that I thought that this would work, but it won't even compile.
Why do we have atom/1 and is_atom/1???

And I know that this one doesn't work.

  -define(is_foo(X), (is_atom(X) orelse (is_tuple(X) andalso (size(X) == 2)))).

Sigh.

Maybe we shouldn't be allowed to write code like this?  No...

My radical suggestion is:

  o  make sure or,and  etc has precedence over ==,/= etc
     (like orelse/andalso)
  o  _remove_ orelse/andalso completely from the language
     (what's the probability of that?)

And then I think (size(X) == 2) should be false if X is not something
you can do size on.  But that's probably out of the question.



/martin


Reply | Threaded
Open this post in threaded view
|

principle of least surprise

Magnus Fröberg-2
But, surprise:

-define(is_foo(X), is_atom(X) ; is_tuple(X), (size(X) == 2)).

/Magnus

Martin Bjorklund wrote:

>After the obfuscation contest we now know that parentheses are
>important in guards...
>
>I have a datatype foo which is either an atom or a tuple of size 2.
>
>It would be nice with a macro to test if a certain value is a foo,
>e.g.
>
>  -define(is_foo(X), (is_atom(X) or (is_tuple(X) and (size(X) == 2)))).
>
>Then I could use this test in guards,
>
>  f(X) when ?is_foo(X) -> yes;
>  f(X) -> no.
>
>Isn't this reasonable?  Anyone can read and understand this code.
>
>The problem is that this won't work; if I call f(foo) it will return
>no.  The reason is that all expressions in my guard will be evaluated,
>and that failure in a boolean expression will fail the guard which is
>interpreted as false. (and in this case size(foo) fails).
>
>So I tried some alternatives:
>
>  -define(is_foo(X), (atom(X) or (tuple(X) and (size(X) == 2)))).
>
>not that I thought that this would work, but it won't even compile.
>Why do we have atom/1 and is_atom/1???
>
>And I know that this one doesn't work.
>
>  -define(is_foo(X), (is_atom(X) orelse (is_tuple(X) andalso (size(X) == 2)))).
>
>Sigh.
>
>Maybe we shouldn't be allowed to write code like this?  No...
>
>My radical suggestion is:
>
>  o  make sure or,and  etc has precedence over ==,/= etc
>     (like orelse/andalso)
>  o  _remove_ orelse/andalso completely from the language
>     (what's the probability of that?)
>
>And then I think (size(X) == 2) should be false if X is not something
>you can do size on.  But that's probably out of the question.
>
>
>
>/martin
>  
>


Reply | Threaded
Open this post in threaded view
|

principle of least surprise

Martin Bjorklund-2
Magnus Fr?berg <mfroberg> wrote:
> But, surprise:
>
> -define(is_foo(X), is_atom(X) ; is_tuple(X), (size(X) == 2)).

Right, but then you can't use is_foo as other booleans, e.g.

    IsBar = (?is_foo(X) or is_list(X)),

or whatever.



/martin





>
> /Magnus
>
> Martin Bjorklund wrote:
>
> >After the obfuscation contest we now know that parentheses are
> >important in guards...
> >
> >I have a datatype foo which is either an atom or a tuple of size 2.
> >
> >It would be nice with a macro to test if a certain value is a foo,
> >e.g.
> >
> >  -define(is_foo(X), (is_atom(X) or (is_tuple(X) and (size(X) == 2)))).
> >
> >Then I could use this test in guards,
> >
> >  f(X) when ?is_foo(X) -> yes;
> >  f(X) -> no.
> >
> >Isn't this reasonable?  Anyone can read and understand this code.
> >
> >The problem is that this won't work; if I call f(foo) it will return
> >no.  The reason is that all expressions in my guard will be evaluated,
> >and that failure in a boolean expression will fail the guard which is
> >interpreted as false. (and in this case size(foo) fails).
> >
> >So I tried some alternatives:
> >
> >  -define(is_foo(X), (atom(X) or (tuple(X) and (size(X) == 2)))).
> >
> >not that I thought that this would work, but it won't even compile.
> >Why do we have atom/1 and is_atom/1???
> >
> >And I know that this one doesn't work.
> >
> >  -define(is_foo(X), (is_atom(X) orelse (is_tuple(X) andalso (size(X) == 2)))).
> >
> >Sigh.
> >
> >Maybe we shouldn't be allowed to write code like this?  No...
> >
> >My radical suggestion is:
> >
> >  o  make sure or,and  etc has precedence over ==,/= etc
> >     (like orelse/andalso)
> >  o  _remove_ orelse/andalso completely from the language
> >     (what's the probability of that?)
> >
> >And then I think (size(X) == 2) should be false if X is not something
> >you can do size on.  But that's probably out of the question.
> >
> >
> >
> >/martin
> >  
> >
>


Reply | Threaded
Open this post in threaded view
|

principle of least surprise

Raimo Niskanen-3
In reply to this post by Martin Bjorklund-2
I guess your radical suggestions to change precedence for
'and' and 'or', or to remove 'orelse' and also 'andalso'
are out of the question, for backwards compatibility
reasons.

What about allowing 'orelse' and also 'andalso' in guards?
That will probably happen one day.


mbj (Martin Bjorklund) writes:

> After the obfuscation contest we now know that parentheses are
> important in guards...
>
> I have a datatype foo which is either an atom or a tuple of size 2.
>
> It would be nice with a macro to test if a certain value is a foo,
> e.g.
>
>   -define(is_foo(X), (is_atom(X) or (is_tuple(X) and (size(X) == 2)))).
>
> Then I could use this test in guards,
>
>   f(X) when ?is_foo(X) -> yes;
>   f(X) -> no.
>
> Isn't this reasonable?  Anyone can read and understand this code.
>
> The problem is that this won't work; if I call f(foo) it will return
> no.  The reason is that all expressions in my guard will be evaluated,
> and that failure in a boolean expression will fail the guard which is
> interpreted as false. (and in this case size(foo) fails).
>
> So I tried some alternatives:
>
>   -define(is_foo(X), (atom(X) or (tuple(X) and (size(X) == 2)))).
>
> not that I thought that this would work, but it won't even compile.
> Why do we have atom/1 and is_atom/1???
>
> And I know that this one doesn't work.
>
>   -define(is_foo(X), (is_atom(X) orelse (is_tuple(X) andalso (size(X) == 2)))).
>
> Sigh.
>
> Maybe we shouldn't be allowed to write code like this?  No...
>
> My radical suggestion is:
>
>   o  make sure or,and  etc has precedence over ==,/= etc
>      (like orelse/andalso)
>   o  _remove_ orelse/andalso completely from the language
>      (what's the probability of that?)
>
> And then I think (size(X) == 2) should be false if X is not something
> you can do size on.  But that's probably out of the question.
>
>
>
> /martin

--

/ Raimo Niskanen, Erlang/OTP, Ericsson AB


Reply | Threaded
Open this post in threaded view
|

principle of least surprise

Martin Bjorklund-2
Raimo Niskanen <raimo> wrote:
> I guess your radical suggestions to change precedence for
> 'and' and 'or', or to remove 'orelse' and also 'andalso'
> are out of the question, for backwards compatibility
> reasons.

Do you really think that changing precedence for and/or will break old
code?

As for andalso and orelse, I realize that it probably won't happen,
but if it did, is it really such a big thing?  A large project would
bring in the new erlang and recompile, and then the compiler would
complain.  It's trivial to change the code (if precedence for or/and
is changed).  Or maybe you could use some flag to the compiler which
could be used for old code?

> What about allowing 'orelse' and also 'andalso' in guards?
> That will probably happen one day.

Better than nothing of course, but wouldn't it be great to fix this
mis-feature and make the language better?


/martin


Reply | Threaded
Open this post in threaded view
|

principle of least surprise

Martin Bjorklund-2
mbj wrote:
> As for andalso and orelse, I realize that it probably won't happen,
> but if it did, is it really such a big thing?  A large project would
> bring in the new erlang and recompile, and then the compiler would
> complain.  It's trivial to change the code (if precedence for or/and
> is changed).

I meant 'if evaluation rules for and/or are changed'.  or something.


/martin


Reply | Threaded
Open this post in threaded view
|

principle of least surprise

Nic Linker
In reply to this post by Martin Bjorklund-2

>Better than nothing of course, but wouldn't it be great to fix this
>mis-feature and make the language better?
>  
>
Hm, what, in your opinion, about the possibility to be an arbitrary
function (returning true or false, of course) in guards? Will it make
the language better?

Best regards,
Linker Nick.


Reply | Threaded
Open this post in threaded view
|

principle of least surprise

Martin Bjorklund-2
Nick Linker <xlcr> wrote:
>
> >Better than nothing of course, but wouldn't it be great to fix this
> >mis-feature and make the language better?
> >  
> >
> Hm, what, in your opinion, about the possibility to be an arbitrary
> function (returning true or false, of course) in guards? Will it make
> the language better?

Maybe I should have added "and more consistent".  Anyway I think
arbitrary functions in guards is an orthogonal question.  Personally I
think it would be nice but it's not that big deal.


/martin


Reply | Threaded
Open this post in threaded view
|

principle of least surprise

Thomas Lindgren-5


--- mbj wrote:

> Nick Linker <xlcr> wrote:
> >
> > >Better than nothing of course, but wouldn't it be
> great to fix this
> > >mis-feature and make the language better?
> > >  
> > >
> > Hm, what, in your opinion, about the possibility
> to be an arbitrary
> > function (returning true or false, of course) in
> guards? Will it make
> > the language better?
>
> Maybe I should have added "and more consistent".
> Anyway I think
> arbitrary functions in guards is an orthogonal
> question.  Personally I
> think it would be nice but it's not that big deal.

Well, you seem to want to introduce abstractions into
your guards by way of macros (?is_foo), and also seem
to want to use the same thing in expressions ... so
actually permitting you to do that by means of the
plain old function call (is_foo) doesn't seem like
quite an orthogonal issue.

I do agree that guards beyond the use of "," and ";"
seem unsatisfactory at this point. (Basically, I think
things would have turned out for the better if the
designers had taken their inspiration from Prolog.)
Nor do I really see the point of two collections of
nearly-identical type test primitives (is_X/1 vs X/1).

Best,
Thomas



               
__________________________________
Yahoo! FareChase: Search multiple travel sites in one click.
http://farechase.yahoo.com


Reply | Threaded
Open this post in threaded view
|

principle of least surprise

Bengt Kleberg-4
On 2005-11-22 15:25, Thomas Lindgren wrote:
...deleted
> Nor do I really see the point of two collections of
> nearly-identical type test primitives (is_X/1 vs X/1).

the question is better stated as why adding is_X/1 when there already
exists X/1?

afaik the X/1 type test primitives where the only ones in the beginning.
  is_X/1 where added afterward. backwards compatibility makes X/1 stay.


bengt


Reply | Threaded
Open this post in threaded view
|

principle of least surprise

Robert Virding-5
In reply to this post by Martin Bjorklund-2
The problem here would be where you get code which goes through the
compiler but is now semantically different because of the changed
precedence. I can imagine how popular that would be!

Robert

mbj wrote:

>Raimo Niskanen <raimo> wrote:
>  
>
>>I guess your radical suggestions to change precedence for
>>'and' and 'or', or to remove 'orelse' and also 'andalso'
>>are out of the question, for backwards compatibility
>>reasons.
>>    
>>
>
>Do you really think that changing precedence for and/or will break old
>code?
>
>As for andalso and orelse, I realize that it probably won't happen,
>but if it did, is it really such a big thing?  A large project would
>bring in the new erlang and recompile, and then the compiler would
>complain.  It's trivial to change the code (if precedence for or/and
>is changed).  Or maybe you could use some flag to the compiler which
>could be used for old code?
>
>  
>
>>What about allowing 'orelse' and also 'andalso' in guards?
>>That will probably happen one day.
>>    
>>
>
>Better than nothing of course, but wouldn't it be great to fix this
>mis-feature and make the language better?
>
>
>/martin
>
>  
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20051122/071465ee/attachment.html>

Reply | Threaded
Open this post in threaded view
|

principle of least surprise

Robert Virding-5
In reply to this post by Martin Bjorklund-2
Martin Bjorklund wrote:

>After the obfuscation contest we now know that parentheses are
>important in guards...
>
>I have a datatype foo which is either an atom or a tuple of size 2.
>
>It would be nice with a macro to test if a certain value is a foo,
>e.g.
>
>  -define(is_foo(X), (is_atom(X) or (is_tuple(X) and (size(X) == 2)))).
>
>Then I could use this test in guards,
>
>  f(X) when ?is_foo(X) -> yes;
>  f(X) -> no.
>
>Isn't this reasonable?  Anyone can read and understand this code.
>
>The problem is that this won't work; if I call f(foo) it will return
>no.  The reason is that all expressions in my guard will be evaluated,
>and that failure in a boolean expression will fail the guard which is
>interpreted as false. (and in this case size(foo) fails).
>  
>
Are you sure that this is the reason? When boolean guard expressions
were added we defined a strict left to right evaluation jst so we could
handle cases like this. One of my standard test cases was something like:

    when is_atom(X) or (is_integer(X) and (X + 5 < 6)) ->

(test cases are seldom reasonable). It worked then. Has the compiler
been changed since then?

>So I tried some alternatives:
>
>  -define(is_foo(X), (atom(X) or (tuple(X) and (size(X) == 2)))).
>
>not that I thought that this would work, but it won't even compile.
>Why do we have atom/1 and is_atom/1???
>
>And I know that this one doesn't work.
>
>  -define(is_foo(X), (is_atom(X) orelse (is_tuple(X) andalso (size(X) == 2)))).
>
>Sigh.
>
>Maybe we shouldn't be allowed to write code like this?  No...
>
>My radical suggestion is:
>
>  o  make sure or,and  etc has precedence over ==,/= etc
>     (like orelse/andalso)
>  o  _remove_ orelse/andalso completely from the language
>     (what's the probability of that?)
>
>And then I think (size(X) == 2) should be false if X is not something
>you can do size on.  But that's probably out of the question.
>  
>
I quite honestly don't really see the problem with the precedences.
Whatever is wrong with your macro it isn't the precedences. How do you
envisage that (size(X) == 2) be false? What should size of something
usizable return?

Since and/or behave as "normal" operators it was thought better to add
new constructions with explicit left-to-right semantics rather than
modify and/or. After a while you get into a horrible mess if you start
special casing certain functions (operators are functions).

Robert


Reply | Threaded
Open this post in threaded view
|

principle of least surprise

Martin Bjorklund-2
Robert Virding <robert.virding> wrote:
> I quite honestly don't really see the problem with the precedences.

Sorry, that referred to the obfuscation contest at euc.   The problem
can be summarized by this:

    if 1 == 1 or is_integer(1) ->
           erlang:display("is_integer"),
       true ->
           erlang:display("else")
    end.


> Whatever is wrong with your macro it isn't the precedences. How do you
> envisage that (size(X) == 2) be false? What should size of something
> usizable return?

No you're right.  Evaluation order is the answer.


/martin


Reply | Threaded
Open this post in threaded view
|

principle of least surprise

Raimo Niskanen-3
In reply to this post by Robert Virding-5
>     when is_atom(X) or (is_integer(X) and (X + 5 < 6)) ->

That does not work, at least not in the shell.

1> if is_atom(a) or (is_integer(a) and (a+5<6)) -> true; true -> false end.
false

OTOH:

2> if is_atom(a) or (is_integer(a) and (a<11)) -> true; true -> false end.
true

works since it does not have to evaluate a+5, which crashes,
causing 1> to fail.

Unfortunately there is no workaround for size(X) that
does not crash for an atom X.

robert.virding (Robert Virding) writes:

> Martin Bjorklund wrote:
>
> >After the obfuscation contest we now know that parentheses are
> >important in guards...
> >
> >I have a datatype foo which is either an atom or a tuple of size 2.
> >
> >It would be nice with a macro to test if a certain value is a foo,
> >e.g.
> >
> >  -define(is_foo(X), (is_atom(X) or (is_tuple(X) and (size(X) == 2)))).
> >
> > Then I could use this test in guards,  f(X) when ?is_foo(X) -> yes;
> >  f(X) -> no.
> >
> >Isn't this reasonable?  Anyone can read and understand this code.
> >
> >The problem is that this won't work; if I call f(foo) it will return
> >no.  The reason is that all expressions in my guard will be evaluated,
> >and that failure in a boolean expression will fail the guard which is
> >interpreted as false. (and in this case size(foo) fails).
> >
> Are you sure that this is the reason? When boolean guard expressions
> were added we defined a strict left to right evaluation jst so we
> could handle cases like this. One of my standard test cases was
> something like:
>
>     when is_atom(X) or (is_integer(X) and (X + 5 < 6)) ->
>
> (test cases are seldom reasonable). It worked then. Has the compiler
> been changed since then?
>
> >So I tried some alternatives:
> >
> >  -define(is_foo(X), (atom(X) or (tuple(X) and (size(X) == 2)))).
> >
> >not that I thought that this would work, but it won't even compile.
> >Why do we have atom/1 and is_atom/1???
> >
> >And I know that this one doesn't work.
> >
> >  -define(is_foo(X), (is_atom(X) orelse (is_tuple(X) andalso (size(X) == 2)))).
> >
> >Sigh.
> >
> >Maybe we shouldn't be allowed to write code like this?  No...
> >
> >My radical suggestion is:
> >
> >  o  make sure or,and  etc has precedence over ==,/= etc
> >     (like orelse/andalso)
> >  o  _remove_ orelse/andalso completely from the language
> >     (what's the probability of that?)
> >
> >And then I think (size(X) == 2) should be false if X is not something
> >you can do size on.  But that's probably out of the question.
> >
> I quite honestly don't really see the problem with the
> precedences. Whatever is wrong with your macro it isn't the
> precedences. How do you envisage that (size(X) == 2) be false? What
> should size of something usizable return?
>
> Since and/or behave as "normal" operators it was thought better to add
> new constructions with explicit left-to-right semantics rather than
> modify and/or. After a while you get into a horrible mess if you start
> special casing certain functions (operators are functions).
>
> Robert

--

/ Raimo Niskanen, Erlang/OTP, Ericsson AB