# Language constructs and callability

- Thread: https://externals.io/message/68206
- Original archive: https://news-web.php.net/php.internals/68206
- Started by Daniel Lowrey on 2013-07-19T16:36:45+00:00
- Messages: 22
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #68206 — Daniel Lowrey — 2013-07-19T16:36:45+00:00

URL: https://externals.io/message/68206#68206

I have a simple question about the callability of language constructs and
whether or not that's something that might change in the future. Consider:

var_dump(is_callable('echo')); // bool(false)
var_dump(call_user_func('echo', 'foo')); // `E_WARNING` 
echo('foo'); // foo

var_dump(is_callable('isset')); // bool(false)
var_dump(isset(1)); // `E_ERROR` 

Obviously this behavior arises because tokens like `echo` and `isset` are
language constructs and not functions. I can see some potential benefits
for working around this. For example, say I want to filter only the `NULL` 
elements from an array but keep the other "falsy" values. Recognizing
`isset` as callable would allow me to do this:

var_dump(array_filter([0, FALSE, NULL], 'isset')); // [0, FALSE]

Of course, this limitation is trivial to work around with a userland
callback to check for the explicit `NULL` equivalency, but it would be nice
to avoid the hassle. So my question is ...

How deeply ingrained into the engine is this behavior? Is there any chance
of language constructs ever passing the tests for callability or is that
just a pipe dream that's not worth the implementation effort?

---

## #68207 — Peter Cowburn — 2013-07-19T17:07:30+00:00

URL: https://externals.io/message/68206#68207 · In reply to: #68206

> I have a simple question about the callability of language constructs and
> whether or not that's something that might change in the future. Consider:
>
> var_dump(is_callable('echo')); // bool(false)
> var_dump(call_user_func('echo', 'foo')); // `E_WARNING` 
> echo('foo'); // foo
>
> var_dump(is_callable('isset')); // bool(false)
> var_dump(isset(1)); // `E_ERROR` 
>
> Obviously this behavior arises because tokens like `echo` and `isset` are
> language constructs and not functions. I can see some potential benefits
> for working around this. For example, say I want to filter only the `NULL` 
> elements from an array but keep the other "falsy" values. Recognizing
> `isset` as callable would allow me to do this:
>
> var_dump(array_filter([0, FALSE, NULL], 'isset')); // [0, FALSE]
>

array_filter([…], 'is_null');


>
> Of course, this limitation is trivial to work around with a userland
> callback to check for the explicit `NULL` equivalency, but it would be nice
> to avoid the hassle. So my question is ...
>
> How deeply ingrained into the engine is this behavior? Is there any chance
> of language constructs ever passing the tests for callability or is that
> just a pipe dream that's not worth the implementation effort?

---

## #68208 — Jelle Zijlstra — 2013-07-19T17:11:37+00:00

URL: https://externals.io/message/68206#68208 · In reply to: #68207

2013/7/19 Peter Cowburn <petercowburn@gmail.com>

> 
>
> > […]
> Consider:
> > […]
>
> array_filter([…], 'is_null');
>
> That would do the opposite of what you want.

>
> > […]
> chance
> > […]

---

## #68209 — Daniel Lowrey — 2013-07-19T17:11:38+00:00

URL: https://externals.io/message/68206#68209 · In reply to: #68207

> array_filter([…], 'is_null');

Doesn't work -- `is_null` would return `TRUE` leaving a result of [NULL]
after the array_filter operation. That's the diametric opposite of what my
example code looks to do.


On Fri, Jul 19, 2013 at 1:07 PM, Peter Cowburn <petercowburn@gmail.com>wrote:

>
>
>
> 
>
> > […]
>
> array_filter([…], 'is_null');
>
>
> > […]

---

## #68210 — Peter Cowburn — 2013-07-19T17:13:30+00:00

URL: https://externals.io/message/68206#68210 · In reply to: #68208

>
>
>
> 2013/7/19 Peter Cowburn <petercowburn@gmail.com>
>
> > […]
>

Absolutely (I blame Friday). Still, this is only being suggested because
"isset" just happens to do what is needed in this case? Where to other
language constructs fit into it?


>
> > […]

---

## #68211 — Daniel Lowrey — 2013-07-19T17:16:55+00:00

URL: https://externals.io/message/68206#68211 · In reply to: #68208

> Oh yeah, I see your point about is_null but not why it makes any more
sense to allow language constructs there.

