Hi Internals,
We've opened the vote on https://wiki.php.net/rfc/multiple-catch
Voting will end on 2015-05-01
--
Regards,
Bronisław Białek.
Den 2016-04-17 kl. 18:51, skrev Bronisław Białek:
Hi Internals,
We've opened the vote on https://wiki.php.net/rfc/multiple-catch
Voting will end on 2015-05-01
Very nice that you added "When is it usefull". Could
also have added when is it not useful, but now we are
in voting...
This feature reminds me about Group Use declarations,
adding syntax sugar to improve code quality and make
code updates less error prone.
Regards //Björn Larsson
PS Typo in "usefull", think it's only one l.
I voted yes.
It's really not that common that I have to do stuff in exception
handling, when I do need to do some clearing up, or checking to see if
the operation that threw the exception needs to be re-tried, having to
repeat that code in all of the catch statements that need it, is just
nasty duplication.
Although it's a small change, having this is a very nice small
improvement that doesn't break anyone's existing code.
Thanks for putting it forward.
cheers
Dan
Hi Internals,
We've opened the vote on https://wiki.php.net/rfc/multiple-catch
Voting will end on 2015-05-01--
Regards,
Bronisław Białek.
Heya,
I voted "NO" due to previous discussion. TL;DR: this is FAR off the 80/20
use-case for a language syntax change.
C&P from my answer elsewhere:
The following typical example is something REALLY rare, and requiring a
parser change for it seems excessive:
try {
// ...
} catch (InvalidArgumentException $e) {
// same handling
} catch (PDOException $e) {
// same handling
} catch (BadMethodCallException $e) {
// same handling
}
These 3 exceptions usually result in separate handling anyway. If same
handling is needed, you can as usual extract a private method (if you are
in a class) and deal with it there:
try {
// ...
} catch (InvalidArgumentException $e) {
$this->sameHandling($e);
} catch (PDOException $e) {
$this->sameHandling($e);
} catch (BadMethodCallException $e) {
$this->sameHandling($e);
}
private function sameHandling(Throwable $caught)
{
// same handling
}
Still, even in this case, I'd flag it up in a code review, as same handling
for 3 different exception types generally (not always) means something is
really wrong.
So we are building a feature for a 1% case that, while simple to implement
at parser-level in PHP, requires changes in all userland libraries that do
parsing or rely on the AST.
Cheers,
Marco Pivetta
I voted yes.
It's really not that common that I have to do stuff in exception
handling, when I do need to do some clearing up, or checking to see if
the operation that threw the exception needs to be re-tried, having to
repeat that code in all of the catch statements that need it, is just
nasty duplication.Although it's a small change, having this is a very nice small
improvement that doesn't break anyone's existing code.Thanks for putting it forward.
cheers
DanHi Internals,
We've opened the vote on https://wiki.php.net/rfc/multiple-catch
Voting will end on 2015-05-01--
Regards,
Bronisław Białek.
-----Original Message-----
From: Marco Pivetta [mailto:ocramius@gmail.com]
Sent: Monday, April 18, 2016 4:41 PM
To: Dan Ackroyd danack@basereality.com
Cc: Bronisław Białek after89@gmail.com; PHP internals
internals@lists.php.net
Subject: Re: [PHP-DEV] [VOTE] Catching Multiple Exception TypesHeya,
I voted "NO" due to previous discussion. TL;DR: this is FAR off the 80/20 use-
case for a language syntax change.C&P from my answer elsewhere:
The following typical example is something REALLY rare, and requiring a
parser change for it seems excessive:try { // ... } catch (InvalidArgumentException $e) { // same handling } catch (PDOException $e) { // same handling } catch (BadMethodCallException $e) { // same handling }
These 3 exceptions usually result in separate handling anyway. If same
handling is needed, you can as usual extract a private method (if you are in a
class) and deal with it there:try { // ... } catch (InvalidArgumentException $e) { $this->sameHandling($e); } catch (PDOException $e) { $this->sameHandling($e); } catch (BadMethodCallException $e) { $this->sameHandling($e); } private function sameHandling(Throwable $caught) { // same handling }
Still, even in this case, I'd flag it up in a code review, as same handling for 3
different exception types generally (not always) means something is really
wrong.
I voted 'yes', as I think that exception handling is one of the few cases where it would make sense to have similar (or completely identical) handling for very different object types. Emitting a meaningful error and cleaning up are likely to look very similar for potentially quite different exception types.
The main thing that made me question whether it's truly necessary, is that the same can be said about catch (Exception $e), which could handle all exception types in a generic way. But I think it is not uncommon to have a situation where you'd want to group a certain number of known exception types in one group, and either propagate other exceptions or handle them differently.
I do find this one quite harmless in terms of potential negative implications - so even if it's not exceptionally useful (no pun intended) - it's fairly intuitive, simple in implementation and doesn't have to deal with scalars. The generic union types RFC has much farther reaching implications in terms of influencing how code is likely to get written in the future, and IMHO not for the better.
Zeev
Heya,
I voted "NO" due to previous discussion. TL;DR: this is FAR off the 80/20
use-case for a language syntax change.
I'm with you on this one.
C&P from my answer elsewhere:
The following typical example is something REALLY rare, and requiring a
parser change for it seems excessive:try { // ... } catch (InvalidArgumentException $e) { // same handling } catch (PDOException $e) { // same handling } catch (BadMethodCallException $e) { // same handling }
These 3 exceptions usually result in separate handling anyway. If same
handling is needed, you can as usual extract a private method (if you are
in a class) and deal with it there:try { // ... } catch (InvalidArgumentException $e) { $this->sameHandling($e); } catch (PDOException $e) { $this->sameHandling($e); } catch (BadMethodCallException $e) { $this->sameHandling($e); } private function sameHandling(Throwable $caught) { // same handling }
Even this is a scary way to do it. As you don't know which exception
class you have, how would you know how to handle each separately. You'd
have to if ($caught instanceof InvalidArgumentException) etc to be sure
that what you're doing with the object is allowed.
If they are user defined exceptions, you should make them implement an
interface, and do a "catch (IMyException $e)" to be sure you can handle
the different exceptions in a consistent manor. And that already works.
If they are exception classes defined by other libraries, then, as you
say, it's very likely they should be handled differenty anyway. Or
rather, very generically, which you can do with "catch (Exception $e)".
cheers,
Derick
--
http://derickrethans.nl | http://xdebug.org
Like Xdebug? Consider a donation: http://xdebug.org/donate.php
twitter: @derickr and @xdebug
Posted with an email client that doesn't mangle email: alpine
If they are user defined exceptions, you should make them implement an
interface,
Even within one oranisation, getting different teams to agree to
common interfaces is not always possible. But when the exceptions are
originating in code that people don't have any control over (e.g.
anything open source) then changing the exceptions to match your
particular interface requirements is a completely impossible.
For example will you and Marco start taking requests to add particular
interfaces to any exceptions thrown by the code in the libraries you
maintain?
Derick wrote:
As you don't know which exception
class you have, how would you know how to handle each separately.
Why do you think you need to know how to handle each separately? If
you ever need to do something different based on the type of
exception, you probably wouldn't be using the multiple-catch. The
multiple-catch is almost only ever going to be used when you have a
list of exceptions where you're going to be doing the same thing for
each. e.g. for retrying an operation:
$success = false;
while ($attempts < 4) {
try {
foo();
$success = true;
}
catch (NetworkException | DBDeadLockException $e) {
// Save exceptions to add to surpressed exception
// when that RFC is hopefully passed.
$attempts++;
}
// All other
}
//Check for success or run out of attempts here.
For NetworkException and DBDeadLockException we're always going to do
the same thing; retry the operation a couple of times. For any
exception type where we would want to do something different......we'd
just use a separate catch block.
cheers
Dan
Hi!
Even this is a scary way to do it. As you don't know which exception
class you have, how would you know how to handle each separately. You'd
have to if ($caught instanceof InvalidArgumentException) etc to be sure
that what you're doing with the object is allowed.
I just recently had a case where I used something similar in Java code,
so I can tell when it's useful:
- You call some complex object that can fail for a lot of reasons (like
HTTP client talking to remote service) - It can fail with a number of exceptions because something went bad -
i.e. network is bad, DNS is bad, server is down, service is too busy,
your request syntax is wrong, request is correct but you're not allowed
to execute it, etc. - These causes can be roughly separated into two groups - requests that
we can and should repeat - e.g. "server too busy" situation, and those
that we should not repeat, e.g. "bad syntax" situation - Furthermore, failures can be "soft" - fail this request but continue
next ones in the batch, or "hard" - drop everything and call for help
because something is seriously broken.
In this situation, grouping exceptions - which, due to being from
different layers of the complex object do not follow any single
structure - becomes very convenient. Of course it is possible to write a
long tree of copy-paste handlers for each of the three cases, but it's
annoying. Much cleaner to have just three branches. And of course I
don't want to do something like catch(Exception) because shudder...
say, it's very likely they should be handled differenty anyway. Or
rather, very generically, which you can do with "catch (Exception $e)".
catch(Exception) is practically never a good idea, unless you really are
able to handle every exception. Now in PHP that includes parse errors,
type errors, etc. I don't think any random code - except for a very
generic final error handler - would ever want to do that.
Stas Malyshev
smalyshev@gmail.com
-----Original Message-----
From: Derick Rethans [mailto:derick@php.net]
Sent: Monday, April 18, 2016 6:18 PM
To: Marco Pivetta ocramius@gmail.com
Cc: Dan Ackroyd danack@basereality.com; Bronisław Białek
after89@gmail.com; PHP internals internals@lists.php.net
Subject: Re: [PHP-DEV] [VOTE] Catching Multiple Exception TypesHeya,
I voted "NO" due to previous discussion. TL;DR: this is FAR off the
80/20 use-case for a language syntax change.I'm with you on this one.
C&P from my answer elsewhere:
The following typical example is something REALLY rare, and requiring
a parser change for it seems excessive:try { // ... } catch (InvalidArgumentException $e) { // same handling } catch (PDOException $e) { // same handling } catch (BadMethodCallException $e) { // same handling }
These 3 exceptions usually result in separate handling anyway. If same
handling is needed, you can as usual extract a private method (if you
are in a class) and deal with it there:try { // ... } catch (InvalidArgumentException $e) { $this->sameHandling($e); } catch (PDOException $e) { $this->sameHandling($e); } catch (BadMethodCallException $e) { $this->sameHandling($e); } private function sameHandling(Throwable $caught) { // same handling }
Even this is a scary way to do it. As you don't know which exception class you
have, how would you know how to handle each separately.
In reality, you often don't truly 'handle' exceptions at all. Different exception types are very often just a slightly fancier and easier way to return errors, and it's actually very common and perfectly reasonable to handle groups of exceptions in an identical way. It's also extremely common to see exception handlers that don't pay any attention to the exception object itself. In fact, glancing through the Zend Framework 2 codebase and the Drupal 8 codebase, it seems that the exception object is rarely ever used.
In Drupal 8, many catch blocks simply resort to return FALSE
or TRUE. For example:
try {
return $router->match($path);
}
catch (ResourceNotFoundException $e) {
return FALSE;
}
catch (ParamNotConvertedException $e) {
return FALSE;
}
catch (AccessDeniedHttpException $e) {
return FALSE;
}
catch (MethodNotAllowedException $e) {
return FALSE;
}
}
Note that this is in fact different from catch (Exception $e), and I'm guessing the author intentionally did not want to catch an exception the type of which she/he weren't expecting to get - and instead, have it propagate upwards.
With this RFC approved, it would become:
try {
return $router->match($path);
}
catch (ResourceNotFoundException|ParamNotConvertedException|AccessDeniedHttpException|MethodNotAllowedException $e) {
return FALSE;
}
Which is much, much nicer and readable. And I found this in the ZF2 codebase:
} catch (DiRuntimeException $e) {
if ($methodRequirementType & self::RESOLVE_STRICT) {
//finally ( be aware to do at the end of flow)
array_pop($this->currentDependencies);
if (isset($alias)) {
array_pop($this->currentAliasDependenencies);
}
// if this item was marked strict,
// plus it cannot be resolve, and no value exist, bail out
throw new Exception\MissingPropertyException(
sprintf(
'Missing %s for parameter ' . $name . ' for ' . $class . '::' . $method,
(($value[0] === null) ? 'value' : 'instance/object')
),
$e->getCode(),
$e
);
} else {
//finally ( be aware to do at the end of flow)
array_pop($this->currentDependencies);
if (isset($alias)) {
array_pop($this->currentAliasDependenencies);
}
return false;
}
} catch (ServiceManagerException $e) {
// Zend\ServiceManager\Exception\ServiceNotCreatedException
if ($methodRequirementType & self::RESOLVE_STRICT) {
//finally ( be aware to do at the end of flow)
array_pop($this->currentDependencies);
if (isset($alias)) {
array_pop($this->currentAliasDependenencies);
}
// if this item was marked strict,
// plus it cannot be resolve, and no value exist, bail out
throw new Exception\MissingPropertyException(
sprintf(
'Missing %s for parameter ' . $name . ' for ' . $class . '::' . $method,
(($value[0] === null) ? 'value' : 'instance/object')
),
$e->getCode(),
$e
);
} else {
//finally ( be aware to do at the end of flow)
array_pop($this->currentDependencies);
if (isset($alias)) {
array_pop($this->currentAliasDependenencies);
}
return false;
}
}
To save everyone some time, the two code blocks for the two exception types are completely identical. I'm not sure why it wasn't function()alized, but regardless, it's another case where the new proposed feature would make perfect sense.
I think saying that identical handling for multiple exception types is bad or is grounds for an urgent code review is fairly disconnected from reality in many if not most cases.
Zeev
Zeev,
Le mar. 19 avr. 2016 à 11:56, Zeev Suraski zeev@zend.com a écrit :
In Drupal 8, many catch blocks simply resort to return
FALSE
or TRUE. For
example:try { return $router->match($path); } catch (ResourceNotFoundException $e) { return FALSE; } catch (ParamNotConvertedException $e) { return FALSE; } catch (AccessDeniedHttpException $e) { return FALSE; } catch (MethodNotAllowedException $e) { return FALSE; }
}
This looks much more like Exceptions used for not "Exceptional" cases:
where Exceptions are preferred over a clean API and avoiding at all price
if-conditions.
One of the very reason I voted "no".
It encourages people to replace any kind of condition with an exception,
leading to less readable code and incomplete API.
try { return $router->match($path); } catch
(ResourceNotFoundException|ParamNotConvertedException|AccessDeniedHttpException|MethodNotAllowedException
$e) {
return FALSE;
}
The "return FALSE" here on all those cases is just another way to ease
silencing Exceptions which is often a bad idea (like @).
It's also perfectly possible to make all those Exceptions implement a
"MatchingFailedException" interface (even empty). This would even make the
collection reusable AND extensible because new user Exception classes could
implement that "MatchingFailedException" interface without the need to
refactor code.
Which is much, much nicer and readable. And I found this in the ZF2
codebase:
} catch (DiRuntimeException $e) { if ($methodRequirementType & self::RESOLVE_STRICT) { //finally ( be aware to do at the end of flow) array_pop($this->currentDependencies); if (isset($alias)) { array_pop($this->currentAliasDependenencies); } // if this item was marked strict, // plus it cannot be resolve, and no value exist,
bail out
throw new Exception\MissingPropertyException(
sprintf(
'Missing %s for parameter ' . $name . '
for ' . $class . '::' . $method,
(($value[0] === null) ? 'value' :
'instance/object')
),
$e->getCode(),
$e
);
} else {
//finally ( be aware to do at the end of flow)
array_pop($this->currentDependencies);
if (isset($alias)) {
array_pop($this->currentAliasDependenencies);
}
return false;
}
} catch (ServiceManagerException $e) {
//
Zend\ServiceManager\Exception\ServiceNotCreatedException
if ($methodRequirementType & self::RESOLVE_STRICT) {
//finally ( be aware to do at the end of flow)
array_pop($this->currentDependencies);
if (isset($alias)) {
array_pop($this->currentAliasDependenencies);
}
// if this item was marked strict,
// plus it cannot be resolve, and no value exist,
bail out
throw new Exception\MissingPropertyException(
sprintf(
'Missing %s for parameter ' . $name . '
for ' . $class . '::' . $method,
(($value[0] === null) ? 'value' :
'instance/object')
),
$e->getCode(),
$e
);
} else {
//finally ( be aware to do at the end of flow)
array_pop($this->currentDependencies);
if (isset($alias)) {
array_pop($this->currentAliasDependenencies);
}
return false;
}
}To save everyone some time, the two code blocks for the two exception
types are completely identical. I'm not sure why it wasn't
function()alized, but regardless, it's another case where the new proposed
feature would make perfect sense.I think saying that identical handling for multiple exception types is bad
or is grounds for an urgent code review is fairly disconnected from reality
in many if not most cases.Zeev
--
Patrick
It's also perfectly possible to make all those Exceptions implement a
"MatchingFailedException" interface (even empty).
Apologies for continuing the conversation, even after the RFC is
incredibly likely to pass, but; I don't understand that argument at
all, and think I disagree with the philosophy behind it.
If it was followed, it would mean that exception classes would need to
be aware of how they are being used. When exception classes are
defined in a library, and the exceptions are being caught in an
applicaton, wouldn't adding applicaton specific interfaces to the
libaries exception classes just violate the separation between the
two? It doesn't seem to be good practice.
It would also lead to multiple interfaces being added to each
exception class. e.g. if in the application we had these catches in
different places in the application:
try {
return foo();
} catch (ResourceNotFoundException $e) {
return FALSE;
} catch (ParamNotConvertedException $e) {
return FALSE;
}
try {
return bar();
} catch (ResourceNotFoundException $e) {
return FALSE;
}
catch (AccessDeniedHttpException $e) {
return FALSE;
}
try {
return quux();
} catch (ResourceNotFoundException $e) {
return FALSE;
} catch (MethodNotAllowedException $e) {
return FALSE;
}
Where we specifically want to catch ResourceNotFoundException and one
other exception. If we followed your advice and added a specific
interface to the exceptions for each of the separate combinations, so
that we could catch just the interface we would end up with this:
class ResourceNotFoundException extends \Exception implements
FooExInterface, BarExInterface, QuuxExInterface {}
i.e. having to add one interface per usage. That doesn't seem that
great either....and again is altering a class based on how it's going
to be used.
It encourages people to replace any kind of condition with an exception,
leading to less readable code and incomplete API.
I don't think this is the right attitude to have when designing a
progrmming language.
Any feature that enables developers to write code that covers edge
cases in their application is by it's very nature going to be possibly
to use in a way most people consider inappropriate.
It's much more productive to focus on making code/features that are
useful when used sensibly. Worrying that people might use it in a way
that you wouldn't recommend using it, is fundamentally the wrong
problem to try to address when designing a language.
I think that's true in general but particularly true in a language
like PHP where one application might be programmed in a very different
way to another application. What would be useful and appropriate for
one of them would be could considered a horrendous hack* in another
application.
If we are limiting ourselves to adding features that are correct and
appropriate to use in all applications, that is limiting ourselves to
a subset of the features that people would want to have to cover all
of their use-cases.
cheers
Dan
Ack
- For example, a lot of people strongly dislike annotations. A
significant number of people use annotations in their applications.
Neither groups is 'right' or 'wrong', but it would be wrong for the
people who don't like annotations to say that annotations should never
be used.
Le ven. 22 avr. 2016 à 00:10, Dan Ackroyd danack@basereality.com a écrit :
It's also perfectly possible to make all those Exceptions implement a
"MatchingFailedException" interface (even empty).Apologies for continuing the conversation, even after the RFC is
incredibly likely to pass, but; I don't understand that argument at
all, and think I disagree with the philosophy behind it.If it was followed, it would mean that exception classes would need to
be aware of how they are being used. When exception classes are
defined in a library, and the exceptions are being caught in an
applicaton, wouldn't adding applicaton specific interfaces to the
libaries exception classes just violate the separation between the
two? It doesn't seem to be good practice.It would also lead to multiple interfaces being added to each
exception class. e.g. if in the application we had these catches in
different places in the application:try {
return foo();
} catch (ResourceNotFoundException $e) {
return FALSE;
} catch (ParamNotConvertedException $e) {
return FALSE;
}try {
return bar();
} catch (ResourceNotFoundException $e) {
return FALSE;
}
catch (AccessDeniedHttpException $e) {
return FALSE;
}try {
return quux();
} catch (ResourceNotFoundException $e) {
return FALSE;
} catch (MethodNotAllowedException $e) {
return FALSE;
}Where we specifically want to catch ResourceNotFoundException and one
other exception. If we followed your advice and added a specific
interface to the exceptions for each of the separate combinations, so
that we could catch just the interface we would end up with this:class ResourceNotFoundException extends \Exception implements
FooExInterface, BarExInterface, QuuxExInterface {}i.e. having to add one interface per usage. That doesn't seem that
great either....and again is altering a class based on how it's going
to be used.
Your are absolutely right. I didn't meant one to modify libraries' or
someone else's code, just the part you are in control of and that one can
rely on Exception interfaces rather than always use an Exception
hierarchy solely based on inheritance. Transforming Exceptions is also
rather common and doesn't require modifying external code.
It encourages people to replace any kind of condition with an exception,
leading to less readable code and incomplete API.I don't think this is the right attitude to have when designing a
progrmming language.Any feature that enables developers to write code that covers edge
cases in their application is by it's very nature going to be possibly
to use in a way most people consider inappropriate.
Sorry if someone found my attitude inappropriate or offensive. I just
wanted to bring the attention that most of the Exception examples mentioned
in this thread seems like wrong usage for Exceptions.
There is just nothing more repeatable or common cases than "resource not
found", "method not allowed", "access denied",... They are all non
exceptional cases better handled without exceptions and with clean APIs
reflecting the business logic of the application.
e.g.:
if (!$actionAccessValidator->check($user, $resource, $action)) {
return false;
}
$action->run($resource);
return true;
Should be preferred over:
try {
// $action->run() combines here both access checking + the actual logic
$action->run($resource, $user);
}
catch (AccessDeniedException $e) {
return false;
}
return true;
I'm not going to mention the advantage of decoupled code.
My vote was mostly motivated by the absence of examples showing code that
is well written and designed which can benefit ot this new language
construct.
It's much more productive to focus on making code/features that are
useful when used sensibly. Worrying that people might use it in a way
that you wouldn't recommend using it, is fundamentally the wrong
problem to try to address when designing a language.
Yes, but my worries are that it can only or mostly be used in cases where
the issues relies elsewhere, I would prefer to avoid giving a signal to
developers that make them comfortable using bad design practices.
Cheers,
Patrick
Patrick ALLAERT wrote on 24/04/2016 14:18:
There is just nothing more repeatable or common cases than "resource not
found", "method not allowed", "access denied",... They are all non
exceptional cases better handled without exceptions and with clean APIs
reflecting the business logic of the application.
I think part of the problem may be that non-trivial examples require
rather a lot of context. An example that comes to my mind is if an
application is combining a bunch of different libraries, and calling
them all in sequence:
try
{
$fooService->doStuff($data);
$barService->doMoreStuff($data);
$bazService->doDifferentStuff($data);
}
catch ( FooException | BarException | BazException $e )
{
// We don't care which library threw the exception, the action is always
the same
throw new AppException($e); // Hide which libraries we're using to
implement the function
}
Regards,
Rowan Collins
[IMSoP]
Den 2016-04-25 kl. 11:49, skrev Rowan Collins:
Patrick ALLAERT wrote on 24/04/2016 14:18:
There is just nothing more repeatable or common cases than "resource not
found", "method not allowed", "access denied",... They are all non
exceptional cases better handled without exceptions and with clean APIs
reflecting the business logic of the application.I think part of the problem may be that non-trivial examples require
rather a lot of context. An example that comes to my mind is if an
application is combining a bunch of different libraries, and calling
them all in sequence:try
{
$fooService->doStuff($data);
$barService->doMoreStuff($data);
$bazService->doDifferentStuff($data);
}
catch ( FooException | BarException | BazException $e )
{
// We don't care which library threw the exception, the action is
always the same
throw new AppException($e); // Hide which libraries we're using to
implement the function
}Regards,
Good example, thats how I would use multi-catch.
Regards //Björn Larsson
I voted "NO" due to previous discussion. TL;DR: this is FAR off the 80/20
use-case for a language syntax change....
These 3 exceptions usually result in separate handling anyway. If same
handling is needed, you can as usual extract a private method (if you are
in a class) and deal with it there:...
Still, even in this case, I'd flag it up in a code review, as same handling
for 3 different exception types generally (not always) means something is
really wrong.So we are building a feature for a 1% case that, while simple to implement
at parser-level in PHP, requires changes in all userland libraries that do
parsing or rely on the AST.
I agree: the times this need arises are relatively rare, and in those cases
functional encapsulation cleanly handles the repetition. I would vote no,
were it not for one thing: union types
https://wiki.php.net/rfc/union_types.
catch constitutes a formal signature, exactly like a function signature. In
my mind, this proposal and the union types proposal are intrinsically tied.
If we have one, we must have the other. If not, we're inducing a sadness.
Since I do see the need for union types, and since I believe the two must
be accepted or rejected together, I voted yes.
Hi!
catch constitutes a formal signature, exactly like a function signature. In
my mind, this proposal and the union types proposal are intrinsically tied.
If we have one, we must have the other. If not, we're inducing a sadness.
This has nothing to do with union types. It is just a shortcut for
repeated catch() clauses, it does not introduce anything into type
system, capture existing use case, and doesn't make anything more
complicated. While union types introduce a lot of new things, make
everything dealing with types more complicated and the need for them,
unless we import a real lot of other concepts like type pattern matching
is quite unclear. So I don't think this has much in common.
Stas Malyshev
smalyshev@gmail.com
On Mon, Apr 18, 2016 at 8:54 PM, Stanislav Malyshev smalyshev@gmail.com
wrote:
Hi!
catch constitutes a formal signature, exactly like a function signature.
In
my mind, this proposal and the union types proposal are intrinsically
tied.
If we have one, we must have the other. If not, we're inducing a sadness.This has nothing to do with union types. It is just a shortcut for
repeated catch() clauses, it does not introduce anything into type
system, capture existing use case, and doesn't make anything more
complicated. While union types introduce a lot of new things, make
everything dealing with types more complicated and the need for them,
unless we import a real lot of other concepts like type pattern matching
is quite unclear. So I don't think this has much in common.
It's about the perception of consistency. "Oh, I can do this! Neat:"
function neat(Foo | Bar $a) { ... }
"But I can't do this? WTF?"
catch (FooException | BarException $ex) { ... }
And vice-versa. The perception revolves around the fact that both appear to
be signatured, regardless of how they're implemented in the engine.
Hi!
It's about the perception of consistency. "Oh, I can do this! Neat:"
function neat(Foo | Bar $a) { ... }
You shouldn't be able to do this, because it makes no sense - why would
a function accept two random types and only them? That's probably a bad
design - it should be one type or two functions.
"But I can't do this? WTF?"
catch (FooException | BarException $ex) { ... }
But this makes total sense - you routinely catch more than one type of
exceptions, just not you write it catch(Foo) ... catch(Bar...). You very
rarely have functions that do exactly the same with two types, and only
those. The usage is different.
I do not think approach "it should look the same, no matter what the
usage is" is right.
And vice-versa. The perception revolves around the fact that both appear
to be signatured, regardless of how they're implemented in the engine.
But that's not what they do. Function says "I accept only parameter of
this type, and it's an error if it's not". Catch says "I will process
exception of this type, but if it's not, other catch clause may process
it". You don't chain functions like that, but you do chain catch
clauses. Again, different usage.
Stas Malyshev
smalyshev@gmail.com
Hi!
It's about the perception of consistency. "Oh, I can do this! Neat:"
function neat(Foo | Bar $a) { ... }
You shouldn't be able to do this, because it makes no sense - why would
a function accept two random types and only them? That's probably a bad
design - it should be one type or two functions.
I detailed use cases for both & and | here:
http://news.php.net/php.internals/92335
Admittedly Foo|Bar is of limited use, although there is some use.
Foo&Bar, however, has plenty of uses.
I do agree with the sentiment that having Foo|Bar for catch but not for
function signatures is going to confuse people. If nothing else, it
will result in even more pressure to add union types.
--Larry Garfield
Hi!
Admittedly Foo|Bar is of limited use, although there is some use.
Foo&Bar, however, has plenty of uses.
That is a different topic, not related to exceptions at all.
I do agree with the sentiment that having Foo|Bar for catch but not for
function signatures is going to confuse people.
Confuse which people? Do people now are confused that you can write
multiple catch clauses for same exception but can't write multiple
function bodies for same parameter set? Are they confused by the fact
that it's a fatal error to pass wrong type to a function but catch just
silently ignores it? Are they confused that you can write
function(string $a) but you can't catch(string $a)? I don't think so. I
think people are pretty good at figuring out exceptions and function
parameters are different things. Java has multiple catch and no
expression types, and .net has the same after a fashion (with somewhat
weird syntax) but programmers on neither wonder around confused
expecting to find type calculus somewhere.
Stas Malyshev
smalyshev@gmail.com
-----Original Message-----
From: Stanislav Malyshev [mailto:smalyshev@gmail.com]
Sent: Tuesday, April 19, 2016 8:11 AM
To: Larry Garfield larry@garfieldtech.com; internals@lists.php.net
Subject: Re: [PHP-DEV] [VOTE] Catching Multiple Exception TypesI do agree with the sentiment that having Foo|Bar for catch but not
for function signatures is going to confuse people.Confuse which people? Do people now are confused that you can write
multiple catch clauses for same exception but can't write multiple function
bodies for same parameter set? Are they confused by the fact that it's a fatal
error to pass wrong type to a function but catch just silently ignores it? Are
they confused that you can write function(string $a) but you can't catch(string
$a)? I don't think so. I think people are pretty good at figuring out exceptions
and function parameters are different things. Java has multiple catch and no
expression types, and .net has the same after a fashion (with somewhat weird
syntax) but programmers on neither wonder around confused expecting to
find type calculus somewhere.
I agree. But I think that if people are going to be confused and will pressure for consistency with functions, it's an exceptionally easy (again, no pun intended) to explain why they are in fact inherently different and why it makes sense here and doesn't make sense there.
Zeev
On Mon, Apr 18, 2016 at 9:28 PM, Stanislav Malyshev smalyshev@gmail.com
wrote:
Hi!
It's about the perception of consistency. "Oh, I can do this! Neat:"
function neat(Foo | Bar $a) { ... }
You shouldn't be able to do this, because it makes no sense - why would
a function accept two random types and only them? That's probably a bad
design - it should be one type or two functions."But I can't do this? WTF?"
catch (FooException | BarException $ex) { ... }
But this makes total sense - you routinely catch more than one type of
exceptions, just not you write it catch(Foo) ... catch(Bar...). You very
rarely have functions that do exactly the same with two types, and only
those. The usage is different.I do not think approach "it should look the same, no matter what the
usage is" is right.And vice-versa. The perception revolves around the fact that both appear
to be signatured, regardless of how they're implemented in the engine.But that's not what they do. Function says "I accept only parameter of
this type, and it's an error if it's not". Catch says "I will process
exception of this type, but if it's not, other catch clause may process
it". You don't chain functions like that, but you do chain catch
clauses. Again, different usage.
// concrete example
// My application uses two implementation of collection pattern:
// one from eloquent and one from haldayne. I have a log method that
// logs value of either collection implementation and catches the same
// kind of invalid collection regardless of implementation
//
// in this example, both union types and multi-catch are available
namespace Eloquent;
class Collection implements Contract\Arrayable {
public static function toArray() {
if (! (is_array($this->data) || is_object($this->data))) {
throw new Exception\UnexpectedValue;
}
// ....
}
}
namespace Haldayne;
class Map implements ToArray {
public static function toArray() {
if (! (is_array($this->data) || is_object($this->data))) {
throw new \UnexpectedValueException;
}
// ....
}
}
namespace Application;
function log(Eloquent\Collection | Haldayne\Map $entity) {
$logger->log($entity->toArray());
}
try {
log(Config::find());
} catch (Eloquent\Exception\UnexpectedValue | \UnexpectedValueException
$ex) {
die('Configuration is neither array nor object');
}
There is a pleasant symmetry in all of this. As a developer, I handle both
library's concrete collection implementations similarly for both happy and
unhappy paths, all using a compact expression (Class1 | Class2). The engine
helps me out here.
Now, imagine multi-catch passes (as I expect), but union types does not. I
would change my log function to this:
function log($entity) {
if ($entity instanceof Eloquent\Collection | $entity instanceof
Haldayne\Map) {
$logger->log($entity->toArray());
} else {
throw new \InvalidArgumentException;
}
}
"Ugh, this is much easier in multi-catch. If only functions took multiple
types, this would be much simpler to write."
Now imagine union types pass, but multi-catch doesn't. Then I'd change my
catch:
try {
log(Config::find());
} catch (Eloquent\Exception\UnexpectedValue $ex) {
die('Configuration is neither array nor object');
} catch (\UnexpectedValueException $ex) {
die('Configuration is neither array nor object');
}
"ugh, this is much easier in function types. If only I could catch multiple
exception types in one block."
So when I'm talking about "user confusion", I'm referring to a perception
of inconsistency by userland developers. And, I think we'd be well-wise to
avoid designing inconsistencies. So even though I consider both union types
and multi-catch to be on the rare side of utility, I consider having one
and not the other a worse situation than having neither.
Hi!
namespace Application;
function log(Eloquent\Collection | Haldayne\Map $entity) {
$logger->log($entity->toArray());
}
This is bad design. It assumes that two completely unrelated classes
have the same function and it means the same. If it's indeed true, we
have a way to express it. It's called "interface". If it's not true, and
you are just duck-typing, you should not pretend you use strict types,
and drop types completely. You shouldn't try yo sit on both chairs at
the same time and pretend you're using strict typing while in fact
duck-typing.
"Ugh, this is much easier in multi-catch. If only functions took
multiple types, this would be much simpler to write."
And it would be completely wrong, abuse of typing. If you mean to say
"anything that has log method", you should either have interface or just
get rid of the strictness and duck-type it and rely on PHP to throw
exception if anything happens. Otherwise it is just a sloppy design.
--
Stas Malyshev
smalyshev@gmail.com
On Tue, Apr 19, 2016 at 2:13 PM, Stanislav Malyshev smalyshev@gmail.com
wrote:
Hi!
namespace Application;
function log(Eloquent\Collection | Haldayne\Map $entity) {
$logger->log($entity->toArray());
}This is bad design. It assumes that two completely unrelated classes
have the same function and it means the same.
Both of these exist in the wild, and both have identical function wrt
toArray: reducing a set of collection-like things to an array of arrays.
If it's indeed true, we
have a way to express it. It's called "interface".
Except these two are in separate libraries that cannot share an interface.
"Ugh, this is much easier in multi-catch. If only functions took
multiple types, this would be much simpler to write."
And it would be completely wrong, abuse of typing. If you mean to say
"anything that has [toArray] method", you should either have interface or
just get rid of the strictness and duck-type it and rely on PHP to throw
exception if anything happens. Otherwise it is just a sloppy design.
An interface is not possible: separate libraries. Duck-typing is not
appropriate: only these two specific classes are supported.
Union types solve certain problems better than equivalent run-time checks.
Multi-catch solves certain other problems better than equivalent
many-catch. They are orthogonal, but the syntax is similar: "into this
block, accept this type or this type or ...."
I contend that this apparent similarity must be consistent, lest we earn
another sadness.
Hi!
Except these two are in separate libraries that cannot share an interface.
If these are separate libraries, you can't be sure that method does the
same thing, or even that it will be existing in both.
An interface is not possible: separate libraries. Duck-typing is not
appropriate: only these two specific classes are supported.
This looks very strange and brittle design - two random classes from two
unrelated libraries, which you rely on have exactly the same
functionality and keep it that way. Not a robust design if you ask me.
Also, it looks strange why only those two random classes can be
converted to arrays but none others can. What if you had third class
that also can be converted to array? What if you had 20 of them? Again,
looks like brittle and ad-hoc design.
--
Stas Malyshev
smalyshev@gmail.com
Hi!
Except these two are in separate libraries that cannot share an interface.
If these are separate libraries, you can't be sure that method does the
same thing, or even that it will be existing in both.An interface is not possible: separate libraries. Duck-typing is not
appropriate: only these two specific classes are supported.This looks very strange and brittle design - two random classes from two
unrelated libraries, which you rely on have exactly the same
functionality and keep it that way. Not a robust design if you ask me.
Also, it looks strange why only those two random classes can be
converted to arrays but none others can. What if you had third class
that also can be converted to array? What if you had 20 of them? Again,
looks like brittle and ad-hoc design.
Stanislav is completely right here, this is exactly one of the usages of
union types that is completely wrong. You had this example:
namespace Application;
function log(Eloquent\Collection | Haldayne\Map $entity) {
$logger->log($entity->toArray());
}
try {
log(Config::find());
} catch (Eloquent\Exception\UnexpectedValue | \UnexpectedValueException
$ex) {
die('Configuration is neither array nor object');
}
The problem with this is that you now require both libraries to be
installed at the same time because otherwise the union type cannot be
verified by PHP's type system. This will bite you in the ass in no time.
Just think of your composer:
"require": {
"eloquent/collection": "",
"haldayne/map": ""
}
Both do the same?!? Better rely on duck typing in such cases.
function log($entity) {
if (method_exists($entity, 'toArray')) {
$logger->log($entity->toArray());
}
else {
// handle is in another way.
}
}
If you really want to ensure that the method is not being called by a
dev in that way go for assertions.
function log($entity) {
assert('method_exists($entity, "toArray")', 'entity must implement
toArray
method');
// Check in production too to avoid fatal errors?
if (method_exists($entity, 'toArray')) {
$logger->log($entity->toArray());
}
}
Union types only have a few special use cases but those are worth
exploring imho.
function div(int|float $x, int|float $y): int|float {
if (is_float($x) || is_float($y)) {
return $x / $y;
}
else {
return intdiv($x, $y);
}
}
function even(int|string $x): bool {
if (is_int($x)) {
return $x % 2 === 0;
}
else {
return gmp_mod($x, 2) == 0;
}
}
--
Richard "Fleshgrinder" Fussenegger
Except these two are in separate libraries that cannot share an interface.
If these are separate libraries, you can't be sure that method does the
same thing, or even that it will be existing in both.
This is not true. I can read the documentation for each method and
know they are the same semantically or if they don't exist in one or
the other.
This looks very strange and brittle design [...]
I agree. However it is not always in our power to control.
Hello,
Representing the Hoa project community, we have voted yes. Not everyone
was agree (60% yes, 40% no), but my vote reflects the average.
Main argument in favor of no:
- Encourage badly designed API.
Main argument in favor of yes:
- This is not an edge case when dealing with badly designed API, so
this is useful.
Cheers.
Hi Internals,
We've opened the vote on https://wiki.php.net/rfc/multiple-catch
Voting will end on 2015-05-01
Only one note. I would prefer to use "," instead of "|" as a class name separator.
Especially if "Union types" are not accepted.
The rest should be fine.
Thanks. Dmitry.
Only one note. I would prefer to use "," instead of "|" as a class name separator.
Especially if "Union types" are not accepted.
The rest should be fine.
On the other hand if union types is accepted it makes no sense to use "," here.
Only one note. I would prefer to use "," instead of "|" as a class name separator.
Especially if "Union types" are not accepted.
The rest should be fine.
On the other hand if union types is accepted it makes no sense to use "," here.
Right. Lets take a final decision about syntax, after "Unoin Types"
voting (but before 7.1 release).
Le 17/04/2016 18:51, Bronisław Białek a écrit :
We've opened the vote on https://wiki.php.net/rfc/multiple-catch
Hi,
At AFUP, we would be +1 on this RFC.
Basically: small feature that should not break existing code and can be
useful in some situations.
Thanks for your work on this!
--
Pascal MARTIN, AFUP - French UG
http://php-internals.afup.org/