Hello everyone.
Since the release of PHP 8, my colleagues and I have been in some heated discussions about why union types are a great addition to PHP as a language.
They claim there’s no “real life” use case for it.
I believe otherwise, but I can’t for the life of me come up with some great real life examples of how it’s useful.
What do you guys think?
Anytime you’d overload a method in some other language to allow passing in different types is when you’d use union types in PHP. Union types for return values is something I’d discourage.
Either way, many existing functions already return multiple types. Narrowing the type from mixed
to int|float
or what have you is still a good thing.
I don’t get it. For classes there are interfaces. Want this function to support another class as input? Make it conform to an interface accepted by this function, problem solved. And for scalar inputs I’d second the request for the real life example.
The one use case I think of is for type hinting models or collections of models.
Have people forgot we’ve been using union types for years now?
?int is just a shorthand union of int|null
Half the built in functions use union return types to indicate an error e.g. string|false
“Half the built in functions use union return types to indicate an error e.g. string|false”… while they shouldn’t, and must be throwing exceptions instead.
So, all the examples so far are something|null
or int|float
. Not so much to justify a whole feature, If you ask me.
Look through your code base for places without a type, are typed using phpdoc annotations, or have multiple similarly named methods with effectively the same signature. There probably won’t be many, but in the few places they are it’s more useful to have it than the leave it untyped (silently mixed
) or to require having multiple variations based on the input type.
The main ones I can find in my own codebase that could make use of it are:
Unions can also be useful for migrating between different implementations of a feature or conflicting interfaces at the choke points of their usage.
Finally something that looks promising and for which I can’t come up with some devil’s advocate bullshit off the top of my head.
Union types are great, but unsurprisingly, the PHP feature is a little underwhelming.
If we could name union types, that would be cool. Something like:
But alas, we can’t do that, which isn’t that big of a deal, but it would be nice as a way of signalling what your API really is.
The other shortcoming is that PHP has no concept of a sealed class (a la Scala and Kotlin, or Rust/Swift enums). So you can’t easily add common behavior to the variants of your unions.
So, like I said, it’s good. It’s just more cumbersome to use effectively than it could’ve been, IMO.
They’re useful and have their place but are not a game changer.
Constructor property promotion OTOH definitely is 🙂
They claim there’s no “real life” use case for it.
I am on mostly on side of your colleagues. For some bad, legacy code; sure, they might temporarily help.
But I don’t see any use case for new apps, except for int|float
.
From my POV, once you start to union string|array
, it will probably end in (over-exaggerating) string|array|float|int|banana|dog|cat
… and the trouble is that code will need to handle each case.
Can you imagine how that will look like?
Instead; go with interface. If you don’t want to make lots of classes, you can always use anon classes. Or define custom types and use static analysis; PHPStorm has a decent support for them.
Vendors code; well that is different story. But I doubt that FOSS will use unions for same reasons as above.
Personal experience: I once made a mistake of returning array|null|bool
(via phpdocs) in interface method; I still can’t unwrap that mess I made.
Ain’t gonna happen again, that’s for sure.
Nullable types we had so far like ?int
is not the same as unions. It is just a great improvement over how Java handles types by default.
Nullable types we had so far like ?int is not the same as unions. It is just a great improvement over how Java handles types by default.
But it is the same. It’s just that now we can encode even more exclusive states than “this type or nothing”.
I mean, I agree that array|null|bool
is kind of scary. But it’s because none of those mean anything to me. What if you returned: ResultList|null|ErrorState
? (I can’t imagine what your interface method could have actually been returning).
The truth is that I don’t know if this will lead to more bad code or less from PHP devs. How common was it before to type hint a function to say it returned int
only to then “update” it to return mixed
? Will it be more likely to happen now that int|bool
looks more “legit” than mixed
or no return hint? Or will it result in the decent devs among us writing cleaner, more expressive API signatures? Perhaps both.
I commented on another reply why interfaces don’t quite cut it all the time.
I also commented that PHP’s unions aren’t nearly as useful as they could be.
The biggest use for me will be to properly handle numeric values from the e-commerce platform that I work with. It has a bad habit of storing floats and integers as strings and sometimes returning one or the other, which means that if I want to use strict types in my code, I need to cast the values ((int)
or (float)
when I know the type, += 0
when I don’t). Instead of casting, I can do this in my code: string|int $parameter
or string|float $parameter
or, in rare instances, string|int|float $parameter
.
You cannot accept both a stringable-object and a primitive string without union types. I predict that will be the most widely used union type, followed by int|float
.
Another possibility, using PSR-7 objects:
In that case, take a request and either give back a modified request or a response (aka “done”). That’s great for building pipelines.
To be clear, yes, it’s a niche feature. It’s not something that every method should be using. But it does have some clear, targeted use cases where it will make code a lot better.
And if you have crappy code that is taking multiple types of parameter for no good reason, now you can document that fact directly in the code. That makes it more obvious that the crappy code is crappy, and you know you should refactor it away from that.
Types are enforced and you can catch errors faster. So you don’t need to document what type a variable should be because you can directly specify it with union types.
Is useful in any real case scenario where a parameter should be only of a specified type group.
So the question is, what is the real life example of such a group.
For me the real life use cases are there if you’d want to model your classes closer to the business.
For example:
Imagine an order. An order might need to be manually reviewed. If so, it can not be shipped yet. Once an order is reviewed it must be shipped, but a copy must also go to the finance department so it can be billed.
If we were to closely model this, a non-crud way of doing so could be using a `ReviewableOrder`, `ShippableOrder`, and a `BillableOrder`. Some functions might work on any of these, some might only on those orders that are already reviewed, etc.
I didn’t think of this example myself, I took it straight out of the Domain Modeling Made Functional book. And since this is a book about the functional programming paradigm, I’m going to assume union types are going to be incredibly useful for those using this paradigm with PHP.
Isn’t it a clear use case for the interfaces? Those names sound exactly as interfaces to me. Interesting point about functional programing though.
Members
Online