Yeah I don't know if it even does. I'm not even really sure if there's a
good example use case for it besides the array_filter/isset combo. My
question was really only exploratory in nature.


On Fri, Jul 19, 2013 at 1:12 PM, Peter Cowburn <petercowburn@gmail.com>
 wrote:

> Oh yeah, I see your point about is_null but not why it makes any more
> sense to allow language constructs there.
>


On Fri, Jul 19, 2013 at 1:11 PM, Jelle Zijlstra <jelle.zijlstra@gmail.com>wrote:

>
>
>
> 2013/7/19 Peter Cowburn <petercowburn@gmail.com>
>
> > […]
>
> > […]

---

## #68212 — Arpad Ray — 2013-07-19T17:23:02+00:00

URL: https://externals.io/message/68206#68212 · In reply to: #68206

> How deeply ingrained into the engine is this behavior? Is there any chance
> of language constructs ever passing the tests for callability or is that
> just a pipe dream that's not worth the implementation effort?
>

It's actually pretty trivial to provide functions for echo and print (just
did a 5 minute POC), but I'm not convinced it's very useful. I don't think
isset would be possible other than making it equivalent to an is_not_null.

If the aim is some sort of consistency, what would you do with constructs
like `define()` or include()?

Arpad

---

## #68213 — Daniel Lowrey — 2013-07-19T17:28:47+00:00

URL: https://externals.io/message/68206#68213 · In reply to: #68212

It would more than likely create some semantic nightmares. I can envision
people doing numpty things like:

array_map('include', $dependencyFilesArr);

While it works, it somewhat obscures what you're trying to accomplish in
the code. The maintainability nightmare alone may be enough to
counterbalance any benefits. Then again, who needs
readability/maintainability when you can write hopelessly impenetrable
code? :)




> 
>
> > […]
>
> It's actually pretty trivial to provide functions for echo and print (just
> did a 5 minute POC), but I'm not convinced it's very useful. I don't think
> isset would be possible other than making it equivalent to an is_not_null.
>
> If the aim is some sort of consistency, what would you do with constructs
> like `define()` or include()?
>
> Arpad

---

## #68215 — Arpad Ray — 2013-07-19T17:32:41+00:00

URL: https://externals.io/message/68206#68215 · In reply to: #68213

> While it works, it somewhat obscures what you're trying to accomplish in
> the code. The maintainability nightmare alone may be enough to
> counterbalance any benefits. Then again, who needs
> readability/maintainability when you can write hopelessly impenetrable
> code? :)
>
>
Yes, quite, let's just pretend this thread never happened ;)

Arpad

---

## #68216 — johannes@schlueters.de — 2013-07-19T18:00:06+00:00

URL: https://externals.io/message/68206#68216 · In reply to: #68206

Hi,



> I have a simple question about the callability of language constructs and
> whether or not that's something that might change in the future. Consider:
> 
> var_dump(is_callable('echo')); // bool(false)
> var_dump(call_user_func('echo', 'foo')); // `E_WARNING` 
> echo('foo'); // foo
> 
> var_dump(is_callable('isset')); // bool(false)
> var_dump(isset(1)); // `E_ERROR` 

By itself the idea looks good and useful - but There is an issue: Those
language constructs have different semantics from functions. On the one
side there a simple difference which can more or less be ignored - like
the fact that "echo" has no return value and might be called without
parentheses.

But also more complex ones:


> var_dump(array_filter([0, FALSE, NULL], 'isset')); // [0, FALSE]

The argument for isset is not sent via the regular parameter stack but
directly as operands to the opcode. This is a key part to the trick on
how it works for not producing a notice about undeclared variables and
not creating it as userland emulations would. 

So let's assume we allow this then we must allow this simple code:

    $c = 'isset';
    $c($foo);

Now the opcode generated by the compiler here won't know what $c is in
the second line (yes, yes, in this case an optimizer could figure it
out) and will produce opcode which tries to fetch $foo on the argument
stack. Now we could fix this in the executor by doing a string
comparison on the function name and - oh wait - we'll already have
messed with a variable which shouldn't be there ...

So, unfortunately no, language constructs have different semantics which
we can't emulate inside function semantics (well ok, it is software, so
it is thinkable .. but nobody,yet, came up with a robust patch which
doesn't cause maintenance *and* performance penalty)

johannes

---

## #68241 — Tjerk Anne Meesters — 2013-07-20T03:53:02+00:00

