> On 9 Sep 2019, at 00:44, Mike Schinkel <m...@newclarity.net> wrote: > > Hi Stephen, > > Thank you for the follow up. > > I am heading out for a week-long conference later today and not sure if I > will have time to participate on the list for a while so I wanted to get a > quick reply to you before I leave. > >> In terms of how I would see it working - > > In hindsight I should have been more clear what I meant when I said "working." > > A critical aspect of the proposal is the syntax for creating anonymous > unions, e.g.: process(Guid|string $guid) vs. always requiring `GuidUnion` to > be predefined before using it in a context like process(GuidUnion $guid). > That is the part where I cannot see interfaces working, unless they are > created magically too. But it there really any point to that? > >> For classes, I don’t know what the solution would be - and I’m honestly less >> clear on how much demand there is for that level of predictable, >> developer-controlled automatic casting anyway. > > I am getting that impression, at least from your response. > > That seems weird to me though given how many people on the list want a > stricter language. To me this seemed like the best of both worlds, but I do > get it. > >> Are you suggesting ... where a function to handle a date/time may for >> example accept an integer or an instance of \DateTimeInterface and thus be >> given a variable of either type and need to do whatever appropriate checks >> to use it, instead it would set a type hint of e.g `DateOrTimestamp` which >> is a class defining that it’s either an int or \DateTimeInterface via the >> `types` keyword, and the original function would get an instance of that >> object, regardless of whichever type is passed, and the engine would >> automatically wrap/box the passed value in that class? > > Yes, you got it, except you did not mention the ability to create an > anonymous union with `int|\ DateTimeInterface`, i.e. using the vertical bar > where a type specifier can otherwise be used. > > So per the proposal this: > > function processGuid(string|Guid $guid) { > ... > } > > Would be equivalent to this, except the above union would not have an actual > name that could be reused elsewhere, just like anonymous classes don't: > > class GuidUnion { > types string|Guid; > } > function processGuid(GuidUnion $guid) { > ... > } > > >> The example you provided doesn’t quite make sense to me even in the above >> scenario, because it shows accepting a string scalar as a type, but then >> somehow that value is an object with methods, not a scalar? Is that a >> further misunderstanding on my point, or is that example meant to include a >> hypothetical `GuidLike` ‘union class’ which has `types Guid|string` in it?. > > If I understand your confusion I think you are confused because I was showing > the use of an anonymous union and the proposal proposes that a scalar passed > to a function type hinted to be a union would receive the scalar "boxed" into > a union object. This means in the function you access the desired scalar via > accessor methods of the union object and not a scalar. > > Does that clarify? > >> Looking back at your original proposal’s examples, it becomes clearer with >> the above understanding (if that’s what you meant), but it also seems even >> _less_ intuitive to me now, even though I understand (well, I *think* I do) >> what you’re actually suggesting, and I still don’t really see the benefits >> you’re proposing. > > I am appreciating that it comes across as less intuitive. It is probably > intuitive to me because what I am proposing is (almost) exactly how *empty* > interfaces work in GoLang and I have been programming in my spare time in Go > for a year now alongside my paid PHP duties. > > The question is, is this concept so unintuitive as to make it a non-starter > for PHP, or is it like many concepts in programming; unintuitive when you > first see them but once you learn them completely intuitive? > > That is a rhetorical question I cannot answer as a single individual; the > arrive at an answer requires the feedback of many others. > >> If a method accepts a parameter with a union type of `GUID|string`, yes you >> have to do some work to verify that the string is in fact a guid - possibly >> converting it into an instance of GUID along the way. > > Which is part of what the proposal is trying to standard and streamline as > there is no way to cast an object to a class in PHP, and thus no way to get a > type-safe value as a known object. You can do this: > > $local_guid = $guid instanceof \Guid ? $guid : null; > > But it seems ro me so much more elegant — and readable — if you could just do > this: > > $local_guid = $guid->toGuid(); > >> If a method has a signature using a ‘Union Class type’ which has a `types >> GUID|string` keyword, you _still_ have to verify that the result of value() >> (or toString()) is in fact a valid GUID > > Yes, and no. In the proposal $guid->toGuid() would return null, so no need > to have to check if you can use the presence of null as an indicator. > > But if you did need to check that is why the proposal includes the > $guid->type() method, that works well with `switch` statements; something we > do not otherwise have in PHP because of the distinction between `gettype()` > vs. `get_class()`. The proposal's approach means less potentially confusing > boilerplate logic. > > As an aside, turning classes into first-class language elements would be > super powerful and I would really like to see that, but that feels like too > big of an ask for the PHP community so I have been thinking it would not be > worth the effort to create such a proposal. > >> because the calling site has just given you a string, and the ‘magic’ has >> boxed that up into an object.. so you can call a method to get back the same >> value, right? > > Not sure I am following that question unless you are just confirming that > when $guid->type() === 'string' that is_string( $guid->toString()) is true? > > One benefit to this approach I realize my proposal did not emphasize as that > of a union becoming a first-class entity, with the ability to extend the base > union when not using anonymous unions, and to add to or modify its methods. > This is something we won't get if we simply use type aliases in Nikitia's > proposal. > > But in hindsight maybe I am trying to propose types as first-class objects > after all, using a use-case where there would be real tangible benefits vs. > the general case? > > What I think I am recognizing is that while this functionality works > brilliantly in Go it may not be of the nature that makes sense for the PHP > world. > > Anyway, thank you again for following up. If I am unable to reply to any > responses for the next week+ it will be because I am focusing on the > conference I will be attending. > > -Mike > > >> - because the calling site has just given you a string, and the ‘magic’ has >> boxed that up into an object.. so you can call a method to get back the same >> value, right? > > >> On Sep 8, 2019, at 9:56 AM, Stephen Reay <php-li...@koalephant.com> wrote: >> >> Hi Mike, >> >> Sorry for the delay responding to this. >> >> So I would agree that magic methods are generally a less-obvious solution, >> and interfaces are generally a better alternatives for new solutions. >> >> In terms of how I would see it working - the same way that implementing the >> `Iterator` (or `IteratorAggregate`) interfaces allows a class to be iterated >> using foreach, my thought (and im pretty sure I’ve seen a similar concept >> suggested by others on internals before too) was that e.g. when passing an >> object that implements the `stringable` interface to a type that expects a >> string, it would convert it to such, even in strict mode, without warning or >> error. The same could be used for any built in basic type (scalars and >> arrays). >> >> Heck, a `Money` class could implement `stringable` (return a formatted >> string), `intable` (return the amount in the minor currency unit - i.e. >> cents for most dollar currencies) and `floatable` (return an approximation >> of the amount as major.minor. >> >> Again - I know those names are not fantastic, but you get the idea. >> >> >> For classes, I don’t know what the solution would be - and I’m honestly less >> clear on how much demand there is for that level of predictable, >> developer-controlled automatic casting anyway. >> >> I’m almost certain I’ve thus-far missed some point of what you were trying >> to convey, but I _think_ I understand now… sort of. >> >> Are you suggesting that rather than (or as well as?), as per Nikita’s >> proposal, where a function to handle a date/time may for example accept an >> integer or an instance of \DateTimeInterface and thus be given a variable of >> either type and need to do whatever appropriate checks to use it, instead it >> would set a type hint of e.g `DateOrTimestamp` which is a class defining >> that it’s either an int or \DateTimeInterface via the `types` keyword, and >> the original function would get an instance of that object, regardless of >> whichever type is passed, and the engine would automatically wrap/box the >> passed value in that class? >> >> >> The example you provided doesn’t quite make sense to me even in the above >> scenario, because it shows accepting a string scalar as a type, but then >> somehow that value is an object with methods, not a scalar? Is that a >> further misunderstanding on my point, or is that example meant to include a >> hypothetical `GuidLike` ‘union class’ which has `types Guid|string` in it?. >> >> Looking back at your original proposal’s examples, it becomes clearer with >> the above understanding (if that’s what you meant), but it also seems even >> _less_ intuitive to me now, even though I understand (well, I *think* I do) >> what you’re actually suggesting, and I still don’t really see the benefits >> you’re proposing. >> >> If a method accepts a parameter with a union type of `GUID|string`, yes you >> have to do some work to verify that the string is in fact a guid - possibly >> converting it into an instance of GUID along the way. >> >> If a method has a signature using a ‘Union Class type’ which has a `types >> GUID|string` keyword, you _still_ have to verify that the result of value() >> (or toString()) is in fact a valid GUID - because the calling site has just >> given you a string, and the ‘magic’ has boxed that up into an object.. so >> you can call a method to get back the same value, right? >> >> >> >> Cheers >> >> Stephen >> >
Hi Mike, Thanks for the confirmation/clarification. 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 was talking about allowing for ‘automatic’, ‘casting’ (in a way that isn't possible now except for strings) of objects when past to into a context where a simple (scalar|array) type is dictated. The end result would be the type specified (irrespective of it being a union or not in fact), and yes, I’m aware there’s potential ambiguous cases - what if the destination accepts `string|int` and the object supports both interfaces? 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 - 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 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? 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? I think (or I hope so at least) we’re at least both clear what the other is talking about now. Enjoy your conference! Cheers Stephen -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php