On 25/08/2024 14:35, Larry Garfield wrote:
My other concern is the list of supported expression types. I
understand how the implementation would naturally make all of those
syntactically valid, but it seems many of them, if not most, are
semantically nonsensical.
I tend to agree with Larry and John that the list of operators should be
restricted - we can always allow more in future, but restricting later
is much harder.
A few rules that seem logical to me:
1) The expression should be reasonably guaranteed to produce the same
type as the actual default.
- No casts
- No comparison operators, because they produce booleans from
non-boolean input
- No "<=>". Technically, it has an integer result, but it's rare to use
it as one, rather than a kind of three-value boolean
- No "instanceof"
- No "empty"
2) The expression should not have side effects (outside of exotic
operator overloads).
- No "include", "require", etc
- No "throw"
- No "print"
- Borderline, but I would also say no "clone"
3) The expression should be passing additional information into the
function, not pulling information out of it. The syntax shouldn't be a
way to write obfuscated reflection, or invert data flow from callee to
caller.
- No assignments.
- No ternaries with "default" on the left-hand side - "$foo ? $bar :
default" is acting on local knowledge, but "default ? $foo : $bar" is
acting on information the caller shouldn't know
- Same for "?:" and "??"
- No "match" with "default" as the condition or branch, for the same
reason. "match($foo) { $bar => default }" is fine, match(default) { ...
}" or "match($foo) { default => ... }" are not.
Note that these can be seen as aspects of the same rule: the aim of the
expression should be to transform the default value into another value
of the same type, not to pull it out and perform arbitrary operations
based on it.
I believe that leaves us with:
- Arithmetic operators: binary + - * / % **, unary + -
- Bitwise operators: & | ^ << >> ~
- Boolean operators: && || and or xor !
- Conditions with default on the RHS: $foo ? $bar : default, $foo ?:
default, $foo ?? default, match($foo) { $bar => default }
- Parentheses: (((default)))
Even then, I look at that list and see more problems than use cases. As
the RFC points out, library authors already worry about the maintenance
burden of named argument support, will they now also need to question
whether someone is relying on "default + 1" having some specific effect?
Maybe we should instead require justification for each addition:
- Bitwise | is nicely demonstrated in the RFC
- Bitwise & could probably be justified on similar grounds
- "$foo ? $bar : default" is discussed in the RFC
- The other "conditions with default on the RHS" in my shortlist above
fit the same basic use case
Beyond that, I'm struggling to think of meaningful uses: "whatever the
function sets as its default, do the opposite"; "whatever number the
function sets as default, raise it to the power of 3"; etc. Again, they
can easily be added in later versions, if a use case is pointed out.
Regards,
--
Rowan Tommins
[IMSoP]