URL: https://externals.io/message/68206#68241 · In reply to: #68215

> 
>
> > […]
> Yes, quite, let's just pretend this thread never happened ;)
>

Let's also pretend I didn't suggest to add a third argument to
 `array_filter()`;

array_filter($arr, 'is_null', PHP_FILTER_INVERT);

;-)


>
> Arpad
>



-- 
--
Tjerk

---

## #68243 — Sara Golemon — 2013-07-20T04:09:28+00:00

URL: https://externals.io/message/68206#68243 · In reply to: #68241

I think the idea comes from a good place, but the actual, practical
usefulness of it is so low that the availability of workaround negates even
the very small amount of effort it would take to create (and maintain!)
shadow functions for the language constructs.

array_filter($arr, function($val) { echo $val; });   <--- done.  No
gymnastics needed.


On Fri, Jul 19, 2013 at 8:53 PM, Tjerk Anne Meesters <datibbaw@php.net>wrote:

> 
>
> > […]
> wrote:
> > […]
> in
> > […]
>
> Let's also pretend I didn't suggest to add a third argument to
> `array_filter()`;
>
> array_filter($arr, 'is_null', PHP_FILTER_INVERT);
>
> ;-)
>
>
> > […]
>
>
>
> --
> --
> Tjerk

---

## #68244 — Ryan McCue — 2013-07-20T04:16:48+00:00

URL: https://externals.io/message/68206#68244 · In reply to: #68216

Johannes Schlüter wrote:
> So, unfortunately no, language constructs have different semantics which
> we can't emulate inside function semantics (well ok, it is software, so
> it is thinkable .. but nobody,yet, came up with a robust patch which
> doesn't cause maintenance *and* performance penalty)

Is there a reason that echo/print couldn't be implemented as functions
with some sort of backwards compatibility layer? isset/etc make sense to
be language constructs, but I can't think of any reason echo/print need
to be.

-- 
Ryan McCue
<http://ryanmccue.info/

---

## #68245 — Sara Golemon — 2013-07-20T04:23:20+00:00

URL: https://externals.io/message/68206#68245 · In reply to: #68244

Lack of parenthesis and the fact that EVERY project out there takes
advantage of this affordance. So BC *nightmare*.




> Johannes Schlüter wrote:
> > […]
>
> Is there a reason that echo/print couldn't be implemented as functions
> with some sort of backwards compatibility layer? isset/etc make sense to
> be language constructs, but I can't think of any reason echo/print need
> to be.
>
> --
> Ryan McCue
> <http://ryanmccue.info/

---

## #68246 — Sara Golemon — 2013-07-20T04:25:04+00:00

URL: https://externals.io/message/68206#68246 · In reply to: #68245

Well, now... to be fair... You could make them functions and use the same
parser trick the backtick operator uses. to map the non-parenthesized
versions.... feels messy though.  I'd just hate to get stuck with a hacky
workaround like that for the long term.




> Lack of parenthesis and the fact that EVERY project out there takes
> advantage of this affordance. So BC *nightmare*.
>
>
> 
>
> > […]

---

## #68249 — Ryan McCue — 2013-07-20T04:28:18+00:00

URL: https://externals.io/message/68206#68249 · In reply to: #68246

Sara Golemon wrote:
> Well, now... to be fair... You could make them functions and use the same
> parser trick the backtick operator uses. to map the non-parenthesized
> versions.... feels messy though.  I'd just hate to get stuck with a hacky
> workaround like that for the long term.

That's what I meant by the "backwards compatibility layer". Not saying
we have to deprecate the use as a construct, but why can't we enable the
use as a function (and hence, callback, etc)? It feels less cleaner from
my point of view (userland).

-- 
Ryan McCue
<http://ryanmccue.info/

---

## #68252 — Sara Golemon — 2013-07-20T04:46:59+00:00

URL: https://externals.io/message/68206#68252 · In reply to: #68249

The danger I see there is the fact that we'd have two different
implementations of the same functionality (leading to potential code-rot),
and worse, it wouldn't be obvious from userspace when one implementation
would be used over the other.  (We'd know, but I mean for the average PHP
user, leading to weird unreproducible bug reports).

-Sara




> Sara Golemon wrote:
> > […]
>
> That's what I meant by the "backwards compatibility layer". Not saying
> we have to deprecate the use as a construct, but why can't we enable the
> use as a function (and hence, callback, etc)? It feels less cleaner from
> my point of view (userland).
>
> --
> Ryan McCue
> <http://ryanmccue.info/

