On 2010-08-02, at 2:35 pm, TSa (Thomas Sandlaß) wrote:
> On Monday, 2. August 2010 20:02:40 Mark J. Reed wrote:
>> [...] it's at least surprising.  I'd expect (anything ~~ True) to be 
>> synonymous with ?(anything)
> Note also that ($anything ~~ foo()) just throws away $anything.

No; only if foo() returns a Bool.  Do check the section on Smart-Matching in 
S03 and Switch Statements in S04; the specced behaviour isn't just a mistake.  
There are only two cases where ~~ ignores the LHS: one is a block that takes no 
args.  A block that takes one arg runs foo($_) (and of course if it takes more 
args, that's an error), but if the block can't take any args, how would you 
apply $_ anyway?  If you want to compare $_ against its result, then you should 
call the block instead of using the Callable code object directly.

> Could someone please give a rational for the interspersing technique
>   given $anything { when $one {...} when two() {...} when $three {...} }
> where two() returning a true value prevents the case $three. 

That's the other case that ignores $_: not merely returning a "true" value, but 
a Bool (whether that Bool is true or false).  Trying to match a literal True or 
False will warn you, so even if you're surprised by it, you won't be caught 
unaware.  If that's what you really want to do, say ?* or !* (which even 
happens to be shorter than 'True' or 'False'!).

The rationale for ignoring the topic with a non-literal Bool is that "when 3<4" 
will tempt people to read that like "if 3<4".  (Human beings don't interpret 
context rigidly enough for the Principle of Least Surprise to be consistent!)  
Saying "when /foo/" or "when bigger-than(4)" obviously call for something to 
compare against, i.e. the topic, but "3<4" or "foo() < $bar" *look* like 
complete conditionals.  So people will get it wrong occasionally no matter 
which way it's specced.  

However, it's arguably more useful to accept conditions in 'when' without 
comparing the result to $_ because that is surely the more common case.  If you 
really want something like "?$_ == (foo<bar)" then you can say that — since the 
result of that expression is itself boolean, "when ?$_ == (foo<bar)" will do 
the right thing.  (Instead of really doing $_ ~~ (?$_ == (foo<bar)), which is 
almost definitely the wrong thing!)

On the other hand, if you do want to see whether $_ is true or not, you'll 
probably be using "if".  Given/when is particularly useful if you have a long 
list of comparisons, so you don't need to repeat a possibly-long topic every 
time, or even to repeat the "$_ ~~" each time.  But with "if" you only need to 
write the "big-long($something.whatever) ~~" once anyway, and the only other 
possibility is simply "else".  

Now, this does mean that the special case for Bool is best suited for use with 
'when'.  You're unlikely to expect $foo ~~ (3<4) to ignore the LHS, I think, 
simply because if that's what you meant, you would just leave off the "$foo~~". 
 You can't do that with a 'when', which is what makes it a useful shortcut 
there.  Maybe such a case could raise a warning.  Or why not have the special 
case apply only to "when" and not to ~~ in general?  

Anyway, given an example like Aaron's:
   if !$dishes-status && $dishes-status ~~ $trash-status {...}
you would use == instead of ~~, and then there's no problem.  Or more likely, 
write "!$dishes && !$trash" or even "none($dishes, $trash)".  A warning here 
would certainly help catch cases where you want == or === instead of ~~. 

>   given $anything { when $one {...} if two() {...} else { when $three {...} } 
> }
> still smart match $anything with $three? Or is the topic the return value of 
> two() at that point?

No, if two() returns false, then the 'else' is executed with the original 
topic.  (Well, unless two() sneakily changed its parent $_, which, being Perl, 
is possible.)  
Now it seems Rakudo breaks out of the 'else' block when $three is true, but 
then continues with the rest of the 'given' block — I think that's a bug, 
because it ought to break out of the scope that took $_ as a param, i.e. it 
should break directly out of the 'given'.

> I would opt for a regularization of the smart match table. First of all the 
> generic scalar rule should coerce the LHS to the type of the RHS and  then 
> use === on them.

And that's close to what it does, except when the RHS is something 
"indeterminate" like a regex or a range or a type.  The Any-Any case just does 
$_ === $X, which instead could arguably cast $_ to the type of $X first.  That 
might be quite useful, especially if there's a way to tell 'when' to use 
another operator (like plain ===) when you need it.

Apart from that, the Bool cases have been optimised for ordinary use in typical 
given/when blocks (as opposed to staring at the smart-match table and saying, 
Why isn't it more consistent??).  They are more obviously exceptional used 
directly with ~~, but also less likely to occur (although to some extent this 
does rely on having a habit of using ? or == to check boolean conditionals).  
Despite that, it still rubs me the wrong way a little bit.  (I guess it's like 
a picture hanging crooked on the wall — it's not actually hurting anything, but 
once you notice it, it's gonna bug you.)

And there are legitimate, if less common, reasons to expect the more consistent 
behaviour: checking for 'when True' as a catch-all near the end of a series of 
'when's is not unreasonable.  And what I said above about using "if" for Bools 
because there are only two possibilities isn't quite true — since this is Perl, 
there are at least four useful values a Bool can have: true, false, undef, or 
Whatever.  It wouldn't be unnatural to reach for 'when' if you need to 
distinguish those.

I did like "whenever" because it seems to fit well psychologically.  (Your 
psyche may vary.)  But now that I've started thinking of the two issues here 
separately (i.e., breaking out of a block vs a shortcut for $_~~), I'm less 
inclined to make a special case just for "when".  It really would be useful to 
have a $_~~-shortcut that worked with "if", or other places.  My first thought 
was separating the -ever somehow, so that you could say "when ever(3<4)" or "if 
ever (3<4)"; but that means most ordinary 'if's would have to be written 'if 
ever...' which is no good. (Except perhaps for Perl poetry.)

Using "if:" or "when:" with the colon to indicate "instead of evaluating this 
conditional, ~~ it to $_" is concise, but restricted to 'if' or 'when' 
statements.  What if we had a metaoperator that could convert a normal op into 
an implied-topic op, i.e. curry it assuming $_ as the first argument?

I don't have a good name, so for now I'll just call it "_", in reminiscence of 
$_.  So "$_ == $foo" would become "_== $foo", "$_ > bar" would be "_> bar", 
etc.  And just as Z can act as an operator by itself, so plain "_" could stand 
for the default topic-op, ~~.  Hence "$_ ~~ $X" would just be "_$X" (unless you 
changed the default default topic-op to something else.)

I really like the idea of being able to take the topic-shortcut anywhere.  I'm 
less thrilled about the idea of having to say "when _ $blah" all over the 
place....  But it is more consistent and more general.  And it's making me 
think of ".foo", which reminds me of another catch with the current spec: "when 
.foo" means "when $_ ~~ $_.foo" — unless the .foo method happens to return a 
Bool, of course — but it's more likely that it ought to be "when *.foo" or 
"when ?.foo".  So that's a pretty plausible case for catching people out.



-David

Reply via email to