Enums were a very useful addition to PHP, however one aspect of them is neither
explicitly documented - or seemingly even talked about.
Enums were implemented as final so they cannot be extended nor can extend
anything else.
From a user perspective it's surprising - and actually limiting.
USAGE EXAMPLE:
I am making an error management system: each error presented to the user
must have a unique code visible.
class SystemError
{
public function __construct(
private string $errorText,
private ErrorCode $code
) {
}
public function __toString():
{
return $this->errorText . ' ' . $this->code->toString();
}
}
// ...
enum ErrorCode
{
case Code_1;
case Code_2;
public function toString(): string
{
return 'Error code:' . substr($this->name, strlen('Code_'));
}
}
Now I want to modify it to support different modules with different
namespaces for
errors, e.g. an ApiError, simple enough:
enum BaseErrorCode
{
// ...
}
enum ErrorCode extends BaseErrorCode
{
case Code_1;
case Code_2;
// ...
}
enum ApiErrorCode extends BaseErrorCode {
// ...
function toString(): string
{
return 'Error code:API-' . substr($this->name, strlen('Code_'));
}
}
This results in a syntax error.
PROPOSAL:
Enums should be able to extend other enums.
For a complete wishlist, add:
- abstract enums;
- enums allowed to implement interfaces;
However since I have no experience in PHP source code, I can only
provide the test suite for a possible PR this might have :(
Do you think this is likely to get implemented?
The point of an ENUM is precisely to restrict options within a certain
range.
Assume you declared ErrorCode
with SOMETHING_BROKE
and PEBKAC
as
possible enumeration values.
If you can (somewhere, anywhere in the code) check instanceof ErrorCode
,
then you know it can only be SOMETHING_BROKE
and PEBKAC
, and you can
write logic around it accordingly.
This restriction/assumption is what makes ENUMs powerful: expanding
pre-existing enums would eliminate this very core design principle, since
you may now no longer have an ErrorCode
, but some child class designed
somewhere that may not even be under your control.
Greets,
Marco Pivetta
https://mastodon.social/@ocramius
Enums were a very useful addition to PHP, however one aspect of them is
neither
explicitly documented - or seemingly even talked about.Enums were implemented as final so they cannot be extended nor can extend
anything else.From a user perspective it's surprising - and actually limiting.
USAGE EXAMPLE:
I am making an error management system: each error presented to the user
must have a unique code visible.class SystemError { public function __construct( private string $errorText, private ErrorCode $code ) { } public function __toString(): { return $this->errorText . ' ' . $this->code->toString(); } } // ... enum ErrorCode { case Code_1; case Code_2; public function toString(): string { return 'Error code:' . substr($this->name, strlen('Code_')); } }
Now I want to modify it to support different modules with different
namespaces for
errors, e.g. an ApiError, simple enough:enum BaseErrorCode { // ... } enum ErrorCode extends BaseErrorCode { case Code_1; case Code_2; // ... } enum ApiErrorCode extends BaseErrorCode { // ... function toString(): string { return 'Error code:API-' . substr($this->name, strlen('Code_')); } }
This results in a syntax error.
PROPOSAL:
Enums should be able to extend other enums.
For a complete wishlist, add:
- abstract enums;
- enums allowed to implement interfaces;
However since I have no experience in PHP source code, I can only
provide the test suite for a possible PR this might have :(Do you think this is likely to get implemented?
--
To unsubscribe, visit: https://www.php.net/unsub.php
Enums were a very useful addition to PHP, however one aspect of them is neither
explicitly documented - or seemingly even talked about.Enums were implemented as final so they cannot be extended nor can extend
anything else.From a user perspective it's surprising - and actually limiting.
USAGE EXAMPLE:
I am making an error management system: each error presented to the user
must have a unique code visible.class SystemError { public function __construct( private string $errorText, private ErrorCode $code ) { } public function __toString(): { return $this->errorText . ' ' . $this->code->toString(); } } // ... enum ErrorCode { case Code_1; case Code_2; public function toString(): string { return 'Error code:' . substr($this->name, strlen('Code_')); } }
Now I want to modify it to support different modules with different
namespaces for
errors, e.g. an ApiError, simple enough:enum BaseErrorCode { // ... } enum ErrorCode extends BaseErrorCode { case Code_1; case Code_2; // ... } enum ApiErrorCode extends BaseErrorCode { // ... function toString(): string { return 'Error code:API-' . substr($this->name, strlen('Code_')); } }
This results in a syntax error.
PROPOSAL:
Enums should be able to extend other enums.
For a complete wishlist, add:
- abstract enums;
- enums allowed to implement interfaces;
However since I have no experience in PHP source code, I can only
provide the test suite for a possible PR this might have :(Do you think this is likely to get implemented?
--
To unsubscribe, visit: https://www.php.net/unsub.php
Hey Rokas,
My approach has been to use an intersection type like:
BackedEnum&MyCustomBehaviorInterface
This works for me but it'd be interesting if something like that
wouldn't work for you.
to restrict options within a certain range.
Okay that's an aspect of enums that I never gave much thought to, but
you're completely right.
However to be explicit about that aspect of functionality one would
then have to also be
allowed to define final enums.
That way you still have both options - to restrict the option set, but
to also allow adding
custom ones while benefiting from other enum advantages.
Thus, rfc idea seems to be:
- enums should be able to extend other enums;
- abstract enums;
- enums allowed to implement interfaces;
- final enums (NEW!)
BackedEnum&MyCustomBehaviorInt
That's a fascinating approach but to a different problem than illustrated in OP.
Would you mind sharing (in private preferably to not distract from the
discussion) the
gist of the problem this solves? I'm really curious, as I've yet to
use intersect types :)!
Enums were a very useful addition to PHP, however one aspect of them is neither
explicitly documented - or seemingly even talked about.Enums were implemented as final so they cannot be extended nor can extend
anything else.From a user perspective it's surprising - and actually limiting.
USAGE EXAMPLE:
I am making an error management system: each error presented to the user
must have a unique code visible.class SystemError { public function __construct( private string $errorText, private ErrorCode $code ) { } public function __toString(): { return $this->errorText . ' ' . $this->code->toString(); } } // ... enum ErrorCode { case Code_1; case Code_2; public function toString(): string { return 'Error code:' . substr($this->name, strlen('Code_')); } }
Now I want to modify it to support different modules with different
namespaces for
errors, e.g. an ApiError, simple enough:enum BaseErrorCode { // ... } enum ErrorCode extends BaseErrorCode { case Code_1; case Code_2; // ... } enum ApiErrorCode extends BaseErrorCode { // ... function toString(): string { return 'Error code:API-' . substr($this->name, strlen('Code_')); } }
This results in a syntax error.
PROPOSAL:
Enums should be able to extend other enums.
For a complete wishlist, add:
- abstract enums;
- enums allowed to implement interfaces;
However since I have no experience in PHP source code, I can only
provide the test suite for a possible PR this might have :(Do you think this is likely to get implemented?
--
To unsubscribe, visit: https://www.php.net/unsub.php
Hey Rokas,
My approach has been to use an intersection type like:
BackedEnum&MyCustomBehaviorInterfaceThis works for me but it'd be interesting if something like that
wouldn't work for you.
Hey Rokas,
The idea of declaring an enum abstract
is potentially worth exploring
(I'm not convinced, but go for it), but beware that removing the implicit
final
from ENUMs is a BC break, since it breaks the assumption that an
ENUM is a sealed type.
If you design a proposal now, you need to consider that final
as the
implicit default is an existing contract that really enables ENUM usage.
Marco Pivetta
https://mastodon.social/@ocramius
to restrict options within a certain range.
Okay that's an aspect of enums that I never gave much thought to, but
you're completely right.However to be explicit about that aspect of functionality one would
then have to also be
allowed to define final enums.That way you still have both options - to restrict the option set, but
to also allow adding
custom ones while benefiting from other enum advantages.Thus, rfc idea seems to be:
- enums should be able to extend other enums;
- abstract enums;
- enums allowed to implement interfaces;
- final enums (NEW!)
BackedEnum&MyCustomBehaviorInt
That's a fascinating approach but to a different problem than illustrated
in OP.Would you mind sharing (in private preferably to not distract from the
discussion) the
gist of the problem this solves? I'm really curious, as I've yet to
use intersect types :)!On Wed, 29 Mar 2023 at 11:47, Robert Landers landers.robert@gmail.com
wrote:On Wed, Mar 29, 2023 at 10:31 AM Rokas Šleinius raveren@gmail.com
wrote:Enums were a very useful addition to PHP, however one aspect of them
is neither
explicitly documented - or seemingly even talked about.Enums were implemented as final so they cannot be extended nor can
extend
anything else.From a user perspective it's surprising - and actually limiting.
USAGE EXAMPLE:
I am making an error management system: each error presented to the
user
must have a unique code visible.class SystemError { public function __construct( private string $errorText, private ErrorCode $code ) { } public function __toString(): { return $this->errorText . ' ' . $this->code->toString(); } } // ... enum ErrorCode { case Code_1; case Code_2; public function toString(): string { return 'Error code:' . substr($this->name, strlen('Code_')); } }
Now I want to modify it to support different modules with different
namespaces for
errors, e.g. an ApiError, simple enough:enum BaseErrorCode { // ... } enum ErrorCode extends BaseErrorCode { case Code_1; case Code_2; // ... } enum ApiErrorCode extends BaseErrorCode { // ... function toString(): string { return 'Error code:API-' . substr($this->name,
strlen('Code_'));
}
}
This results in a syntax error. PROPOSAL: Enums should be able to extend other enums. For a complete wishlist, add: * abstract enums; * enums allowed to implement interfaces; However since I have no experience in PHP source code, I can only provide the test suite for a possible PR this might have :( Do you think this is likely to get implemented? -- To unsubscribe, visit: https://www.php.net/unsub.php
Hey Rokas,
My approach has been to use an intersection type like:
BackedEnum&MyCustomBehaviorInterfaceThis works for me but it'd be interesting if something like that
wouldn't work for you.--
To unsubscribe, visit: https://www.php.net/unsub.php
BC break
Hm, do you really think so? I wouldn't say removing the final
attribute from enums
actually "breaks" any functionality.
It WOULD allow some potentially illicit hacking to the end users of
our code, that's
true. I'd say a minor possibility of impact.
And anyway, isn't that what got you to fall in love with PHP in the first place?
Some good ol' illicit hacking of some dude's library :)
Hey Rokas,
The idea of declaring an enum
abstract
is potentially worth exploring (I'm not convinced, but go for it), but beware that removing the implicitfinal
from ENUMs is a BC break, since it breaks the assumption that an ENUM is a sealed type.If you design a proposal now, you need to consider that
final
as the implicit default is an existing contract that really enables ENUM usage.Marco Pivetta
https://mastodon.social/@ocramius
to restrict options within a certain range.
Okay that's an aspect of enums that I never gave much thought to, but
you're completely right.However to be explicit about that aspect of functionality one would
then have to also be
allowed to define final enums.That way you still have both options - to restrict the option set, but
to also allow adding
custom ones while benefiting from other enum advantages.Thus, rfc idea seems to be:
- enums should be able to extend other enums;
- abstract enums;
- enums allowed to implement interfaces;
- final enums (NEW!)
BackedEnum&MyCustomBehaviorInt
That's a fascinating approach but to a different problem than illustrated in OP.Would you mind sharing (in private preferably to not distract from the
discussion) the
gist of the problem this solves? I'm really curious, as I've yet to
use intersect types :)!Enums were a very useful addition to PHP, however one aspect of them is neither
explicitly documented - or seemingly even talked about.Enums were implemented as final so they cannot be extended nor can extend
anything else.From a user perspective it's surprising - and actually limiting.
USAGE EXAMPLE:
I am making an error management system: each error presented to the user
must have a unique code visible.class SystemError { public function __construct( private string $errorText, private ErrorCode $code ) { } public function __toString(): { return $this->errorText . ' ' . $this->code->toString(); } } // ... enum ErrorCode { case Code_1; case Code_2; public function toString(): string { return 'Error code:' . substr($this->name, strlen('Code_')); } }
Now I want to modify it to support different modules with different
namespaces for
errors, e.g. an ApiError, simple enough:enum BaseErrorCode { // ... } enum ErrorCode extends BaseErrorCode { case Code_1; case Code_2; // ... } enum ApiErrorCode extends BaseErrorCode { // ... function toString(): string { return 'Error code:API-' . substr($this->name, strlen('Code_')); } }
This results in a syntax error.
PROPOSAL:
Enums should be able to extend other enums.
For a complete wishlist, add:
- abstract enums;
- enums allowed to implement interfaces;
However since I have no experience in PHP source code, I can only
provide the test suite for a possible PR this might have :(Do you think this is likely to get implemented?
--
To unsubscribe, visit: https://www.php.net/unsub.php
Hey Rokas,
My approach has been to use an intersection type like:
BackedEnum&MyCustomBehaviorInterfaceThis works for me but it'd be interesting if something like that
wouldn't work for you.--
To unsubscribe, visit: https://www.php.net/unsub.php
Am 29.03.2023 um 11:31 schrieb Rokas Šleinius:
I wouldn't say removing the final attribute from enums actually "breaks" any functionality.
I am with Marco on this: removing the "finality" from enum would be a
major backward compatiblity break as it breaks a fundamental assumption
about enums.
Hi
Am 29.03.2023 um 11:31 schrieb Rokas Šleinius:
I wouldn't say removing the final attribute from enums actually "breaks" any functionality.
I am with Marco on this: removing the "finality" from enum would be a
major backward compatiblity break as it breaks a fundamental assumption
about enums.
And to give a specific example from PHP 8.3: As part of the Randomizer
additions RFC (https://wiki.php.net/rfc/randomizer_additions), PHP 8.3
got its first "natively included" enum (\Random\IntervalBoundary). This
enum works together with the new Randomizer::getFloat() method to
specify if the given min and max value may be returned or not.
The Randomizer::getFloat() method internally includes switch statement
that chooses the implementation based on the IntervalBoundary value given.
If a user would be able to extend the IntervalBoundary enum, the method
would not be able to make sense of it.
The IntervalBoundary enum completely enumerates all four possible
combinations for the two possible states for each of the two boundaries
(2*2 = 4). By definition there is no other valid value.
Best regards
Tim Düsterhus
Am 29.03.2023 um 11:55 schrieb Tim Düsterhus tim@bastelstu.be:
Am 29.03.2023 um 11:31 schrieb Rokas Šleinius:
I wouldn't say removing the final attribute from enums actually "breaks" any functionality.
I am with Marco on this: removing the "finality" from enum would be a
major backward compatiblity break as it breaks a fundamental assumption
about enums.And to give a specific example from PHP 8.3: As part of the Randomizer additions RFC (https://wiki.php.net/rfc/randomizer_additions), PHP 8.3 got its first "natively included" enum (\Random\IntervalBoundary). This enum works together with the new Randomizer::getFloat() method to specify if the given min and max value may be returned or not.
The Randomizer::getFloat() method internally includes switch statement that chooses the implementation based on the IntervalBoundary value given.
If a user would be able to extend the IntervalBoundary enum, the method would not be able to make sense of it.
The IntervalBoundary enum completely enumerates all four possible combinations for the two possible states for each of the two boundaries (2*2 = 4). By definition there is no other valid value.
I do understand the reason behind making Enums final and your example illustrates the point very well.
I still have a question I already asked in a different context: If there ever is the need to add a case to an Enum, was there any thought put into making this possible? Or is this categorically ruled out when using Enums?
Let’s look at a very, very hypothetical example: Imagine a Randomizer is much, much slower for certain boundaries and it is decided that some programs do not care about Closed/Open but instead care more about speed. So they would want to use something like IntervalBoundary::Fastest.
As far as I understand such an addition would never be possible, right? This means people defining Enums have to be very very certain that no one will ever want another value, right?
Regards,
- Chris
Hi!
There's a hypothetical (based on a similar real world problem that I
am facing) example for the use of extending enums in the OP.
And I don't suppose Tim was arguing for not allowing enum
extendability, but rather contributing a real world example where
users who were having some totally legal fun hacking around with
internal PHP code would break said internal PHP code.
Which may or may not be a big deal from my uneducated point of view,
just saying :) And all could be prevented just by adding a final
statement to the definition of IntervalBoundary
.
Am 29.03.2023 um 11:55 schrieb Tim Düsterhus tim@bastelstu.be:
Am 29.03.2023 um 11:31 schrieb Rokas Šleinius:
I wouldn't say removing the final attribute from enums actually "breaks" any functionality.
I am with Marco on this: removing the "finality" from enum would be a
major backward compatiblity break as it breaks a fundamental assumption
about enums.And to give a specific example from PHP 8.3: As part of the Randomizer additions RFC (https://wiki.php.net/rfc/randomizer_additions), PHP 8.3 got its first "natively included" enum (\Random\IntervalBoundary). This enum works together with the new Randomizer::getFloat() method to specify if the given min and max value may be returned or not.
The Randomizer::getFloat() method internally includes switch statement that chooses the implementation based on the IntervalBoundary value given.
If a user would be able to extend the IntervalBoundary enum, the method would not be able to make sense of it.
The IntervalBoundary enum completely enumerates all four possible combinations for the two possible states for each of the two boundaries (2*2 = 4). By definition there is no other valid value.
I do understand the reason behind making Enums final and your example illustrates the point very well.
I still have a question I already asked in a different context: If there ever is the need to add a case to an Enum, was there any thought put into making this possible? Or is this categorically ruled out when using Enums?
Let’s look at a very, very hypothetical example: Imagine a Randomizer is much, much slower for certain boundaries and it is decided that some programs do not care about Closed/Open but instead care more about speed. So they would want to use something like IntervalBoundary::Fastest.
As far as I understand such an addition would never be possible, right? This means people defining Enums have to be very very certain that no one will ever want another value, right?
Regards,
- Chris
--
To unsubscribe, visit: https://www.php.net/unsub.php
Hi
There's a hypothetical (based on a similar real world problem that I
am facing) example for the use of extending enums in the OP.And I don't suppose Tim was arguing for not allowing enum
extendability, but rather contributing a real world example where
users who were having some totally legal fun hacking around with
internal PHP code would break said internal PHP code.
I'm seeing that you already thought about the topic more based on the
other responses. But for the record: I was arguing for not allowing
enum extendability.
Which may or may not be a big deal from my uneducated point of view,
just saying :) And all could be prevented just by adding afinal
statement to the definition ofIntervalBoundary
.
Requiring folks to add 'final' to their existing enums for them to
behave as expected would be considered a breaking change.
Best regards
Tim Düsterhus
Hi
And to give a specific example from PHP 8.3: As part of the Randomizer additions RFC (https://wiki.php.net/rfc/randomizer_additions), PHP 8.3 got its first "natively included" enum (\Random\IntervalBoundary). This enum works together with the new Randomizer::getFloat() method to specify if the given min and max value may be returned or not.
The Randomizer::getFloat() method internally includes switch statement that chooses the implementation based on the IntervalBoundary value given.
If a user would be able to extend the IntervalBoundary enum, the method would not be able to make sense of it.
The IntervalBoundary enum completely enumerates all four possible combinations for the two possible states for each of the two boundaries (2*2 = 4). By definition there is no other valid value.
I do understand the reason behind making Enums final and your example illustrates the point very well.
I still have a question I already asked in a different context: If there ever is the need to add a case to an Enum, was there any thought put into making this possible? Or is this categorically ruled out when using Enums?
Adding a case to an enum would certainly be considered a breaking change
for folks that rely on having exhaustively handled all possible cases.
Let’s look at a very, very hypothetical example: Imagine a Randomizer is much, much slower for certain boundaries and it is decided that some programs do not care about Closed/Open but instead care more about speed. So they would want to use something like IntervalBoundary::Fastest.
In this specific hypothetical example, the fastest case would be one of
the existing 4 cases - an alias if you want to call it that.
Defining an alias is possible without breaking compatibility by
leveraging class constants:
<?php
enum IntervalBoundary {
case ClosedOpen;
case ClosedClosed;
case OpenClosed;
case OpenOpen;
public const Fastest = self::ClosedOpen;
}
var_dump(IntervalBoundary::ClosedOpen === IntervalBoundary::Fastest);
function foo(IntervalBoundary $b) {
var_dump($b);
}
foo(IntervalBoundary::Fastest);
Alternatively a method "public static function getFastest(): self" would
be possible.
As far as I understand such an addition would never be possible, right? This means people defining Enums have to be very very certain that no one will ever want another value, right?
I believe the short answer to the last question is "Yes".
Best regards
Tim Düsterhus
Thank you for all the insight, I feel like having extendable enums
is an agreeable and organic way forward, so if I may, I would like
to go ahead and create an RFC draft for this and keep adding the
discussion outcomes till it settles for good.
If I can get the Karma to do so, of course, my wiki name is
raveren, thank you!
Sidenote, just asking :) was this expected behaviour that I had to
analyse PHP source of the Docuwiki, create a new input field in
the registration form named "spam" and fill it to gain a wiki
username? Otherwise it would just keep expecting a different
answer from me no matter what email I put in...
R.
Enums were a very useful addition to PHP, however one aspect of them is neither
explicitly documented - or seemingly even talked about.Enums were implemented as final so they cannot be extended nor can extend
anything else.From a user perspective it's surprising - and actually limiting.
USAGE EXAMPLE:
I am making an error management system: each error presented to the user
must have a unique code visible.class SystemError { public function __construct( private string $errorText, private ErrorCode $code ) { } public function __toString(): { return $this->errorText . ' ' . $this->code->toString(); } } // ... enum ErrorCode { case Code_1; case Code_2; public function toString(): string { return 'Error code:' . substr($this->name, strlen('Code_')); } }
Now I want to modify it to support different modules with different
namespaces for
errors, e.g. an ApiError, simple enough:enum BaseErrorCode { // ... } enum ErrorCode extends BaseErrorCode { case Code_1; case Code_2; // ... } enum ApiErrorCode extends BaseErrorCode { // ... function toString(): string { return 'Error code:API-' . substr($this->name, strlen('Code_')); } }
This results in a syntax error.
PROPOSAL:
Enums should be able to extend other enums.
For a complete wishlist, add:
- abstract enums;
- enums allowed to implement interfaces;
However since I have no experience in PHP source code, I can only
provide the test suite for a possible PR this might have :(Do you think this is likely to get implemented?
Am Mi., 29. März 2023 um 12:49 Uhr schrieb Rokas Šleinius <raveren@gmail.com
:
Sidenote, just asking :) was this expected behaviour that I had to
analyse PHP source of the Docuwiki, create a new input field in
the registration form named "spam" and fill it to gain a wiki
username? Otherwise it would just keep expecting a different
answer from me no matter what email I put in...
No, thanks for reporting. This should be fixed now.
Best,
Niklas
Enums were a very useful addition to PHP, however one aspect of them is
neither
explicitly documented - or seemingly even talked about.Enums were implemented as final so they cannot be extended nor can extend
anything else.
This is by design.
Enumerations are in type theory parlance sum types.
Objects in PHP, in general, are what are called product types.
From a user perspective it's surprising - and actually limiting.
The point of enums is to be limiting.
USAGE EXAMPLE:
I am making an error management system: each error presented to the user
must have a unique code visible.class SystemError { public function __construct( private string $errorText, private ErrorCode $code ) { } public function __toString(): { return $this->errorText . ' ' . $this->code->toString(); } } // ... enum ErrorCode { case Code_1; case Code_2; public function toString(): string { return 'Error code:' . substr($this->name, strlen('Code_')); } }
Now I want to modify it to support different modules with different
namespaces for
errors, e.g. an ApiError, simple enough:enum BaseErrorCode { // ... } enum ErrorCode extends BaseErrorCode { case Code_1; case Code_2; // ... } enum ApiErrorCode extends BaseErrorCode { // ... function toString(): string { return 'Error code:API-' . substr($this->name, strlen('Code_')); } }
This results in a syntax error.
You are approaching the design of this in a fundamentally wrong way.
Enums, as sum types, are meant to enclose a finite number of states.
If you want to expand those finite number of states, the solution is to use
a union type.
Let's say you have those 3 enums:
enum GenericErrors {
case ErrorType1;
case ErrorType2;
}
enum FileErrors {
case FileErrorType1;
case FileErrorType2;
case FileErrorType3;
}
enum NetworkErrors {
case NetworkErrorType1;
case NetworkErrorType2;
case NetworkErrorType3;
}
And you have a function that can fail with either a generic error or a file
error then you can be explicit by having:
function foo(): T|GenericErrors|FileErrors { /* Do something or Fail */ }
This signature provides you with great type safety and can be checked via
static analysis that all error cases are handled.
Moreover, you know you don't need to handle NetworkErrors.
Extending an Enum loses you this type safety, because now ff you say
something returns a GenericErrors well who knows if you cover all cases
because someone might have ad-hock added a new one.
PHP's type system is not perfect yet, but it has become powerful enough
that you can do things like what I just described.
Ideally we would have generics, and you could use a Result/Either Monad
(fancy word for "wrapper") to have a return type of Result<T,
GenericErrors|FileErrors>
Use the type system properly instead of trying to shove everything into an
"Inheritance" view of things.
Union types are not "bad", they are another way of creating a sum type.
(You could, with a lot of effort, create "proper" enumerations since PHP
8.0 by using union types and singleton classes.)
PROPOSAL:
Enums should be able to extend other enums.
For a complete wishlist, add:
- abstract enums;
- enums allowed to implement interfaces;
Enums can already implement interfaces: https://3v4l.org/tKtQ0
However since I have no experience in PHP source code, I can only
provide the test suite for a possible PR this might have :(Do you think this is likely to get implemented?
I dear hope so not.
Breaking fundamental type theoretical assumptions is terrible.
Best regards,
George P. Banyard
Enums were implemented as final so they cannot be extended nor can extend
anything else.This is by design.
Enumerations are in type theory parlance sum types.
Objects in PHP, in general, are what are called product types.
I was just about to write a response along the same lines. I think the
confusion comes because in Java-style OOP, the keyword "extends" actually
does two things at once:
- Takes the existing definition and adds to or over-rides parts of it
- Takes the existing type and declares a sub-type
What you actually want to do with an enum is:
- Take the existing definition and add cases to it
- Take the existing type and declare a super-type
To give a concrete example of why, consider Christian's hypothetical:
Imagine a Randomizer is much, much slower for certain boundaries and it
is decided that some programs do not care about Closed/Open but instead
care more about speed. So they would want to use something like
IntervalBoundary::Fastest.
If we use the normal definition of "extends", we could say this:
enum PerfIntervalBoundary extends IntervalBoundary {
case Fastest;
}
Now we have an enum with three cases, which is what we wanted. But we've
also declared a sub-type: we've said that "every PerfIntervalBoundary
is-a IntervalBoundary". That means that anywhere that previously expected
an IntervalBoundary, we can pass PerfIntervalBoundary::Fastest - but none
of the existing Randomizer classes will know what to do with it!
What we actually wanted to do is leave all the existing uses of
IntervalBoundary alone, but create a new WeirdPerfRandomizer which accepts
the new type. We wanted to list "PerfIntervalBoundary" as the input type,
but still be able to pass it IntervalBoundary::Closed.
In short, we want to say "every IntervalBoundary is-a PerfIntervalBoundary"
- the relationship is the other way around.
As George says, this can be implemented right now by using a union type
constraint, like PerfIntervalBoundary|IntervalBoundary, which means "allow
any case of PerfIntervalBoundary as well as any value of IntervalBoundary".
If we wanted to support it more directly, we would need a new keyword to
make clear that this is not a sub-type relationship - perhaps something
like "expands", "allows", or "encompasses".
Regards,
Rowan Tommins
[IMSoP]
Ok so I am trying to find the argumentation here:
This is by design.
The point of enums is to be limiting.
This is clearly an assumption. That statement is not in the docs or
RFC, nor such an oversimplification makes logical sense to me, but
maybe you have a source to back it up..?
Re: problem in the OP
You are approaching the design of this in a fundamentally wrong way.
The described problem is dealing with error codes not types, which
means that each error is assigned a new code. We are dealing with
anticipated errors here, things like validation, data integrity, 3rd
party API breakage etc.
e.g.: user gets code 456 trying to ship out package, it says package
not paid, user says he looked it up, its paid. The developer has a
very concrete lead to the problem - lookup usages of
ErrorCodes::Code_456
.
The set in question is finite, as you say of course, but much larger
and different in principle than in the solution you are proposing.
Problem in OP is looking for a way for end users to keep adding new
domains to the error codes passed to the parent system to be handled.
The actual, real requirements are:
- Ease of management of codes: simplicity of adding new codes to
the set, easy lookup, simplicity of making sure a code is not used
elsewhere before using it. All perfectly fulfilled by enums. - Reusing the parent system for different modules, using the parent
system in two different packages I want to opensource. Open Sourcing
the parent system..
Rowan Tommins is right on the target however, that is amazing feedback
for the desired functionality, excellent distillment of logic and all
correct points!
I will take some time to think about that.
Enums were a very useful addition to PHP, however one aspect of them is neither
explicitly documented - or seemingly even talked about.Enums were implemented as final so they cannot be extended nor can extend
anything else.This is by design.
Enumerations are in type theory parlance sum types.
Objects in PHP, in general, are what are called product types.From a user perspective it's surprising - and actually limiting.
The point of enums is to be limiting.
USAGE EXAMPLE:
I am making an error management system: each error presented to the user
must have a unique code visible.class SystemError { public function __construct( private string $errorText, private ErrorCode $code ) { } public function __toString(): { return $this->errorText . ' ' . $this->code->toString(); } } // ... enum ErrorCode { case Code_1; case Code_2; public function toString(): string { return 'Error code:' . substr($this->name, strlen('Code_')); } }
Now I want to modify it to support different modules with different
namespaces for
errors, e.g. an ApiError, simple enough:enum BaseErrorCode { // ... } enum ErrorCode extends BaseErrorCode { case Code_1; case Code_2; // ... } enum ApiErrorCode extends BaseErrorCode { // ... function toString(): string { return 'Error code:API-' . substr($this->name, strlen('Code_')); } }
This results in a syntax error.
You are approaching the design of this in a fundamentally wrong way.
Enums, as sum types, are meant to enclose a finite number of states.
If you want to expand those finite number of states, the solution is to use a union type.Let's say you have those 3 enums:
enum GenericErrors {
case ErrorType1;
case ErrorType2;
}enum FileErrors {
case FileErrorType1;
case FileErrorType2;
case FileErrorType3;
}enum NetworkErrors {
case NetworkErrorType1;
case NetworkErrorType2;
case NetworkErrorType3;
}And you have a function that can fail with either a generic error or a file error then you can be explicit by having:
function foo(): T|GenericErrors|FileErrors { /* Do something or Fail */ }
This signature provides you with great type safety and can be checked via static analysis that all error cases are handled.
Moreover, you know you don't need to handle NetworkErrors.
Extending an Enum loses you this type safety, because now ff you say something returns a GenericErrors well who knows if you cover all cases because someone might have ad-hock added a new one.PHP's type system is not perfect yet, but it has become powerful enough that you can do things like what I just described.
Ideally we would have generics, and you could use a Result/Either Monad (fancy word for "wrapper") to have a return type of Result<T, GenericErrors|FileErrors>Use the type system properly instead of trying to shove everything into an "Inheritance" view of things.
Union types are not "bad", they are another way of creating a sum type.
(You could, with a lot of effort, create "proper" enumerations since PHP 8.0 by using union types and singleton classes.)PROPOSAL:
Enums should be able to extend other enums.
For a complete wishlist, add:
abstract enums;
enums allowed to implement interfaces;
Enums can already implement interfaces: https://3v4l.org/tKtQ0
However since I have no experience in PHP source code, I can only
provide the test suite for a possible PR this might have :(Do you think this is likely to get implemented?
I dear hope so not.
Breaking fundamental type theoretical assumptions is terrible.Best regards,
George P. Banyard
Ok so I am trying to find the argumentation here:
This is by design.
The point of enums is to be limiting.This is clearly an assumption. That statement is not in the docs or
RFC, nor such an oversimplification makes logical sense to me, but
maybe you have a source to back it up..?
From a theory point of view, any type definition is about limiting
allowed values. The difference between "mixed" and "integer" is that
"mixed" allows both 'Hello' and 42, but "integer" does not - it defines a
tighter limit.
In the same way, saying "I accept an error code" means "I accept an error
code and nothing else" - the definition of what is and what isn't an
"error code" is deliberately a limit on the values that you accept.
Re: problem in the OP
You are approaching the design of this in a fundamentally wrong way.The described problem is dealing with error codes not types
An enum is a type, so that's why George was talking about types. He was
making the same point, in different words, as I did: the relationship
between types implied by the keyword "extends" is not the relationship you
want in this case.
The set in question is finite, as you say of course, but much larger
and different in principle than in the solution you are proposing.
Problem in OP is looking for a way for end users to keep adding new
domains to the error codes passed to the parent system to be handled.
This is the key point - if you pass an error code that didn't previously
exist, the existing system won't be able to handle it.
That's why enums are inherently restrictive: the system wants to be able to
say "this is the list of error codes I understand, don't give me anything
else".
If you have a system that accepts the new codes as well as the old ones,
you can use a union type declaration as George says:
enum AdditionalErrorCode {
case Code_456;
}
class ExtendedErrorHandler extends StandardErrorHandler {
public function handle(StandardErrorCode|AdditionalErrorCode $code) {
switch ( $code ) {
case AdditionalErrorCode::Code_456:
// handling for new code goes here
break;
default:
// assert($code instanceof StandardErrorCode);
parent::handle($code);
break;
}
}
}
Regards,
Rowan Tommins
[IMSoP]
First, I'm pretty sure I agree now that enums should not be
extend
-ed as regular classes, there's a fundamental difference as
you state - the extended enum is a fundamentally different THING as it
can hold different values - when an enum's sole purpose is to hold one
of the set of values.
That is to say - for most implementations that's the desired case.
I'll refer to the solution to the OP problem as ECS, Error Code System.
For ECS, the most elegant, but also the only, solution I can come up
with - is the kind of enum that you also suggest, a base-enum
.
abstract enum
?
Let me try again to describe the problem that I came here with that is
unsolvable by union types:
- ECS is a generic component. I want to plug it into any working
system (like an existing cms). - Invoking it must provide a unique ErrorCode. The existing cms has
to create an enum that will be accepted astype: ErrorCode
. It
cannot edit the existing ErrorCode as it is part of ECS and not the
cms. - There is NO special handling for the custom ErrorCode's that the
users of ECS create. We are just using the enums inferred value.
I will keep processing this situation further, but for now it seems to me like
- Extending enums is fundamentally flawed.
- Basing enums off of other enums has valid usage scenarios.
In that case, and, again, this needs way more thought to it, it's not
such a "generic way forward" that seemed to me at first and might only
provide marginal value at the cost of type complexity and that is most
probably, unfortunately, not worth it...
(unless abstract enum
might make sense, but my brain needs some time
off of this problem for now)
Thank you for such a thought out discussion, everyone!
Ok so I am trying to find the argumentation here:
This is by design.
The point of enums is to be limiting.This is clearly an assumption. That statement is not in the docs or
RFC, nor such an oversimplification makes logical sense to me, but
maybe you have a source to back it up..?From a theory point of view, any type definition is about limiting
allowed values. The difference between "mixed" and "integer" is that
"mixed" allows both 'Hello' and 42, but "integer" does not - it defines a
tighter limit.In the same way, saying "I accept an error code" means "I accept an error
code and nothing else" - the definition of what is and what isn't an
"error code" is deliberately a limit on the values that you accept.Re: problem in the OP
You are approaching the design of this in a fundamentally wrong way.The described problem is dealing with error codes not types
An enum is a type, so that's why George was talking about types. He was
making the same point, in different words, as I did: the relationship
between types implied by the keyword "extends" is not the relationship you
want in this case.The set in question is finite, as you say of course, but much larger
and different in principle than in the solution you are proposing.
Problem in OP is looking for a way for end users to keep adding new
domains to the error codes passed to the parent system to be handled.This is the key point - if you pass an error code that didn't previously
exist, the existing system won't be able to handle it.That's why enums are inherently restrictive: the system wants to be able to
say "this is the list of error codes I understand, don't give me anything
else".If you have a system that accepts the new codes as well as the old ones,
you can use a union type declaration as George says:enum AdditionalErrorCode {
case Code_456;
}class ExtendedErrorHandler extends StandardErrorHandler {
public function handle(StandardErrorCode|AdditionalErrorCode $code) {
switch ( $code ) {
case AdditionalErrorCode::Code_456:
// handling for new code goes here
break;
default:
// assert($code instanceof StandardErrorCode);
parent::handle($code);
break;
}
}
}Regards,
Rowan Tommins
[IMSoP]
On Wed, 29 Mar 2023 at 15:26, Rokas Šleinius rokas.sleinius@gmail.com
wrote:
Invoking it must provide a unique ErrorCode.
The concept of "uniqueness" only makes sense within a certain context - the
CMS easily create these two enums:
enum HttpErrorCode: int extends ErrorCode {
case NotFound = 404;
}
enum SQLErrorCode: int extends ErrorCode {
case BadColumnRef = 404;
}
If ECS accepts both HttpErrorCode::NotFound and SQLErrorCode::BadColumnRef,
and looks at the integer value, they will both be 404. Are they still
"unique" in any useful way?
The existing cms has
to create an enum that will be accepted as
type: ErrorCode
.
[...]
There is NO special handling for the custom ErrorCode's that the
users of ECS create.
I don't understand this pair of requirements: if ECS doesn't have handling
based on the value, why does it care what type it is?
If it cares that it can turn it into an integer, it can just request an
integer directly, or an object implementing an appropriate interface:
interface ErrorValueInterface {
public function getCode(): int;
}
From the description given, it seems like enums are sinmply the wrong tool
for the job.
Regards,
Rowan Tommins
[IMSoP]
First, I'm pretty sure I agree now that enums should not be
extend
-ed as regular classes, there's a fundamental difference as
you state - the extended enum is a fundamentally different THING as it
can hold different values - when an enum's sole purpose is to hold one
of the set of values.That is to say - for most implementations that's the desired case.
I'll refer to the solution to the OP problem as ECS, Error Code System.
For ECS, the most elegant, but also the only, solution I can come up
with - is the kind of enum that you also suggest, abase-enum
.
abstract enum
?Let me try again to describe the problem that I came here with that is
unsolvable by union types:
- ECS is a generic component. I want to plug it into any working
system (like an existing cms).- Invoking it must provide a unique ErrorCode. The existing cms has
to create an enum that will be accepted astype: ErrorCode
. It
cannot edit the existing ErrorCode as it is part of ECS and not the
cms.- There is NO special handling for the custom ErrorCode's that the
users of ECS create. We are just using the enums inferred value.I will keep processing this situation further, but for now it seems to me like
- Extending enums is fundamentally flawed.
- Basing enums off of other enums has valid usage scenarios.
In that case, and, again, this needs way more thought to it, it's not
such a "generic way forward" that seemed to me at first and might only
provide marginal value at the cost of type complexity and that is most
probably, unfortunately, not worth it...(unless
abstract enum
might make sense, but my brain needs some time
off of this problem for now)Thank you for such a thought out discussion, everyone!
-
Please don't top-post.
-
Some good reading material for this topic, both on enums and on error handling:
https://peakd.com/hive-168588/@crell/on-the-use-of-enums
https://peakd.com/hive-168588/@crell/much-ado-about-null
They're not short, but that's because they are complete. :-)
- In the error code case, the answer is to leverage the fact that enums do support interfaces.
interface ErrorCode
{
public function message(): string;
}
enum CommonErrors: string implements ErrorCode
{
case YourFault = 'You screwed up';
case OurFault = 'We screwed up';
public function message(): string
{
return $this->value;
}
}
readonly class SomeoneElseError implements ErrorCode
{
public function __construct(privateUser $atFault) {}
public function message(): string
{
return sprintf('%s screwed up', $this->atFault->name());
}
}
Now an error handling system can type against the ErrorCode interface, common errors have trivially simple enum values you can use, but you can also make your own. In this case, you're not using an enum as a limited-set; you're taking advantage of it being a way to predefine singleton objects. I would consider this a completely valid use of enums, and actually do something very similar in my serialization library:
https://github.com/Crell/Serde/blob/master/src/Renaming/RenamingStrategy.php
https://github.com/Crell/Serde/blob/master/src/Renaming/Cases.php
https://github.com/Crell/Serde/blob/master/src/Renaming/Prefix.php
- As others have said, extending enums is a bad idea with lots of reasons it's a bad idea, both conceptual and pragmatic. However, I would be open to discussing a
use
for enums to import cases from one enum into another.
There's two concerns with that to consider:
A) What happens to methods on the imported enum? Are they pulled in as well? Do interface definitions carry over? I don't know.
B) The long-term goal is to expand Enums to include associated values (https://wiki.php.net/rfc/tagged_unions, although that's a bit out of date now so don't take it as a roadmap). How would use
-ing an enum within another enum affect that? I have no idea, off hand.
--Larry Garfield
First, I'm pretty sure I agree now that enums should not be
extend
-ed as regular classes, there's a fundamental difference as
you state - the extended enum is a fundamentally different THING as it
can hold different values - when an enum's sole purpose is to hold one
of the set of values.That is to say - for most implementations that's the desired case.
I'll refer to the solution to the OP problem as ECS, Error Code System.
For ECS, the most elegant, but also the only, solution I can come up
with - is the kind of enum that you also suggest, abase-enum
.
abstract enum
?Let me try again to describe the problem that I came here with that is
unsolvable by union types:
- ECS is a generic component. I want to plug it into any working
system (like an existing cms).- Invoking it must provide a unique ErrorCode. The existing cms has
to create an enum that will be accepted astype: ErrorCode
. It
cannot edit the existing ErrorCode as it is part of ECS and not the
cms.- There is NO special handling for the custom ErrorCode's that the
users of ECS create. We are just using the enums inferred value.I will keep processing this situation further, but for now it seems to me like
- Extending enums is fundamentally flawed.
- Basing enums off of other enums has valid usage scenarios.
In that case, and, again, this needs way more thought to it, it's not
such a "generic way forward" that seemed to me at first and might only
provide marginal value at the cost of type complexity and that is most
probably, unfortunately, not worth it...(unless
abstract enum
might make sense, but my brain needs some time
off of this problem for now)Thank you for such a thought out discussion, everyone!
Please don't top-post.
Some good reading material for this topic, both on enums and on error handling:
https://peakd.com/hive-168588/@crell/on-the-use-of-enums
https://peakd.com/hive-168588/@crell/much-ado-about-nullThey're not short, but that's because they are complete. :-)
- In the error code case, the answer is to leverage the fact that enums do support interfaces.
interface ErrorCode
{
public function message(): string;
}enum CommonErrors: string implements ErrorCode
{
case YourFault = 'You screwed up';
case OurFault = 'We screwed up';public function message(): string
{
return $this->value;
}
}readonly class SomeoneElseError implements ErrorCode
{
public function __construct(privateUser $atFault) {}public function message(): string
{
return sprintf('%s screwed up', $this->atFault->name());
}
}Now an error handling system can type against the ErrorCode interface, common errors have trivially simple enum values you can use, but you can also make your own. In this case, you're not using an enum as a limited-set; you're taking advantage of it being a way to predefine singleton objects. I would consider this a completely valid use of enums, and actually do something very similar in my serialization library:
https://github.com/Crell/Serde/blob/master/src/Renaming/RenamingStrategy.php
https://github.com/Crell/Serde/blob/master/src/Renaming/Cases.php
https://github.com/Crell/Serde/blob/master/src/Renaming/Prefix.php
- As others have said, extending enums is a bad idea with lots of reasons it's a bad idea, both conceptual and pragmatic. However, I would be open to discussing a
use
for enums to import cases from one enum into another.There's two concerns with that to consider:
A) What happens to methods on the imported enum? Are they pulled in as well? Do interface definitions carry over? I don't know.
B) The long-term goal is to expand Enums to include associated values (https://wiki.php.net/rfc/tagged_unions, although that's a bit out of date now so don't take it as a roadmap). How would
use
-ing an enum within another enum affect that? I have no idea, off hand.--Larry Garfield
- Please don't top-post.
Am I doing it right now? I've never been on a mailing list.
In fact, I was too young and missed out on BBS as well, and now using
this laughably archaic collaboration method, receiving mostly
stone-harsh replies generously sprinkled with genius insights, having
to quite literally hack a registration form as one of the several
required steps for contribution... Wow this is a really remarkable
experience for me :D
- Some good reading material for this topic, both on enums and on error handling:
Thanks, noted, bookmarked, subscribed.
BTW the ECS example is only dealing with Errors because it's a concept
I thought people would grasp and relate to easily.
yesssss, enums support interfaces!!!!!!
Less than five minutes away from the keyboard I realised that this is
the key and my problem is solved more elegantly than the initial idea
of extending - the interface
is perfect, thank you to everyone who
pointed it out!
I am really impressed how elegant and actually genius the enum
implementation is. So well rounded, minimal, yet creates so much
value!
Re: Rowan:
The concept of "uniqueness" only makes sense within a certain context
That's why in ECS example I added the toString()
method to the enum,
but you're right, I should have named it something along the lines of
getErrorPrefix()
- and now - using the interface approach I can
assure any enum passed to "AddError" will also provide a prefix along
with the enum->name (error code) + enum->value (error text) :)
type ApiError = DatabaseError|HttpError;
Sounds cool and useful, hope there's motivation enough to see it
through, but that's a separate topic, I'm glad this discussion
happened, bye now everyone!!
Hi
- Please don't top-post.
Am I doing it right now? I've never been on a mailing list.
Yes. Ideally you would also cut the quoted parts to a minimum (like I'm
doing here), but I believe the Gmail web interface doesn't make this easy.
to quite literally hack a registration form as one of the several
required steps for contribution... Wow this is a really remarkable
experience for me :D
It appears that the wiki registration got broken during a wiki upgrade a
few days ago. Unfortunate timing on your end. I've let the responsible
person know about this.
Best regards
Tim Düsterhus
Hi
- Please don't top-post.
Am I doing it right now? I've never been on a mailing list.
Yes. Ideally you would also cut the quoted parts to a minimum (like I'm
doing here), but I believe the Gmail web interface doesn't make this easy.
Heya, I'm using the Gmail web interface and I think I haven't really
encountered major problems so far. Open the [...] below and you can split
and type underneath quoted parts. That said, mailing lists are ancient and
in my opinion a horrible way of doing things. I've had my fair share of
issues to sign up initially, there's a weird etiquette of how you have to
structure your replies, and most importantly you have to remind yourself
every time to press "reply all". There are also issues with messages ending
up in junk-mail or caught by spam-filters, and you heavily depend on your
mail client to properly group messages so you can actually digest them in a
conversational form. Email is also lacking edit buttons and integrated code
blocks like we're used to with github (or markdown in general). I don't
know how other projects deal with this though.
Am 30.03.2023 um 10:50 schrieb Lynn kjarli@gmail.com:
That said, mailing lists are ancient and in my opinion a horrible way of doing things.
Mailing lists have been around for a long time, yes, but it also means the way we use it for larger scale communication has been refined over time. Please consider that this process is based on a lot of experience.
I've had my fair share of issues to sign up initially, there's a weird etiquette of how you have to structure your replies
Once you familiarized yourself with the notion of giving enough context to your reply parts (but not too much) it becomes a very valuable way of keeping communication efficient.
The main point is that 1 person is writing a reply but 100s to 1000s have to read it and they do not have the same mental context as the writer.
So spending this extra effort to minimize the effort of the reader is well worth it.
, and most importantly you have to remind yourself.every time to press "reply all".
As it is sufficient to just reply to the mailing list this could technically be changed by adding a Reply-To. The downside would be that it makes private replies harder and the occasional slip-up could send a private reply to the list instead.
IMHO private replies should be kept to a minimum so I think (re-)considering adding a Reply-To might be worth it.
There are also issues with messages ending up in junk-mail or caught by spam-filters, and you heavily depend on your
mail client to properly group messages so you can actually digest them in a conversational form.
Threading emails has been part of mail clients for 20 years, I'm puzzled that people still consider this to be an issue.
Mail filters also allow to automatically separate into folders, if one wishes so.
Email is also lacking edit buttons
This is actually an important feature! It forces the writer to be more careful with replies which in turn safes time for the 100s of readers.
It also means that I don't miss edits or have to go back to edited messages and guess what has changed.
Editing messages just doesn't scale well with large recipient groups IMHO.
and integrated code blocks like we're used to with github (or markdown in general).
I don't think it is hard to format example code in a way that they are readable in an email.
Again: It is a little bit of extra work for the writer but this tends to lead to simpler code examples for the reader.
Regards,
- Chris
Am 30.03.2023 um 10:50 schrieb Lynn kjarli@gmail.com:
, and most importantly you have to remind yourself.every time to press "reply all".
As it is sufficient to just reply to the mailing list this could
technically be changed by adding a Reply-To. The downside would be that
it makes private replies harder and the occasional slip-up could send a
private reply to the list instead.
IMHO private replies should be kept to a minimum so I think
(re-)considering adding a Reply-To might be worth it.
Most mail clients I've used in the past decade have a "reply to list" function that I use extensively, which works quite well. That said, I am really sick of getting double copies of every reply in a thread after I've posted in it, so I would be delighted to update Reply-To to go back to the list only if it means I get less duplicate email. :-) Making it slightly harder for people to mail me off list (and confuse me about whether it's on list or not, which happens surprisingly often) is a small price to pay. Maybe an added benefit.
--Larry Garfield
Hi
This subthread really goes off-topic (I've renamed the Subject once more
:-) ).
That said, I am really sick of getting double copies of every reply in a thread after I've posted in it
I actually enjoy getting the second copy, because I can independently
file emails that specifically target me into a separate folder. Even if
I zone out on a larger thread, I will be able to react on emails that
are waiting for my response reasonably quickly.
On a non-PHP mailing list that I frequent, it is usual to always "Reply
All", but to manually trim the list of recipients in subthreads that are
likely no longer relevant to some of the folks that previously were
included. For internals I try to mimic the behavior of the person I'm
responding to.
Best regards
Tim Düsterhus
Hi
This subthread really goes off-topic (I've renamed the Subject once more
:-) ).
So on GMail and other clients using subject-based "conversation" grouping,
this is now a third thread; on clients using header-based "thread"
grouping, it is still in the original.
I actually enjoy getting the second copy, because I can independently
file emails that specifically target me into a separate folder.
This feels like something the mail client should be handling, not the
sender - watch/star thread, ignore/mute thread, filter by threads you've
participated in, etc
You certainly won't get such copies from me, except by mistake -
Thunderbird has "Reply to list", and in GMail web and K-9 Mail I mimic it
by hitting "Reply all" then trimming to just the list address.
That's both the biggest strength and the biggest weakness of e-mail -
different clients can give users different views and workflows of exactly
the same content. On the one hand, that means you can find one that works
how you like (e.g. I straight-up hate GMail's web UI for long threads). But
on the other hand, you can't predict how your messages will interact with
someone else's UI and workflow.
Regards,
Rowan Tommins
[IMSoP]
Hi
So on GMail and other clients using subject-based "conversation" grouping,
this is now a third thread; on clients using header-based "thread"
grouping, it is still in the original.
It's fine. At least that ensure that this off-topic discussion doesn't
pollute the view for clients that do not use a threaded view (looking at
you, Gmail).
I actually enjoy getting the second copy, because I can independently
file emails that specifically target me into a separate folder.This feels like something the mail client should be handling, not the
sender - watch/star thread, ignore/mute thread, filter by threads you've
participated in, etc
The mail client can't detect whether I'm the intended "direct" recipient
of a single email within a large thread. Especially not if I didn't
previously participate in it, but someone wants my opinion or expertise.
As Derick said: Deduplicating the email in your MUA is easy (the
Message-ID is identical), but getting the second copy without the
List-ID header is not easy.
Best regards
Tim Düsterhus
Am 30.03.2023 um 10:50 schrieb Lynn kjarli@gmail.com:
, and most importantly you have to remind yourself.every time to press "reply all".
As it is sufficient to just reply to the mailing list this could
technically be changed by adding a Reply-To. The downside would be that
it makes private replies harder and the occasional slip-up could send a
private reply to the list instead.
IMHO private replies should be kept to a minimum so I think
(re-)considering adding a Reply-To might be worth it.Most mail clients I've used in the past decade have a "reply to list"
function that I use extensively, which works quite well.
That said, I am really sick of getting
double copies of every reply in a thread after I've posted
in it, so I would be delighted to update Reply-To to go back
to the list only if it means I get
less duplicate email. :-)
Please no. As it's a destructive operation that you can't easily work around in email clients. Trimming recipients is doable.
You can handle duplicates locally too if you must.
cheers
Derick
Am 30.03.2023 um 10:50 schrieb Lynn kjarli@gmail.com:
, and most importantly you have to remind yourself.every time to press "reply all".
As it is sufficient to just reply to the mailing list this could
technically be changed by adding a Reply-To. The downside would be that
it makes private replies harder and the occasional slip-up could send a
private reply to the list instead.
IMHO private replies should be kept to a minimum so I think
(re-)considering adding a Reply-To might be worth it.Most mail clients I've used in the past decade have a "reply to list"
function that I use extensively, which works quite well.
That said, I am really sick of getting
double copies of every reply in a thread after I've posted
in it, so I would be delighted to update Reply-To to go back
to the list only if it means I get
less duplicate email. :-)Please no. As it's a destructive operation that you can't easily work
around in email clients. Trimming recipients is doable.
Then we need a cultural shift to get people to trim more, because I got two copies of your email, which show together in a thread even when I filter email separately.
--Larry Garfield
Please no. As it's a destructive operation that you can't easily work
around in email clients. Trimming recipients is doable.
Then we need a cultural shift to get people to trim more, because I got two copies of your email, which show together in a thread even when I filter email separately.
Also, some guidance on who to trim and when.
I count 7 people who could be in the reply list for this message if it
hadn't been trimmed (Rokas, George, Larry, Tim, Lynn, Christian,
Derick); I have no idea who is interested in getting a duplicate
notification of this reply, so tend to assume that the polite thing is
to trim all of them.
The mail client can't detect whether I'm the intended "direct"
recipient of a single email within a large thread. Especially not if I
didn't previously participate in it, but someone wants my opinion or
expertise.
That's actually a good argument for not using Reply All by default,
because as the person sending the message, deciding when to add you
as a direct recipient is considerably easier than deciding when to
remove you.
Regards,
--
Rowan Tommins
[IMSoP]
Hi Rokas,
- Please don't top-post.
Am I doing it right now? I've never been on a mailing list.
You're replying in the preferred manner, yes. Quoting only the needed
amount and putting replies underneath. Though deleting all
non-relevant text is also good.
Have two possibly useful guidelines:
-
Mailing list etiquette guidelines -
https://phpopendocs.com/internals/mailing_list -
Email etiquette for people new to newsgroups / PHP internals -
https://phpopendocs.com/internals/mailing_list_for_younguns
which might get copied across to php.net at some point...
In fact, I was too young and missed out on BBS as well, and now using
this laughably archaic collaboration method, receiving mostly
stone-harsh replies generously sprinkled with genius insights, having
to quite literally hack a registration form as one of the several
required steps for contribution... Wow this is a really remarkable
experience for me :D
Well, it builds character.
And as soon as someone suggests something that is obviously better,
and not just replacing one set of problems with another, the
discussions might move.
I am really impressed how elegant and actually genius the enum
implementation is. So well rounded, minimal, yet creates so much
value!
And it only took the PHP project about 20 years of discussion to get
an agreeable implementation...
cheers
Dan
Ack