Re: [RFC] Safe Casting Functions

From: Date: Tue, 21 Oct 2014 02:59:36 +0000
Subject: Re: [RFC] Safe Casting Functions
Groups: php.internals 
Request: Send a blank email to internals+get-78172@lists.php.net to get a copy of this message
On Oct 20 2014, at 8:07 PM, Josh Watzman <jwatzman@fb.com> wrote: > 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 of NULL 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 PHP > > Since 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: 1. Exceptions can provide more information about the cast failure (e.g. overflow vs. invalid type vs. missing __toString method) 2. 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. 3. There *is* precedent for this in other languages. Josh already mentioned Python, and I'll add C#. Convert.ToInt64() in C# throws a FormatException if the string does not have valid syntax, or an OverflowException if the value is less than Int64.MinValue or greater than Int64.MaxValue. 4. 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. On Oct 20 2014, at 8:17 PM, Andrea Faulds <ajf@ajf.me> wrote: > 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

« previous php.internals (#78172) next »