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

Reply via email to