Re: Union Class Types (was Union Type (singular) straw man proposal)
| From: | Mike Schinkel | Date: | Thu, 12 Sep 2019 17:12:26 +0000 |
| Subject: | Re: Union Class Types (was Union Type (singular) straw man proposal) | ||
| References: | 1 2 3 4 5 6 7 8 9 10 11 12 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-106984@lists.php.net to get a copy of this message | ||
Hi Larry,
Thank you for the comment.
> It seems like what you're describing here is more user-customizable autoboxing than
> anything to do with union types.
Your first statement ("user-customizable autoboxing") is probably fair to say.
OTOH, rather than say "anything to do with union type" I would instead say "where
union-types is an excellent use-case for auto-boxing."
> For that, it seems like you'd want more of a fromString() method (magic or interface):
That would certainly work.
But unfortunately that requires the class developer to be forward-thinking enough to implement
methods required for all the use-cases of their class users, and I think it is unlikely we can
depend on that case for most classes in the wild.
What I'd rather see is a solution that gives the user of the class more control and a
well-known/consistent programming model (e.g. ->toGuid().)
> Whether or not something like that is wise I don't know, but it seems like it would
> resolve the use case you describe better than fiddling with union types. Or am I totally
> misunderstanding you as well? :-)
I made the proposal because I felt that auto-boxing was the best solutions for the union-types
use-case. So if renaming the proposal to "user-customizable autoboxing" is more preferred
by others I'd see that to be fine, assuming it was implemented in a manner that allowed for
adding the use-case of union types. :-)
-Mike
P.S. I am not seeing much traction from this proposal so even though we discuss the nuances it seems
likely it won't matter beyond this discussion.
> On Sep 11, 2019, at 6:07 AM, Larry Garfield <larry@garfieldtech.com> wrote:
>
> On Sun, Sep 8, 2019, at 2:11 PM, Mike Schinkel wrote:
>> Hi Stephen,
>>
>> Thanks again.
>>
>>> So let me start out by clarifying that what *I* was suggesting with unions is quite a
>>> different concept than you’re talking about.
>>
>> I did not realize you were proposing a different solution, too. Sorry I
>> missed that.
>>
>>> yes, I’m aware there’s potential ambiguous cases - what if the destination accepts
>>>
string|int and the object supports both interfaces?
>>
>>
>> Precisely. And the goal of my proposal was in part to eliminate that ambiguity.
>>
>>> Ok - the example given makes “sense” with the understanding that it’s alternative
>>> ‘short’ syntax - but that makes it yet another step further from ‘intuitive’ to me, compared
>>> with current type hints, and the extension to allow unions Nikita introduced -
>>
>> Acknowledged.
>>
>>> and if Im not mistaken it makes it incompatible too (i.e. you can’t have the
>>> behaviour Nikita’s RFC describes and what you describe with the ‘anonymous unions’ - they’re
>>> too very different approaches using the same syntax.
>>
>> I do not see how they are incompatible except for the details mine
>> changes, but maybe that is moot at this point?
>>
>>> I think the intuitiveness factor is easier to explain if you consider the case of the
>>> Number example - plenty of things will work exactly as you expect if you use either an int or a
>>> float. But then with your proposal suddenly you don’t have a scalar variable any more - yes I know
>>> you can call value() - part of the point of type hints is to reduce
>>> boilerplate with type checking arguments within the function body - and now you have to call a
>>> method to get a usable value anyway?
>>
>> I do not see any technical reason why mine would not be able to behave
>> the same using the magic methods you were proposing such as
>> __toString() and for other scalar types — since fundamentally the
>> proposed union type would be a class anyway. When used that way it
>> would nullify the benefit of the explicit typing, but if that is what
>> the developer wanted who was using then why not?
>>
>> In that context it would be almost the same as just passing in a scalar.
>>
>>> The GUID example still confuses me in terms of how your example ‘helps’. If
>>> $guid->getGuid returns null (signifying it’s a string?) presumably I still want to do something
>>> with that string - otherwise I wouldn’t have accepted it as a valid type - at which point I might
>>> as well just do a instanceof or
>>> is_string call - but now I have to check if the value I was given is
>>> null, even if I didn’t specify the parameter as being nullable?
>>
>> It does not help that way. The main help is with $guid->type() which
>> can be used in a switch. It's just cleaner code, though I can
>> appreciate that some might see this as not really a benefit:
>>
>> function processGuid(string|Guid $guid) {
>> switch( $guid->type()) {
>> case 'Guid':
>> $guid->toGuid()->doSomething();
>> break;
>> case 'string':
>> $g = new Guid($guid->toString());
>> $g-> doSomething();
>> break;
>> }
>> }
>>
>> The other benefit is that it becomes a first-class concern that can be
>> extended when named union classes are used so I could implement this
>> inside the union class and use it in many functions like this, if I
>> wanted, rather than have to use type checking boilerplate in Every.
>> Single. Function:
>>
>> class GuidUnion {
>> types string|Guid
>> public function doSomething() {
>> switch( $this->type()) {
>> case 'Guid':
>> $this->toGuid()->doSomething();
>> break;
>> case 'string':
>> $g = new Guid($this->toString());
>> $g-> doSomething();
>> break;
>> }
>> }
>> }
>>
>> /**
>> * HERE is where you start seeing a real benefit...
>> */
>> function processGuid1(GuidUnion $guid) {
>> $guid->doSomething();
>> }
>> function processGuid2(GuidUnion $guid) {
>> $guid->doSomething();
>> }
>> function processGuid3(GuidUnion $guid) {
>> $guid->doSomething();
>> }
>>
>>
>>> I think (or I hope so at least) we’re at least both clear what the other is talking
>>> about now.
>>
>> Hopefully, with that last point clarified, yes.
>>
>> Thanks again.
>>
>> -Mike
>
> It seems like what you're describing here is more user-customizable autoboxing than
> anything to do with union types. Viz, since a Guid has a natural and round-trippable string
> representation, you want to be able to ask for a Guid, be given a string, and then work with it as a
> Guid either way.
>
> For that, it seems like you'd want more of a fromString() method (magic or interface):
>
> class Guid implements FromStringInterface {
>
> public static function fromString(string $s) {
> if (is_valid_guid($s)) {
> return new static($s);
> }
> throw new \TypeError();
> }
> }
>
> function needGuid(Guid $g) {
> $g->doGuidThings();
> }
>
>
> needGuid('a9d05a5f-e643-441e-8784-cfd613072072');
>
> Whether or not something like that is wise I don't know, but it seems like it would
> resolve the use case you describe better than fiddling with union types. Or am I totally
> misunderstanding you as well? :-)
>
> --Larry Garfield
>
> --
> PHP Internals - PHP Runtime Development Mailing List
> To unsubscribe, visit: http://www.php.net/unsub.php
>