> 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

Reply via email to