---

## #68261 — Stas Malyshev — 2013-07-20T07:54:40+00:00

URL: https://externals.io/message/68206#68261 · In reply to: #68244

Hi!

> Is there a reason that echo/print couldn't be implemented as functions
> with some sort of backwards compatibility layer? isset/etc make sense to

Yes, the reason is not fixing what isn't broken :)
-- 
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227

---

## #68264 — Lester Caine — 2013-07-20T08:24:46+00:00

URL: https://externals.io/message/68206#68264 · In reply to: #68261

Stas Malyshev wrote:
> > […]

> Yes, the reason is not fixing what isn't broken:)

And perhaps introducing 'black holes' like the one created when <?= suddenly 
stopped working, then had to be repaired. If we were writing and supporting 
'flat' code where we know how everything works, things would be a lot easier, 
but many of us are using third party libraries and frameworks which tend to 
follow 'new practices' while some of us still stubbornly stick to old ones ;) 
The discussion on picking up a know version of a library is very relevant here, 
and would at least help to maintain consistency. Can one ensure that backwards 
compatibility does not become undefined in some circumstances and just adds to 
the workload ... and creates further havoc in user land?

There has to be a substantial reason to implementing change?

-- 
Lester Caine - G8HFL
-----------------------------
Contact - http://lsces.co.uk/wiki/?page=contact
L.S.Caine Electronic Services - http://lsces.co.uk
EnquirySolve - http://enquirysolve.com/
Model Engineers Digital Workshop - http://medw.co.uk
Rainbow Digital Media - http://rainbowdigitalmedia.co.uk

---

## #68266 — Arpad Ray — 2013-07-20T09:31:09+00:00

URL: https://externals.io/message/68206#68266 · In reply to: #68264

> Stas Malyshev wrote:
>
> > […]
>  Yes, the reason is not fixing what isn't broken:)
> > […]
>
> And perhaps introducing 'black holes' like the one created when <?=
> suddenly stopped working, then had to be repaired. If we were writing and
> supporting 'flat' code where we know how everything works, things would be
> a lot easier, but many of us are using third party libraries and frameworks
> which tend to follow 'new practices' while some of us still stubbornly
> stick to old ones ;) The discussion on picking up a know version of a
> library is very relevant here, and would at least help to maintain
> consistency. Can one ensure that backwards compatibility does not become
> undefined in some circumstances and just adds to the workload ... and
> creates further havoc in user land?
>
> There has to be a substantial reason to implementing change?
>
>
I agree this is a largely pointless can of worms, but here's my POC from
yesterday in case anyone wants to play with it:
https://gist.github.com/arraypad/6044439

Arpad

---

## #68269 — johannes@schlueters.de — 2013-07-20T11:37:39+00:00

URL: https://externals.io/message/68206#68269 · In reply to: #68249

> Sara Golemon wrote:
> > […]
> 
> That's what I meant by the "backwards compatibility layer". Not saying
> we have to deprecate the use as a construct, but why can't we enable the
> use as a function (and hence, callback, etc)? It feels less cleaner from
> my point of view (userland).

There again is a thing we can't emulate using functions: The operator
precedence when using () with echo is "special":

php > echo(0) || print(1);
11

alright, that's weird isn't it? Anyways let's try to emulate using a
function:

php > function echo_func($foo) {
php {     echo $foo;
php { }
php > echo_func(0) || print(1);
01

damn different result, ah, echo-func returns `NULL` maybe when we return
true?

php function echo_func1($foo) {
php {     echo $foo;
php {     return true;
php { }
php > echo_func1(0) || print(1);
0

still not. Reason is that currently in 

  echo(0) || print(1);

the expression (0) || print(1) is evaluated first and then the result is
passed to echo.

Maybe one might have designed it differently but more than 15 years into
the game it's hard.

johannes

---

## #68270 — johannes@schlueters.de — 2013-07-20T11:53:40+00:00

URL: https://externals.io/message/68206#68270 · In reply to: #68266

> I agree this is a largely pointless can of worms, but here's my POC from
> yesterday in case anyone wants to play with it:
> https://gist.github.com/arraypad/6044439

*If* we want to provide a simple function for easy printing for
callbacks and such it should not use the echo/print names. This messes
with debugging etc. (mind: function names listed in xdebug's output, gdb
breakpoint on zif_echo, our dtrace hooks will be misleading, ...)

johannes
