Re: Union Class Types (was Union Type (singular) straw man proposal)

From: 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 >

« previous php.internals (#106984) next »