On Sun, Mar 29, 2020, at 5:07 PM, Ilija Tovilo wrote:
> Hi Larry
> 
> Thanks for your suggestion.
> 
> I chose to use switch instead of match for a couple of reasons:
> 
> 1. It uses the same AST, code generation and opcodes as the switch, I
> don't agree that it is significantly different than the switch that we
> already have.
> 2. Adding the `match` keyword is a breaking change, not an insignificant one.
> 3. If we can fix the switch statement semantics in the future the two
> will only differ in that one returns a value and the other one
> doesn't. This is a much smaller distinction than functions/closure.
> 4. If we'd every want to add pattern matching, we'd still have a
> keyword available to us.
> 
> > I don't believe that's the case here, however.  `switch` is a language 
> > construct for a *statement*, which branches the flow of control of the 
> > program.
> >
> > What you're proposing is a language construct for an *expression*, which 
> > evaluates depending on internal logic to a different value.
> 
> Well, I encourage you to look at the implementation. You'll see that
> the two are in fact very similar.
> 
> I'm not averse to using the match keyword if people are ok with the BC
> change. If we do though we definitely should make it work as a
> statement, allow blocks in addition to single expression and also fix
> type coercion. This would make it a replacement of the switch
> statement instead of an addition.
> 
> Ilija

Stas already replied and I agree with his statements, so I won't repeat them.

I will, however, add a bit more:

As Stas said, that the internal implementation is nearly the same is irrelevant 
from a language design perspective; the behavior of the syntax for the user is 
what matters.  Essentially, its "user interface".  That should be the driving 
factor.

Introducing a new keyword in a major release seems entirely within scope.  
We've added keywords in minors before, too, so I'm not too worried.

As to your other points:

* Being strictly typed rather than coercively typed: I'd be fine with this, 
frankly.  There may be an argument to be made that it should respect the 
declare statement, or something else, but making it a new construct puts using 
=== on the table, and I'd be fine with it.

* Work as a statement; allow blocks.

I disagree with these strongly, for very closely related reasons.

1) match/select/whatever it's called is not a statement.  It is an expression.  
It evaluates to something.  That's its value.  if you just want something 
that's a control statement, we already have switch.  That's it's value.

Not everything has to be a statement.  In fact, I've seen arguments before that 
statements in general are a bad idea and everything should be an expression.  
There's a reasonably good language design argument to be made there, but at the 
very least we shouldn't be pretending that statements are intrinsically 
superior.  They're very much not.

The point of statements is to make some change to the state of the system.  The 
point of an expression is to be evaluated into another, simpler value.  match() 
is, specifically, an expression that evaluates to a value.  sticking statements 
inside an expression is just kinda weird, and opens up the door to all kinds of 
side effect behavior.  If you want side effects... you already have switch.

2) The use of a single-expression is, I would argue, a feature, not a defect.

As above, expressions evaluate and you usually do not want them to have side 
effects.  Allowing a block on the right side means:

* It encourages side effect code.
* It makes the construct visually larger, not smaller.  That makes it harder to 
read and understand.
* It discourages refactoring of multiple operations into a single operation, 
viz, a function call.
* You have to have either have a return statement or a magic Rust/Ruby-like 
"last evaluation gets returned" behavior, which is unprecedented in PHP.

I would argue that in the match() use cases, any time you may be tempted to use 
a multi-line expression what you really want is a stand-alone function.  It 
creates a cleaner visual in place, plus encourages breaking up code into 
smaller, named, self-documenting bits.  Those could be named functions, anon 
functions, method calls, etc.

If you have a block of code that ends in a return... it's a function.  We have 
ample ways to define functions already; there's no need to add another one-off 
way.

Viz, if I see this:

match($foo) {
  5 => {
    $thing = getThing($foo);
    $b = $thing->extract($bar);
    return $b * 4;
  };
  6 => 'blah';
}

Then I would argue this is superior in every situation:

match($foo) {
  5 => mapFoo($foo);
  6 => 'blah';
}

function mapFoo($foo, $bar) {
    $thing = getThing($foo);
    $b = $thing->extract($bar);
    return $b * 4;
}

As for pattern matching, if it supports an arbitrary left-side expression (or 
list of expressions) then I'm not clear what else is needed.  Isn't this "close 
enough" to pattern matching that it coves all reasonable use cases:

match(true) {
  $foo < 5 => $someVal;
  $foo < 10 => $otherVal;
  $foo <= 100=> $somethingElse;
  default => $whatever.
}

Switching on a type would be a bit more verbose as you'd need to repeat 
instanceof in each match expression, but I think I'm willing to accept that.  
Now, if we had proper enumerations and the ability to enforce that you 
exhausted the possible values (a la Rust), then I'd be on board with 
internalizing the type match.  Sadly, I don't see that happening in the near 
future, much as I would support it.

--Larry Garfield

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to