I think this is pretty cool, but I'm really worried about some of its typing implications. In particular:
The functions return
FALSE
on failure instead ofNULL
because:Throwing an exception or even returning
NULL
seems so much better than returning "false" -- "false" is a boolean, not an error, and despite some historical cases of PHP using "false" as a poor person's error code, it really isn't. If you want error codes, use error codes, or use exceptions, but having two kinds of failure, null vs false, is really confusing.Addressing your arguments one by one:
• If strict type hinting were added, they would fail for a nullable typehint
Throwing an exception also addresses this, in a much cleaner way. If you're worried about strict typing, then returning "false" is even worse, since the return type of, for example, to_int is now "int | bool" as opposed to "nullable-int" or "int" (if you throw).
•
FALSE
is a traditional error value in PHPSince this is a new function, one that doesn't interoperate in any complicated way with the existing library or affect BC, this doesn't seem that important. IMO a language should have one failure/absense-of-value, in most cases "null", and having a weird second case seems, well, weird. If you have more interesting failure cases, just throw an exception, or return null if you want, don't continue propagating a weird second kind of null (that isn't actually null, it's a boolean false).
Also, that we couldn't introduce a meaningful to_bool function, even if we decided we wanted it, indicates to me that returning false is the wrong thing to do.
•
NULL
is used to signify the absence of a value - but what we want to signify is an invalid value (similar to 0 vs NaN)I might argue that failure is indeed absense-of-value, but an exception seems like a much better way to convey this.
It's also interesting to look at how other languages handle failures of this kind. Most imperative languages I know of either throw or return null; in particular, Python, which has fairly similar type system to PHP in a lot of ways, throws. Functional languages do more interesting things; Haskell does something complicated but morally equivalent to returning null. But I'm not aware of any language which specifically returns false.
I'm going to second Josh on this and add some of my own thoughts as a userland developer.
I believe it is important to have a simple, safe, and consistent way to cast values in PHP, and I would be supportive of these functions regardless of whether they throw an exception or return an error value. With that said, I believe that exceptions would be the best way to handle cast failures for the following reasons:
- Exceptions can provide more information about the cast failure (e.g. overflow vs. invalid type vs. missing
__toString
method) - Throwing an exception is safer. For example,
strpos($haystack, $needle, to_int($offset))
would thrown an exception rather than silently converting $offset to 0 if it can't be safely cast. - There is precedent for this in other languages. Josh already mentioned Python, and I'll add C#.
Convert.ToInt64()
in C# throws aFormatException
if the string does not have valid syntax, or anOverflowException
if the value is less than Int64.MinValue or greater than Int64.MaxValue. - Mixed return types are a bad pattern. If we get scalar return type declarations in the future it would be nice to be able to use them with these functions.
Exceptions make chaining more difficult.
How so? Chaining is worse with error values, in my experience! For example:
$result = do_something_with(to_int(to_float($value)));
if ($result === false) {
// I have no idea which function returned false or why
}
vs.
try {
do_something_with(to_int(to_float($value)));
} catch (Exception $e) {
// I know exactly which function caused the error and why it occurred
}
My userland polyfill includes a branch which throws exceptions: https://github.com/theodorejb/PolyCast/tree/use-exceptions.
Theodore Brown
http://theodorejb.me