Re: Union Class Types (was Union Type (singular) straw man proposal)
| From: | Mike Schinkel | Date: | Fri, 06 Sep 2019 07:15:46 +0000 |
| Subject: | Re: Union Class Types (was Union Type (singular) straw man proposal) | ||
| References: | 1 2 3 4 5 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-106891@lists.php.net to get a copy of this message | ||
Before responding to your points let me reiterate that I made the proposal as much to generate
discussion on ideas that I was not seeing discussed as to see *my* proposal get selected and
implemented. So I like to think I won't be defensive about any criticism, and will be happy if
the only thing that comes out of this is seeing these concepts considered that I did not think were
being discussed.
> -- New magical methods that appear out of nowhere without an
> interface, e.g. ::type(), ::value(), ::setValue(), ::toXZY(). if these
> magical methods suddenly appear with may be varadic based on the value
> of the
types setting, this is inconsistent to the rest of PHP.
That is a really good point, and something I agonized over.ai
(By your use of the term "variadic" I assume you are referring to the fact that toXxxx()
functions would be defined based on the number of types listed after the types keyword?)
I thought about the potential to propose a base class but that would not address the toXxxx()
functions. And the same problem exists with an interface, right?
I thought about the potential to propose a typedValue() method to which you would pass
the type you want to return, but types are not first-class in PHP and are only represented in
strings such as you get when called gettype(). So that would mean the method could not be typed and
thus effectively eliminate the whole point of the proposal.
Another issue with a base class and an interface is the desire to create a syntax that can be
concise yet still leverage what already exists as much as possible as illustrated by these two
examples from the proposal:
function showNumber(new class{types int|float} $number) {
echo $number->value();
}
function showNumber2(int|float $number) {
echo $number->value();
}
It would be very tedious is we had to do this:
function showNumber(new class extends UnionClass {types int|float} $number) {
echo $number->value();
}
Or worse:
function showNumber(new class implements UnionInterface {types int|float; function type(){...};
function value(){...}; etc...} $number) {
echo $number->value();
}
So, I will turn it around. How can we address this better than I proposed? I sincerely did my best
thus far, but I am sure it can be done better if only I could envision it.
(One idea would be for it to automatically implement a magic "UnionTypes" interface. Which
would also need to be variadic.)
> -- There is no mention of what happens if there is multiple
> types in
> a definition.
I don't understand this point. Didn't my type example use int|float and then add string
and \Foo\Bar?
I am sure you were referring to something else, but it is not clicking for me.
> -- Why is the syntax using | as a separator where all other decls is
> using a comma separated list?
Because that is the same syntax as both recent union types proposal used and also PHPDoc, for
example:
https://github.com/nikic/php-rfcs/blob/union-types/rfcs/0000-union-types-v2.md#proposal
<https://github.com/nikic/php-rfcs/blob/union-types/rfcs/0000-union-types-v2.md#proposal>
And one of the reasons I think they may have chose that syntax is because I think using a comma here
would potentially result in some developer confusion if not ambiguity, and to use pipes in the
function parameter list but commas after the type keyword would be inconsistent:
function showNumber2(int,float $number) {
echo $number->value();
}
That said, I am agnostic about that syntax; I picked what seemed to be the best. When I started
writing it I was planning to propose a new language feature called a union that could be declared
like this:
union Number {
int;
float;
}
Or like this
function showNumber2(union{int;float} $number) {
echo $number->value();
}
But as I wrote it up I realized that what I was proposing was simply a class with some special
properties. To be clear the aspect of the proposal that is most fundamental is the auto-creation of
the union class instance when a value is passed as a parameter or assigned to a property declare for
that union class and the "variadic" methods that allow accessing the values in a type safe
way,
> -- Nitpick The "types" decl doesn't use an ending semicolon in any of
> your examples
> -- Nitpick: Your examples in the "Accepting params into a union
> instance" section uses $this in a procedural function
Doh! Copy paste error and my normal dependence on PhpStorm to ensure my syntax is always correct
(but I can't have PhpStorm correct my proposed future syntax!)
Anyway, fixed. And thanks for catching.
> -- In the "Named Union equivalence to anonymous union" section I fail
> to see how Building2 can magically accept Building, this is based on
> properties and there is there is no mention of the ability to have (or
> not to have) multiple named unions as property types.
Well, would you accept "magic" as a valid answer? :-D
Seriously though, I envision that PHP would need to keep a types token for union classes that could
be compared for type equivalence. PHP would need to take the list of types and although I assume it
would be better do use a more compact internal form I'll use a string for discussion. It would
sort the types and convert to a string like "int|float" and/or
"int|float|string" and then do the same for the function. It would then compare them for
equivalence to determine
Effectively what I am proposing here is a form of structural typing vs. nominal typing, which can be
done easily since the list of types is
https://en.wikipedia.org/wiki/Structural_type_system
<https://en.wikipedia.org/wiki/Structural_type_system>
https://en.wikipedia.org/wiki/Nominal_type_system
<https://en.wikipedia.org/wiki/Nominal_type_system>
https://medium.com/@thejameskyle/type-systems-structural-vs-nominal-typing-explained-56511dd969f4
<https://medium.com/@thejameskyle/type-systems-structural-vs-nominal-typing-explained-56511dd969f4>
BTW, I also would like to later to propose PHP consider supporting structural typing because there
are so many things we could do with PHP and type hinting if we had this that we cannot do today,
such as pressure that every class with a __ToString() method implements a hypothetical standardized
Stringable() interface. It would also allow us to use interfaces with PHP libraries where the
original author did not explicitly declare that his classes implemented the interface you happen to
need.
> -- Is this designed to be only meant for mutability (You are
> publically exposing setValue())?
Sorry, I do not understand the context of this question.
> -- What happens if the class is extended?
I just updated the proposal to provide an example, but I was not able to discover any edge cases
that would have it behave any differently than extended classes currently behave.
Was there any edge cases you were thinking about, or just noting that I did not have a section
covering extension?
> -- Why is it desired to implement a namespaced class instance return?
> If you are working on the object, you should already have access to
> this information it provides, no?
That is a good question. It might be an edge case I did not consider, although I'm still not
sure.
What should happen if we wanted this union?
namespace Autos
class Mercedes {
types German\Mercedes | Austrian\Mercedes;
}
> -- Why is ::types() apart of the union instead of leaving that to
> reflection?
> No other part of PHP exposes such as a method over
> procedural functions (like get_object_vars()).
Because using Reflection can requires a mental translation at times. At least for me. So since I
like the convenience of it and as it was my proposal so I thought I'd slide it in. :-D
That said, totally does not matter to the proposal. As I envisioned it, this was a straw man
proposal, intended to be torn apart, and then hopefully reassembled into something better.
Does this address all your concerns, at least well enough to bring up additional concerns?
Whatever the case, I appreciate the many questions, even if it was in the morning. :-)
-Mike