[Discussion] Thoughts on casting to null

php.internals

Robert Landers

2 years ago
Hello Internals, Today, I was working with a library (ReVolt) that throws if a callback returns something other than null (why it doesn't ignore the value, I don't know, maybe I'll file an issue). I discovered the topic of this email after trying to be simple: EventLoop::repeat($pingInterval, $client->ping(...)); Where `ping()` will return a bool which triggers an error since it returns something. I thought to myself, cool, I'll just cast the result to null and everything will be fine: EventLoop::repeat($pingInterval, fn() => (null) $client->ping()); However, this isn't allowed and fails with a parse error. I won't be the first to say this, at first glance, casting to null sounds silly, but short arrow functions must always return something, by design. That's when casting to null makes any sense at all (that I can think of): you want to write a succinct, short function but guarantee the result is discarded. Instead, if you really must use a short array function, you have to do something even weirder: EventLoop::repeat($pingInterval, fn() => $client->ping() ? null : null); I assume casting to null was discussed previously (at some point, though I didn't see anything), but what are your thoughts? Is this something that even makes sense? I'll admit, I've never tried casting to null before, but I had assumed it would "just work" and was slightly surprised that it didn't. Robert Landers Software Engineer Utrecht NL

Ilija Tovilo

2 years ago
Hi Robert On Wed, Feb 14, 2024 at 1:29 AM Robert Landers <landers.robert@gmail.com> wrote:
> > I won't be the first to say this, at first glance, casting to null > sounds silly, but short arrow functions must always return something, > by design. That's when casting to null makes any sense at all (that I > can think of): you want to write a succinct, short function but > guarantee the result is discarded. Instead, if you really must use a > short array function, you have to do something even weirder: > > EventLoop::repeat($pingInterval, fn() => $client->ping() ? null : null);
While (void) $client->ping() would solve your problem, it's not very useful outside this scenario. I think there are two issues you're implicitly referring to. 1. Arrow functions cannot be void, because they always return something. 2. Arrow functions cannot contain multiple statements. As for the former, PHP actually had a similar issue for never closures that was solved a while ago [1]. In the report I suggested that the same could be done for void, by making void arrow functions evaluate and drop the right hand side of =>, and always return nothing (i.e. null). This would solve your issue, although probably mostly by accident (because void functions return null, and your caller expects exactly null). Regardless, I think this change would be useful, if just to signal that the return value of an arrow function is not intended to be used. The latter would require some sort of block or grouped expression. Short closures have been discussed extensively in the past, so I won't get into that. There's also the comma operator in some languages like C and JavaScript that evaluates a list of expressions and returns the result of the last one, although probably not universally liked (i.e. fn () => ($client->ping(), null)). [1] https://github.com/php/php-src/commit/f957e3e7f17eac6e9195557f3fa79934f823fd38 Ilija

Григорий Senior PHP / Разработчик Web

2 years ago
I don't even know why lambda realization is not the same as in javascript. ``` let action = function () { console.log('hello'); return 1; } let fn1 = () => action(); console.log(fn1()); // calls hello(), returns 1, for me, not a best way, could be read like "call action() then create function that returns result of that call" let fn2 = () => (action()); console.log(fn2()); // calls hello(), returns 1, common, short way // what's this? (() => ())(); // could look strange? no, its declaring unnamed function that does nothing and returns nothing and calls it immediately. let fn3 = () => { action(); } console.log(fn3()); // calls hello(), returns undefined, common, short without return let fn4 = () => action; console.log(fn4()); // returns function, very handy for Promises, that start init function immediate on new call, so it wraps function to "task" available to call it later somewhere, maybe in another promise ``` Why is the PHP implementation redesigned from scratch instead of using the idea of the one that works? ps. Guess, the discussion to change syntax that you cold rewrite with single line: ``` // > what? // fn() => $client->ping() ? null : null // > that // static function () { $client->ping(); } ``` Ahh, the main problem in fn() is exactly that you don't like.... `use` statement)))) So long to write the `use` keyword. Аnd you prefer the `js` implementation, which automatically uses the current scope in your callback. And you need something handy, that is allow create functions: - with all variables (copy local state) - with short syntax - with return modes variety - with return types and parameter types, if you need it is more interesting, than talk about error collection that requires rewriting half of the app As I remember you say, you work on projects with a million lines of code and you prefer correctness instead of simplification. Why do you use short functions then? OOP awaits you. Create a function that wraps function: 1. one function returns result (client.ping()) 2. second one returns null|result (myclass.clientPingOrNull()) 3. The third one catches the first one with a try catch to return the result and return errors (myclass.tryClientPingOrNull()) 4. then call that batch of correctness in your callback... Sorry, couldn't resist myself

Hans Henrik Bergan

2 years ago
EventLoop::repeat($pingInterval, function(...$args)use($client){$client->ping(...$args)});