Podcaster
Episoden
24.06.2022
1 Minute
PHP Internals News: Episode 103: Disjunctive Normal Form
(DNF) Types
Friday, June 24th 2022, 09:07 BST
London, UK
In this episode of "PHP Internals News" I talk with
George Peter Banyard (Website, Twitter, GitHub, GitLab)
about the "Disjunctive Normal Form Types" RFC that he has
proposed with Larry Garfield.
The RSS feed for this podcast is
https://derickrethans.nl/feed-phpinternalsnews.xml, you
can download this episode's MP3 file, and it's available
on Spotify and iTunes. There is a dedicated website:
https://phpinternals.news
Transcript
Derick Rethans 0:15
Hi, I'm Derick. Welcome to PHP internals news, a
podcast dedicated to explaining the latest
developments in the PHP language. This is episode
103. Today I'm talking with George Peter Banyard
again, this time about a disjunctive normal form
types RFC, or DNF, for short, which he's proposing
together with Larry Garfield. George Peter, would you
please introduce yourself?
George Peter Banyard 0:39
Hello, my name is George Peter Banyard, I work on PHP
paid part time, by the PHP foundation.
Derick Rethans 0:44
Just like last time, we are still got colleagues.
George Peter Banyard 0:46
Yes, we are indeed still call it.
Derick Rethans 0:48
What is this RFC about? What is it trying to solve?
George Peter Banyard 0:52
The problems of this RFC is to be able to mix
intersection and union types together. Last year,
when intersection types were added to PHP, they were
explicitly disallowed to be used with Union types.
Because: a) mental framework, b) implementation
complexity, because intersection types were already
complicated on their own, to try to get them to work
with Union types was kind of a big step. So it was
done in chunks. And this is the second part of the
chunk, being able to use it with Union types in a
specific way.
Derick Rethans 1:25
What is the specific way?
George Peter Banyard 1:27
The specific way is where the disjoint normal form
thing comes into play. So the joint normal form just
means it's a normalized form of the type, where it's
unions of intersections. The reason for that it helps
the engine be able to like handle all of the various
parts it needs to do, because at one point, it would
need to normalize the type anyway. And we currently
is just forced on to the developer because it makes
the implementation easier. And probably also the
source code, it's easier to read.
Derick Rethans 1:54
When you say, forcing it up on a developer to check
out you basically mean that PHP won't try to
normalize any types, but instead throws a compilation
error?
George Peter Banyard 2:05
Exactly. It's, it's the job of the developer to do
the normalization step. The normalization step is
pretty easy, because I don't expect people to do too
many stuff as intersection types. But as can always
be done as a future scope of like adding a
normalization step, then you get into the issues of
like, maybe not having deterministic code, because
normalization steps can take very, very long, and you
can't necessarily prove that it will terminate, which
is not a great situation to be in. Imagine just
having PHP not running at all, because it's stuck in
an infinite loop trying to normalize the format. It's
just like, oh, I can't compile
Derick Rethans 2:39
Would a potential type alias kind of syntax help with
that?
George Peter Banyard 2:44
Maybe, I'm not really sure. Actually reading like
research about it from computer scientists, in
functional programming languages, which is everything
is compiled on my head. And they have the whole thing
was like, well, they need to type type normalize, and
especially with type aliases, they haven't really
figured out a way yet. So I'm not sure how we are
going to figure out a way if experts and PhD students
and researchers haven't really figured out a way.
Derick Rethans 3:08
And is the reason for that mostly, because PHP,
resolves types while it is running code sometimes
because it has to overload classes, and then it might
find out it is an inherited class, for example?
George Peter Banyard 3:19
Yes, I think it's like this weird thing where might
maybe PHP has like kind of an advantage, because it
doesn't need to, like resolve all of the types at
once. And if you have a type alias, it's just oh, if
it's used, and you just need to resolve it, and then
try to figure it out. There's also the added
complexity of like, variance checks, because most
functional programming languages, they have variance
to some degree, but they don't have the whole
inheritance of like typical OOP languages have. It's
kind of a very strange field, the fact that yeah, PHP
is just like, well, we kind of do stuff at runtime,
and you don't necessarily need everything. And it
just works is like, well, we'll do. That's mainly the
reason why the dev needs to do the normalization
step, the form is done. It's also I think, the most
easiest to understand, it's just like, Oh, you have
this and this, or this group, or stuff, or this group
of stuff, or this thing, simple type. The other form
would be another normalized form would be conjunctive
normal form, which is a list of ANDs of ORs to just
have this thing, or X, like (A or B or C) and X and
(Y or Z), which I think is harder to understand.
Derick Rethans 4:26
What is the exact syntax then?
George Peter Banyard 4:28
So the exact syntax is, if you want to have an
intersection type was in a union type, you need to
like bracket it by parentheses. And then you have
like the normal pipe union operator and you can mix
it with single types, you can mix it with true, you
can mix it with false, which are literal types, which
now exist, or just normal, bool types.
Derick Rethans 4:48
The parenthesis is actually required. You don't rely
on operator precedence to make things work?
George Peter Banyard 4:53
Yes. Relying on operator precedence is terrible.
Derick Rethans 4:57
Yep, I agree.
George Peter Banyard 4:58
I'd say Oh, yeah, but I think I've heard this
argument on the list like a couple of times, it's
just, oh, yeah, but maths, like, has like, and as
priority over like, or, I mean, I did three years of
a maths degree and not gonna lie. Maths notation is
terrible for most of us. People don't even agree on
terminology. I'm just gonna say, let's, let's just do
better.
Derick Rethans 5:19
I agree. I mean, most coding standards for any sort
of variable for like conditions, will already require
parenthesis around multiple complex clauses anyway,
right? I mean, it's a sensible thing to do, just for
readability, in my opinion. So the RFC also talks
about a few syntax that you aren't allowed to do, and
that you have to normalize or deconstruct yourself,
what kinds of things are these?
George Peter Banyard 5:41
if you would want to have a type which has an
intersection of a class A with at least one other
class, so let's say X or Y, but you can always
convert it into DNF form, how this type would be, it
would be (A and X) or (A and Y). This seems to be the
more unusual case, I would imagine. One of the
motivating cases of DNF types is to do something like
Array or (Traversable and Countable). I don't really
see mixing and matching various different object
interfaces in differencing, the most useful user land
cases to be able to do Array or (Traversable and
Countable) so that you can use just count or seeing
something as an array, or you have like Traversable
and Countable and ArrayAccess. And it's just like,
Oh, here's an object, which kind of behaves like an
array.
Derick Rethans 6:32
I think there's currently another RFC just being
proposed, that extends iterator_to_array to multiple
types as well to accept more things. So that sort of
fits into this category of things to do with
iterables and traversals then I suppose.
George Peter Banyard 6:49
yeah
Derick Rethans 6:50
I'm hoping to talk to the author of that RFC as well.
At the moment where two and a half weeks or so before
a feature freeze, you now see a whole flurry of RFCs
while it was a bit quiet in the last few months. So
because you're adding to the type system, that's also
usually has consequences for variance rules, or
rather, how inheriting works with return types and
argument types, as well as property types. What do
DNF types mean for these variance checks?
George Peter Banyard 7:19
The variance is checks, kind of follow the similar
rules as before. So property types are easy. They are
invariant, so you can't change them. You can reorder
types, like was in your union if you want to. But
that was already the case with Union types
previously, because PHP will just check that, well,
the types match. So contravariant, you can always
restrict types, meaning you can either add
intersections, or you can remove unions, broadly
speaking. What you could do, for example, if you have
like A or B or C, you could do A and X as a subtype,
because you're restricting A to be of an extra, like
an extra interface.
Derick Rethans 8:06
So then you will have (A and X) or B or C.
George Peter Banyard 8:10
Yes. So that's one restriction. You can add how many
interfaces you want and do an intersection type, you
can add them on every type you can. On the other
side, you can just add like unions. So if for
contravariance, or like an an argument type, it's
like, well, I just want to return something new,
well, then you can add unions, but you can't add an
intersection to a type, you can only widen types of
arguments. So if your type is A or B or C, you can't
do A and B, and you can't do (A and X) or B or C,
because you're restricting the type. If your type
would be (A and X) or (B and Y) or (C and Z), then
you could lift the restriction to A or B or (C and Z)
because you loosening the requirements on on the type
that you're accepting.
Derick Rethans 8:55
To summarize this: argument types, you can always
widen; return types you can only restrict, and, and
property types you can't change at all. I
specifically wanted to summarize that because I
always find contravariance and covariance. These
names confuse me. So that's why I prefer to talk
about widening types and restricting types instead.
Because there are so close together for me. We spoke
a little bit about redundant types. What is this new
functionality do if you specify redundant types?
George Peter Banyard 9:30
Redundant types how they currently work in PHP are
done at compile time. And they do exact class matches
or constant class aliasing matches.
Derick Rethans 9:41
That will need an explanation.
George Peter Banyard 9:44
Class names and interface names in PHP are case
insensitive. So you can write a lower-case a or
upper-case A and it means the same class. If you
provide let's say lower-case a or upper-case A, the
engine realize this, this is the same class, so we'll
serve it on the type error. So PHP has use
statements, or use as. So these are compile time
aliases. If you define a class A, and then you say
use A as B. So B is a compile time alias of A. And
then you do a type which has A or B, PHP already
knows these things refer to the same class. So it
will raise a compile time error.
Derick Rethans 10:25
These use aliases are per file only, right?
George Peter Banyard 10:28
Yes, that's usually to do with if you import traits
or like a namespaces. And you get conflicting class
names. That's how you handle it about. PHP has also
this feature, which you can do this at runtime, using
the function called class_alias. Now, obviously,
compile time checks are done at compile time. So it
doesn't know at runtime that you aliasing these
classes or using this name as an alias. So then PHP
won't complain.
Derick Rethans 10:53
But will don't complain during runtime.
George Peter Banyard 10:56
No.
Derick Rethans 10:56
You really just wanted to shoot yourself in the foot,
we'll let you do this.
George Peter Banyard 11:00
Yet, during this at runtime, just as like a whole
layer of time, because it's not it's not really
useful. Basically, what it means that PHP won't
guarantee you the type is minimal. I.e. you might
have redundant types, but it will just try to tell
you, it's like oh, the- these are exactly the same
types. And I know these are the same types, you
probably do get mistake. So if it can determine this
at compile time, it will tell you.
Derick Rethans 11:23
The variance is still checked when you're passing in
things.
George Peter Banyard 11:26
Yes, so variance is checked on inheritance. When the
class is inherited and compiled, because it needs to
load the parent class, it will then check that it's
built properly, and otherwise it will raise an error,
that's fine. But just checking that the types is
minimal is not possible. A) because inheritance, you
don't know how it works, because it will only do the
checks on basically on the name of the strings, it
will do like compare strings of class names. And if
it doesn't know the class name, or if it or if it
needs to do some inheritance, it just won't do an
instance of check. They just ignore that. It's just
like, well, maybe it is maybe it's not I don't know.
And that's fine.
Derick Rethans 12:08
Of course, if you pass in a wrong type at runtime,
then it will still get rejected during runtime
anyway.
George Peter Banyard 12:14
Yes, that hasn't changed.
Derick Rethans 12:16
The only thing that you might end up in a situation
where you don't get warned during compile time
whether type is redundant.
George Peter Banyard 12:23
Yes. So that's the behaviour we currently are the
behaviour is added. So, it will check that two
intersection types within the union are identical
using the same class stuff. So for example, if you
have class A, and you say use a as B, and then you
have a type which is (A and X) or (B and X), it will
tell you: Okay, these classes are the same. The check
it adds now also it will check that you don't have a
more restrictive type with a wider type. So if your
type is T or (T and X), because T is wider than T and
X, it will error at compile time, it'll tell you
well, T is less restrictive than T and X. So the T
and X type is redundant.
Derick Rethans 13:11
Okay, so nothing strange. Basically, what you expect
to happen will happen. And PHP does its best telling
you at compile time whether you've done something
wrong or not.
George Peter Banyard 13:22
Yes.
Derick Rethans 13:24
I think we've spoken mostly about the functionality
itself and types. I'm a little bit interested in
whether you encountered some interesting things while
implementing this feature.
George Peter Banyard 13:33
This feature basically, was a bit in limbo for the
implementation, because I was waiting on a change to
make Iterable, a compile time alias of Array or
Traversable, which shouldn't affect userland. Because
previously, all of the checks needed to cater to if
you get Iterable, then you need to check for the
variance. Has it Array , has it a Traversable type,
does this accept? Is it why the is it more
restrictive, it's identical. It's just this weird
edge case, which makes the variance code harder.
Moving this to a compile time alias, where now it
just uses the standard, a standard union type in some
sense, just makes a lot of the variance checks
already streamlined and simpler. And because this is
simpler, in some sense, was DNF types. When you hit
the intersection, you need to recurse one step to
check the variance. This helps. This is also kind of
why DNF types are enforced like as like the structure
on the dev because otherwise, you could potentially
get into the whole like, oh, infinite recursion if
you do like very nested types, because it's just
like, oh, you hit one nested type and so, oh okay,
now I'm again in unnecessary time and then you
recurse again and then you recurse again, and so
that's all you get into the thing: Oh you need to
normalize the type. The variance check is: Can you
see if it's a union type is the first type a sub list
So a list of intersection types, okay, is it
balanced? And then just recall the same function in
some sense, like, check the types for variance, is
this correct? Okay, move to the next type back into
the Union and everything. So the implementation is
conceptually simple, because all of the
implementation details already exist. And all the
everything hard has already been done. Now, it's just
like, in some sense, it was extracting it into its
own function, and then like recurse into it, and not
forget to update opcache properly.
Derick Rethans 15:31
You mentioned that in order to make the DNF types
work, you were waiting on this Array or Iterable or
Traversable kind of type. Is this also type people
can use it and userland? Or is it internal only?
George Peter Banyard 15:44
It is the standard Iterable type that you can already
use. So currently, PHP considered Iterable, a full
type in some sense. And what the this implementation
change basically makes it Iterable into ... compile
time alias of Array or Traversable. Iterable exists
since, PHP, 7.1, I think. Can still use it,
reflection should still be fine if you use it as a
single type.
Derick Rethans 16:08
So to change there is more, instead of: if you
encounter Iterable, we check for both Array and
Traversable. Then, instead of making the check every
time you look at Iterable is already part of the type
system, so you don't have to make the check every
time.
George Peter Banyard 16:23
Exactly, you basically move when it's being
transformed in some sense. Now it has some
repercussion on other parts, which needed to be taken
care of, which is probably why it was in limbo for 10
months. I had already done the implementation of DNF
types, basically, working on my local copy of that
branch. It's just like: Okay, this got merged, nice,
I can now open the PR onto PHP SRC. So I didn't wait
for it to land until start working on it.
Derick Rethans 16:50
Things like that also often affect reflection,
because you're adding more complex types to the type
system. So what kind of changes does that make to
PHP's reflection system? And does this end up
breaking backwards compatibility?
George Peter Banyard 17:04
So in theory, no, it doesn't. How the reflection API
works around the type system is that most method
calls will turn a reflection type interface,
ReflectionNameType, ReflectionUnionType, and
ReflectionIntersectionType, are all instances of a
ReflectionType. And methods if you would call on the
list. So on a union type, the type it would return if
you get like getTypes is a ReflectionType. The type
system and how the reflection idea was designed,
there is no BC break. How the standard was working,
it's like, Oh, if you had like a union type, or an
intersection type, if you call the getList or
getListOfTypes, or getTypes, I don't remember exactly
what the method name is actually called, you will
always get an array of reflection name types, because
you can only have like one level of list in some
sense. However, now, if your top type is a union
type, then if you get getTypes, you might get an
array of ReflectionNameTypes with
ReflectionIntersectionTypes. So that's the case that
you now need to cater to. So if you get another
ReflectionIntersectionType in between. There, you
could only have ReflectionNameTypes, there was no
nesting, whereas now if you have a union type, one of
the types that you get back from the getTypes method
in the array will be a ReflectionIntersectionType.
Technically, all of the types of the part of the
reflection type, so it's an array of reflection types
that you get. How it worked before is that you didn't
need to care about this distinction between: Oh, it
returns a ReflectionType and a ReflectionNameType
because well, it only return a ReflectionNameType.
But now this is not the case. So you now need to
cater to that that oh, you might have nesting. Which
kind of boils down to like if in the future, we
decide to like have oh, you can nest union types in
an intersection type, then the getTypes method might
return a union type with other name types.
Derick Rethans 19:03
You just need to make sure that you check for more
than just one thing that it previously would have
done. You can't assume not everything is a
ReflectionType any more. It could also be
ReflecionIntersectionType.
George Peter Banyard 19:18
Yes, exactly.
Derick Rethans 19:20
I think that sort of what's in the RFC, is there any
future scope?
George Peter Banyard 19:25
I mean, the future scope is type alias. As usual.
Everything I feel when you talk about the type
system, it's like type aliases. At one point when
your types gets very complicated. It would be nice to
just be able to refer this as a as a named type in
some sense, instead of needing to retype every time
the whole union slash intersection of it. Hopefully
we can get this running for 8.3. We are starting to
get kind of complicated types. It would be nice being
able to have this feature. The other obvious future
scope in some sense, who knows if it's actually
desirable is to allow either having conjunctive
normal form so you can have like a list of ANDs or
ORs
Derick Rethans 20:05
You call these conjunctive normal forms?
George Peter Banyard 20:08
Yes. Or just a type, which is not normalized. Not
sure if it's really desirable to have this feature,
because then you get into the whole thing of, if PHP
doesn't, either PHP doesn't know how to like
normalize it, or it's not in the best form, and then
you get into like, very long compilation units or
just checking. It's like, okay, does it respect the
type? Does it do all of the instance of checks? And
I'm not sure if it's super desirable.
Derick Rethans 20:38
So it could be considered future scope. But from what
I gather from you, you don't actually know what it is
actually a desirable thing to add to the language?
George Peter Banyard 20:46
Yes.
Derick Rethans 20:47
Okay, George, thank you for taking the time this
morning to talk about this new DNF types RFC.
George Peter Banyard 20:54
Thank you for having me. As always.
Derick Rethans 20:59
Thank you for listening to this installment of PHP
internals news, a podcast dedicated to demystifying
the development of the PHP language. I maintain a
Patreon account for supporters of this podcast as
well as the Xdebug debugging tool. You can sign up
for Patreon at https://drck.me/patreon. If you have
comments or suggestions, feel free to email them to
derick@phpinternals.news. Thank you for listening,
and I'll see you next time.
Show Notes
RFC: Disjunctive Normal Form Types
Credits
Music: Chipper Doodle v2 — Kevin MacLeod
(incompetech.com) — Creative Commons: By
Attribution 3.0
Mehr
02.06.2022
1 Minute
PHP Internals News: Episode 102: Add True Type
Thursday, June 2nd 2022, 09:06 BST
London, UK
In this episode of "PHP Internals News" I talk with
George Peter Banyard (Website, Twitter, GitHub, GitLab)
about the "Add True Type" RFC that he has proposed.
The RSS feed for this podcast is
https://derickrethans.nl/feed-phpinternalsnews.xml, you
can download this episode's MP3 file, and it's available
on Spotify and iTunes. There is a dedicated website:
https://phpinternals.news
Transcript
Derick Rethans 0:00
Hi I'm Derick. Welcome to PHP internals news, the
podcast dedicated to explaining the latest
developments in the PHP language. This is episode
102. Today I'm talking with George Peter Banyard
about the Add True Type RFC that he's proposing.
Hello George Peter, would you please introduce
yourself?
George Peter Banyard 0:33
Hello, my name is George Peter Banyard, I work part
time for the PHP Foundation. And I work on the
documentation.
Derick Rethans 0:40
Very well. We're co workers really aren't we?
George Peter Banyard 0:43
Yes, indeed, we all co workers.
Derick Rethans 0:45
Excellent. We spoke in the past about related RFCs. I
remember, which one was that again?
George Peter Banyard 0:51
Making null and false stand alone types
Derick Rethans 0:53
That's the one I was thinking of him. But what is
this RFC about?
George Peter Banyard 0:56
So this RFC is about adding true as a single type. So
we have false, which is one part of the Boolean type,
but we don't have true. Now the reasons for that are
a bit like historical in some sense, although it's
only from PHP 8.0. So talking about something
historical. When it's only a year ago, it's a bit
weird. The main reason was that like PHP has many
internal functions, which return false on failure. So
that was a reason to include it in the Union types
RFC, so that we could probably document these types
because I know it would be like, string and Boolean
when it could only return false and never true. So
which is a bit pointless and misleading, so that was
the point of adding false. And this statement didn't
apply to true for the most part. With PHP 8, we did a
lot of warning to value error promotions, or type
error promotions, and a lot of cases where a lot of
functions which used to return false, stopped
returning false, and they would throw an exception
instead. These functions now always return true, but
we can't type them as true because we don't have it,
and have so they are typed as bool, which is kind of
also misleading in the same sense, with the union
type is like, well, it only returns false. So no
point using the boolean, but these functions always
return true. But if you look at the type signature,
you can see like, well, I need to cater to the case
where the returns true and when returns false.
Derick Rethans 2:19
Do they return true or throw an exception?
George Peter Banyard 2:22
Yeah, so they either return true, or they either
throw an exception. If you would design these
functions from scratch, you would make them void, but
legacy... and we did, I know it was like PHP 8.0, we
did change a couple of functions from true to void.
But then you get into these weird shenanigans where
like, if you use the return value of the function in
a in an if statement, null gets because in PHP, any
function does return a value, even a void function,
which returns null. Null gets coerced to false. So
you now get like, basically a BC break, which you
can't really? Yeah, we did a bit and then probably we
sort of, it's probably a bad idea. That's also the
point of like, making choices, things that are static
analysers can be like, more informants being like,
Okay, your if statement is kind of pointless here.
Derick Rethans 3:06
Yeah, you don't want to end up breaking BC. Now, we
already had false and bool, you're adding true to
this. How does that work with Union types? Can you
make a union type of true or false?
George Peter Banyard 3:18
No. So there are two reasons mainly. A. true and
false is the same as like boolean, which is like just
use Boolean in this case. But you can say, well, it's
more specific, so just allow it. So that's would be
reasonable. But the problem is, false has different
semantics than boolean. False does not coerce values.
So it only accepts false as a literal value. Whereas
boolean, if you're not in strict type, which is a lot
of code, it will cause values like zero to false one,
or any other integers to true. It will coerce every
other integer to true, like the true type follows the
behaviour of false of being a value type. So it only
accepts true, you would get into this weird
distinction of does true or false, mean exactly true
or false? Or do you get the same behaviour as using
the boolean type?
Derick Rethans 4:07
So I would say that true or false would than be more
restrictive than bool.
George Peter Banyard 4:12
Exactly, which is a bit of a problem, because PHP
internally has true and false and separate types,
which also makes the implementation of this RFC
extremely easy, because PHP already makes the
distinction of them. But at the same time, the
boolean type is just a union of the bitmask of true
and false. You can't really distinguish between the
types, true or false, or the boolean type within the
type system. Currently just does it by checking if it
only has one then it can do like two checks.
Specifically, you would need to add like an extra
flag. I mean, it's doable, but it's just like, Well,
who knows which semantics we want? Therefore, just
leave it for future discussion because I'm not very
keen on it to be fair.
Derick Rethans 4:55
True or false are really only useful for return
values and not so much for arguments types, because
if you have an argument that that always must be
true, then it's kind of pointless to have of course.
George Peter Banyard 5:05
Same as like it was with the null type RFC. Although
there might be one case where PHP internal functions
might change the value to true for an argument, I can
maybe two types, would be like with the define
function, this thing being like case insensitive or
case sensitive, I don't remember what the parameter
actually; could actually either be false or true,
because at the moment, I think emits a notice, things
do like the this thing is not supported, therefore
the values what was ignored. But we could conceivably
see that in PHP 9, we would actually implement this
as a proper like: Okay, this only accepts true, yes,
this argument is pointless, but it's in the middle of
the function signature, so you can't really move it.
The spl_register_overload function has like as its
second argument, the throw on error or not, which
since PHP 8 only accepts true, but it's in the middle
of the function. The last argument is still very
useful. It's prepend, instead of append the
autoloader, I think, or might be the other way
around, check the docs. Since PHP 8, this only
accepts true. So if you pass in false, it will emit a
notice and saying you'd like this argument has just
been ignored. So whatever. But we can't really remove
the argument. Because well, it's, if you use the
third argument, as with positional arguments, then
you would change like the signature and you would
break it. Now, we don't have a way to enforce in PHP
to use named arguments, because that would be a
solution. It's just like, well, if you want to set
this argument, you need to use named arguments, but
we can't do that. Otherwise, then creating a new
function, which has an alias, which is also kind of
terrible. That would be one of the maybe only cases
where you would actually get like true as a as an
argument
Derick Rethans 6:39
is that now currently bool? And there's a specific
check for it?
George Peter Banyard 6:42
It's currently bool, and if you pass in false
enrolment, like a warning, or notice.
Derick Rethans 6:47
How would inheritance work? As return types, you can
always make them smaller, right? More restrictive.
George Peter Banyard 6:53
Yes, that's also the thing. But that already exists
in some sense a problem of. Like if you go from
boolean to false, you're already restricting the
type. And that problem existed, even before the
restricting, well allowing false as a stand-alone
type if you had like, as a union, because you could
always say like, I don't know. That problem already
existed with Union types. Because you could have
something like overturn an array or bool and then you
change it to either an array or false. And then if
you try to return like zero, then you will get like a
coercion problem. So the same problem applies with
true, because it only affects return values. And like
you control the code within a function compared to
like how you pass it, that's less of an issue. It
applies also, with argument types where you can go
from true to like boolean, or true and like a union
type.
Derick Rethans 7:44
So there's nothing surprising here. I see that the
RFC also talks a little bit about future scope. Can
you tell a bit more about that?
George Peter Banyard 7:53
True and false are part of what are called value
types, they are a specific value within the type. One
possible future scope would be to expand value types
to all possible types. So that you could say, oh,
this function returns one, two or three.
Derick Rethans 8:08
Would you not rather use an enum for that?
George Peter Banyard 8:09
Exactly. That's the point I was going to make is that
enums serve this purpose, in my opinion. And as a
type purist, ideally, I would have preferred that we
didn't have to enforce because the code, it kind of
goes against the grain in this sense.
Derick Rethans 8:23
We've had it for 25 years, booleans.
George Peter Banyard 8:26
Yes, right. But boolean is its own type, in some
sense, which you could say is a special enum. Enums
are types. But we have false, and not having true is
just so weird to me. It's like, oh, you've got this
thing, but you don't have this other thing. And there
are loads of cases where functions return true, or
due to legacy reasons and to preserve BC, and PHP 8
promoted a bunch of warnings to to error. So now
you've got functions which used to return false,
don't return false any more. And they only return
true. Now, some of the famous examples are probably
like array_sort of, like actually, the sorting array
functions, return true for basically all of PHP 7. I
think there was something changed in PHP 7, probably
was the engine or something like that, that they
stopped returning false, which is strange. And I've
made the discovery somewhat recently, I'm like, this
is so pointless, because you see loads of loads of
code checking like that the return value of the sort
function is correct.
Derick Rethans 9:20
It's also that most of the sort functions actually
sort by reference instead of returning the sorted
array, which I can understand as a performance reason
to do but...
George Peter Banyard 9:29
it's not very functional. You modify stuff in place
and like passing it around. And because yeah, I think
the initial thing was that like, well do it would
return a false or true because sometimes it could,
the sort could fail.
Derick Rethans 9:42
I don't understand how a sort could failure, but
there we go.
George Peter Banyard 9:46
I mean, I suppose if you have like incomparable
values within the array like that somewhat logical, I
suppose.
Derick Rethans 9:53
Was there anything else in future scope?
George Peter Banyard 9:56
One of the future scope, I feel was everything type
related. It's like type aliases, because when you
start making more complicated types, having a way to
type alias, it is probably nice. Don't think we'll
get this for PHP 8.2. I don't think we any of us had
the time to work on it.
Derick Rethans 10:11
Well, we only have a month left anyway.
George Peter Banyard 10:13
Yeah. And I mean, I'll probably be back on here. I'm
trying to get DNF types working, but...
Derick Rethans 10:19
Can you explain that these are?
George Peter Banyard 10:20
Disjoint normal form types?
Derick Rethans 10:22
That did not help.
George Peter Banyard 10:24
But it's the being able to combine union types with
intersection types together,
Derick Rethans 10:28
I can understand that doing that is kind of
complicated. You also need to sort of come up with a
with a language to define them almost right? I mean,
you then get the argument, are you going to require a
parenthesis around things?
George Peter Banyard 10:38
I'm requiring parentheses. People have told me the
argument of like: Yeah, but in maths like and takes
priority, it's just like, have you seen
mathematicians, mathematicians don't agree on
notation, and it's terrible, or they call stuff and
the different they call it something is like, oh,
sometimes a ring is commutative, and sometimes it's
not. Don't follow mathematicians, don't follow
mathematician,
Derick Rethans 10:57
Type aliases is something that would only apply to
single files. See, that's what you're suggesting. And
then there's exported type definitions, which I guess
could be autoloaded at some point; would be nice to
have, I guess.
George Peter Banyard 11:09
I think that's the trouble just like defining the
semantics. Type aliases within a file are nice, but
they're not very useful. Most of the time, you would
want to export the type. For example, if you say: Oh,
I accept, I don't know, something which looks like an
array, which is like an array and like Traversable,
and ArrayAccess or something. I'm sure, it's nice to
have it in your own file. But like, if you use it
around a project, and you need to redefine the type,
every single file kind of defeats the purpose.
Derick Rethans 11:35
It's kind of tricky to do with type definitions,
because you sort of need to make sure that there are
available and maybe can be autoloaded, just like
classes can be right. And that makes things tricky.
Because having a type definition and just three lines
in a file, is kind of annoying, but I guess that is
sort of necessary to do the kind of thing in a PHP
ish way.
George Peter Banyard 11:55
Yeah, we talked about it with Ilija because he he was
on about it. And I was like: Well, ideally, you would
want the separate autoload of types. That's how I
initially conceived it, it's like having a different
autoloading for types. But then the problem is, is
like if anytime you hit a class, like in an argument,
if you autoload the type first, it will go through
all of the type definitions. And if, okay, at the
moment, that wouldn't be there wouldn't be much. But
if you go into like importing 30 composer projects,
or libraries, which are define their own types, it
will go through all of those first, before going to
the classes autoloaded, and trying to find it then,
which is not ideal. Yeah, it's going to be a tricky
problem. It's either you merge these symbols
together. But then the class table is not always a
class. And sometimes you can't do new type. Like I
said, tricky problems.
Derick Rethans 12:43
Yeah, that's a tricky problem, but an interesting
one.
George Peter Banyard 12:47
Yeah.
Derick Rethans 12:47
So that's future scope then.
George Peter Banyard 12:50
Exactly. That is future scope.
Derick Rethans 12:52
Do you have anything else to add?
George Peter Banyard 12:54
Um, no, not really. I think I've said all I have to
say it's pretty straightforward. Should be
uncontroversial, hopefully.
Derick Rethans 13:02
It currently looks like it's 20 for, and zero again.
So I guess it will pass.
George Peter Banyard 13:07
Brilliant.
Derick Rethans 13:08
Who said that, that if your RFC ends up passing
unanimously, it is too boring?
George Peter Banyard 13:13
Nikita.
Derick Rethans 13:14
Which is not incorrect.
George Peter Banyard 13:16
It is not incorrect. But I mean, at the beginning,
because I was like: Well, this is pretty
straightforward. So I wrote the RFC, it was tiny. And
I put it on to the list and people was like: Yeah,
but what's the motivation for? I understand for
adding false, because they already exist. But what's
the motivation for adding a new type, and I was like,
I now need to go back to the drawing board and write
more. To be fair, that was a smart, because I then
discovered the whole issue about true and false. That
false is just a value type and doesn't do coercions.
And it's like, okay, how do you handle the semantics
and everything?
Derick Rethans 13:46
I'm glad to hear it. Then all I have to say thank you
for taking the time today to talk about this new RFC.
George Peter Banyard 13:53
Thank you for having me as usual.
Derick Rethans 13:59
Thank you for listening to this installment of PHP
internals news, a podcast dedicated to demystifying
the development of the PHP language. I maintain a
Patreon account for supporters of this podcast as
well as the Xdebug debugging tool. You can sign up
for Patreon at https://drck.me/patreon. If you have
comments or suggestions, feel free to email them to
derick@php internals.news. Thank you for listening,
and I'll see you next time.
Show Notes
RFC: Add True Type
RFC: Allow Null and False as Standalone Types
Credits
Music: Chipper Doodle v2 — Kevin MacLeod
(incompetech.com) — Creative Commons: By
Attribution 3.0
Mehr
19.05.2022
1 Minute
PHP Internals News: Episode 101: More Partially Supported
Callable Deprecations
Thursday, May 19th 2022, 09:05 BST
London, UK
In this episode of "PHP Internals News" I talk with
Juliette Reinders Folmer (Website, Twitter, GitHub) about
the "More Partially Supported Callable Deprecations" RFC
that she has proposed.
The RSS feed for this podcast is
https://derickrethans.nl/feed-phpinternalsnews.xml, you
can download this episode's MP3 file, and it's available
on Spotify and iTunes. There is a dedicated website:
https://phpinternals.news
Transcript
Derick Rethans 0:14
Hi, I'm Derick. Welcome to PHP internals news, the
podcast dedicated to explaining the latest
developments in the PHP language. This is episode
101. Today I'm talking with Juliette Reinders Folmer,
about the Expand Deprecation Notice Scope for
Partially supported Callables RFC that she's
proposing. That's quite a mouthful. I think you
should shorten the title. Juliette, would you please
introduce yourself?
Juliette Reinders Folmer 0:37
You're starting with the hardest questions, because
introducing myself is something I never know how to
do. So let's just say I'm a PHP developer and I work
in open source, nearly all the time.
Derick Rethans 0:50
Mostly related to WordPress as far as I understand?
Juliette Reinders Folmer 0:52
Nope, mostly related to actually CLI tools. Things
like PHP Unit polyfills. Things like PHP Code
Sniffer, PHP parallel Lint. I spend the majority of
my time on CLI tools, and only a small portion of my
time consulting on the things for WordPress, like
keeping that cross version compatible.
Derick Rethans 1:12
All right, very well. I actually did not know that.
So I learned something new already.
Juliette Reinders Folmer 1:16
Yeah, but it's nice. You give me the chance now to
correct that image. Because I notice a lot of people
see me in within the PHP world as the voice of
WordPress and vice versa, by the way in WordPress
world to see me as far as PHP. And in reality, I do
completely different things. There is a perception
bias there somewhere and which has slipped in.
Derick Rethans 1:38
It's good to clear that up then.
Juliette Reinders Folmer 1:39
Yeah, thank you.
Derick Rethans 1:40
Let's have a chat about the RFC itself then. What is
the problem that is RFC is trying to solve?
Juliette Reinders Folmer 1:46
There was an RFC or 8.2 which has already been
approved in October, which deprecates partially
supported callables. Now for those people listening
who do not know enough about that RFC, partially
supported callables are callables which you can call
via a function like call_user_func that which you
can't assign to variable and then call as a variable.
Sometimes you can call them just by using the syntax
which you used for defining the callable, so not as
variable but as the actual literal.
Derick Rethans 2:20
And as an example here, that is, for example, static
colon colon function name, for example.
Juliette Reinders Folmer 2:26
Absolutely, yeah.
Derick Rethans 2:27
Which you can use with call_user_func by having two
array elements. You can call it with literal syntax,
but you can't assign it to a variable and then call
it. Do I get that, right?
Juliette Reinders Folmer 2:36
Absolutely. That's it. There's eight of those. And
basically, the original RFC from Nikita proposed to
deprecate support for them in 8.2, add deprecation
notices and remove support for them altogether in PHP
nine. And the original RFC explicitly excluded two
particular things from those deprecation notices.
That's the callable type and using the syntaxes in
combination with the is_callable function, where
you're checking if the syntax is callable. The
argument used in the original RFC was to keep those
side effect free. The problem with this is that with
the callable type, this means you go from absolutely
no notice or nothing, to a fatal error in PHP 9.
Everything works, and you're not getting any
notification. But in PHP 9, its fatal error at the
moment that callable is being passed to a function.
Derick Rethans 3:31
This is the callable type in function declarations.
Juliette Reinders Folmer 3:33
Yeah, absolutely. And with is_callable, I discovered
a pattern in my wanderings across the world where
people use the syntax in is_callable, but then use it
in a literal call. So not using call_user_func, not
using a variable to call it, but it's callable static
double colon method name, and then called static
double colon method name as literal. And that pattern
basically, for valid calls would mean that that
function would no longer be called in PHP 9 without
any notification whatsoever.
Derick Rethans 4:13
So it's a silent change that you can't detect at all.
Juliette Reinders Folmer 4:17
Yeah, which to me sounded dangerous. I started asking
some questions about that. But six weeks ago, the
conclusion was, well, maybe this should be changed.
But as this was explicit in the original RFC, we
can't just change it. We need to have a new RFC to
basically amend the original RFC and remove the
exception for these two situations and allow them to
throw deprecation notices.
Derick Rethans 4:44
What are you proposing to change with this RFC than?
Juliette Reinders Folmer 4:47
What this RFC is proposing is simply to remove the
exception that the callable type and is_callable are
not throwing a deprecation notice. This RFC is
proposing that they should throw a deprecation
notice, so that more of these type situations can be
discovered in time for PHP 9 to prevent users getting
fatal errors.
Derick Rethans 5:08
Now, of course, we have no idea when PHP nine is
actually showing up, but I don't think it will be
this year. Well, I know it won't be this year, and it
certainly won't be be next year neither, I think.
Juliette Reinders Folmer 5:17
That's all the same. I mean, it makes there'll be
two, three years ahead, but it doesn't really make
sense to have the main deprecation in 8.2 and then
have the additional deprecation in 8.4 or something.
Derick Rethans 5:29
Absolutely.
Juliette Reinders Folmer 5:30
It's a lot more logical to have it all in in the same
version. Because it's all related. It's basically the
same thing without the exception for callable type.
And is_callable.
Derick Rethans 5:42
Although there is no current application, would this
be able to be found if you had like a comprehensive
test suite?
Juliette Reinders Folmer 5:48
Yes and no. Yes, you can find this with a test suite.
But one, you're presuming that there are tests. Two,
that the tests covered the effected code with enough
path coverage. Three, imagine a test you've written
yourself at some point in the past where which
affected callables, you might have, you know, a data
provider where you say: Okay, valid callable
function, which you've mocked or, you know, closure,
which you've put in and second, this function does
not exist. Okay, so now you're testing this function,
which at some point in its logic has a callable, and
expects that type to receive that type. But are you
actually testing with the specific deprecated
partially supported callables? Even if you have a
test, and the test covers the affected code, if you
do not test with one of these eight syntaxes, which
has been deprecated, you still cannot detect it. And
then, four, you still need to make sure that the
tests are routinely run, and in open source, that's
generally not a problem. Most open source projects,
use GitHub actions by now to run the tests
automatically on every pull request, etc. But, have
the tests been turned on to actually run against PHP
8.2. Are the tests run against pull requests? I mean,
there are still plenty of projects, which don't do
that kind of thing. Yes, you can detect it with a
good test suite. But there's a lot of caveats when
you will not detect it. And more importantly, you
will not be able to detect it until PHP 9.
Derick Rethans 7:23
Yes, when your code and stops behaving as you were
expecting it to be.
Juliette Reinders Folmer 7:28
Yeah, because in 8.2, you're gonna get deprecation
notices for everything else, but these two
situations. But not in 8.2, not in 8.3, not in 8.4,
and then whatever eights we're gonna get until nine,
you will not be able to detect without deprecation
notices, until PHP 9 actually removes support for
these partials deprecated callables. Yes, but no.
Derick Rethans 7:53
We already touched a little bit on how you found out
for the need for this RFC or for changing behaviours.
But as people have stated in the past, adding
deprecation notices is a BC break. That's a subject
that we will leave for some other time because I
don't necessarily believe that. But would, the
changes in your RFC not add more backwards
compatibility issues?
Juliette Reinders Folmer 8:14
The plain and simple the backward compatibility break
is in the original RFC. That's where the deprecation
is happening. This RFC just makes it clearer where
the BC break is going to be in PHP 9. It's not PHP
8.2, which has a backward compatibility break. It's
PHP 9 which will have to backward compatibility
break. Yes, I've heard all those arguments, people
saying deprecation notes are BC break, no they're
not. But they are annoying. And Action List, to for
everything that needs to be fixed before 9. Given big
enough projects, you cannot say: Okay, I'm gonna do
this at the last moment, just before 9 comes out. It
literally means 10 months of the year I for one am
working on getting rid of deprecation notices in
project to prepare them all to be ready for PHP 9
when PHP 9 comes round.
Derick Rethans 9:06
But it's still better to have them than to not,.and
then you code starts breaking right? Because that is
exactly why you're proposing this RFC as far as I
understand.
Juliette Reinders Folmer 9:16
Yes, absolutely. I mean, I'm always very grateful for
deprecation notices, but it would be nice if we had
fewer changes, which would cost them, for a year or
two, so I can actually catch my breath again.
Derick Rethans 9:28
I think PHP 8.2 will have fewer of these changes in
there. There will still be some of course.
Juliette Reinders Folmer 9:35
Well, I mean, this one is one deprecation. And then
we have the deprecated Dynamic Properties and that
one is already giving me headaches before I can
actually start changing it in a lot of projects. I'm
not joking, that one really is going to cause a
shitload of problems.
Derick Rethans 9:51
It's definitely for products have been going on for
so long, where dynamic properties are used all over
the place. And I see that in my own code as well. I
just noticed this morning does actually breaks
Xdebug.
Juliette Reinders Folmer 10:03
I know it's currently breaking mockery, we're gonna
have to have a discussion how to fix that or whether
or not to fix it. If Mockery is broken, that means
all your tests are broken. So the test tooling needs
to be fixed first.
Derick Rethans 10:18
That's always the case, if you work with CLI tools
that make people run code on newer PHP versions,
that's always a group of tools that needs to be
upgraded first, which is your sniffers, your static
analysis, your debugger still will always need to go
first.
Juliette Reinders Folmer 10:27
Which is why I look at things a lot earlier, probably
then the majority of people. I mean, I see him huge
difference between the open source and closed source
community. For open source, I started looking at it
well, I've been looking at 8.2 since the beginning.
And I started running tests for all the CLI tools. As
soon as 8.1 comes out, 8.2 gets added to the matrix
for running in continuous integration. And then for
applications, it gets added like in you know, once
alpha 1-2-3 has come out. For the extensions, it gets
added in September once the first RFC gets added. And
all of them are trying to get ready before the
release of 8.1 or 8.2 in this case, because you do
not know as an open source maintainer, what version
people are going to run your code on. And you can say
IP, you can manage that via Composer, no you can't.
Sorry, you can only do that if your users are
actually installing via Composer. If your users are
downloading a zip file, and uploading it to a web
host via FTP, there's literally no way you can
control whether they're running on 8.0, or 8.1,
except for maybe during check: You cannot run on 8.1
yet.
Derick Rethans 11:52
Upgrading software with version support is an issue
that's been going on for 40 years and will go on for
at least another 40 more. This is not a problem that
we can solve easily.
Juliette Reinders Folmer 12:03
But what I see there is like the closed source
community is like, oh, yeah, but you know, by the
time I want to upgrade my server to 8.1, or 8.2, I
just run Rector and all will be fine. And I'm like,
yeah, sorry, that does not work for open source. We
need cross version compatible with multiple versions.
And I try to keep that range of version small for the
project, I initiate, I don't always have control over
it. If for instance, one of the projects I maintain
is Requests. And that's a project which does HTTP
requests. It's used by WordPress, it cannot be let go
of the minimum of 5, PHP 5.6, until WordPress, lets
go of that.
Derick Rethans 12:48
Well, the alternative is that WordPress uses an older
version until it can let go of it.
Juliette Reinders Folmer 12:54
Yeah, the only problem then is that we don't want to
maintain multiple stable branches. For security
fixes.
Derick Rethans 13:03
For Xdebug, what I do is I support what the PHP
project support when a PHP release comes out, which
is a bit longer than PHP itself usually, but not by
much more than a year or two.
Juliette Reinders Folmer 13:15
I understand that. And I mean, I applaud Sebastian
for at some point, having the guts to say to the
community, I'm limiting the amount of versions I'm
supporting. And I'm sticking to the officially
supported PHP versions. That does not mean that that
didn't give a large part of community which does need
to support a wider range of PHP versions a problem. I
fully support that people limit the amount of fish
and stay support and like Sebastian, who I know got
half the community up in arms against him when he
said, I'm not going to support older PHP versions any
more. It did create a problem and but the problem
which I've tried to solve for instance with the PHP
unit polyfills, which now is solvable by using the
PHP Unit polyfills in quite a transparent way, which
is helpful for everyone. It takes the complainers of
Sebastian's back, and at the same time, it allows
them to run the test.
Derick Rethans 14:10
I think another good thing that Sebastian recently
has done is make sure that deprecation notices are no
longer failing your tests.
Juliette Reinders Folmer 14:17
I don't agree. The thing is, I do understand him
making that change. But changing that default from
not showing those deprecation notices or not not
allowing deprecation notes to fail the test, or not
in a patched version, I don't think was the right
thing to do. That should have been in a minor, let
alone or maybe even in a major not in a 9.5.18 patch
version. Also with the whole idea, I mean, again,
this is very much an open source versus closed source
discussion for closed source I completely understands
that people say I don't want to know until I actually
am ready to upgrade to that version.
Derick Rethans 14:56
I understood it's more of a difference not
necessarily between open and closed source, but
rather between library maintainers and application
maintainers. And the applications can then also be
closed source.
Juliette Reinders Folmer 15:06
The open source work I work in, I mean, I do want to
see them. And the problem with the deprecation
notices anyhow, and I've seen various experiments via
Twitter fly past for the past year. Say you build
something on top of something else, you want to see
the deprecation notices and the errors which apply to
your code. We don't want to see the ones which come
from the framework on which you build on top. The
silencing deprecation notices or not, allow tests to
error out on deprecation and just not solve that
problem.
Derick Rethans 15:39
The only thing it does is make things a little bit
less noisy so that fewer people complain to library
authors isn't it? That's pretty much what it does.
Juliette Reinders Folmer 15:48
The thing would I see what it has done is that people
think the tests are passing.
Derick Rethans 15:54
Well they are passing, but...
Juliette Reinders Folmer 15:56
Yeah, but most people don't read change logs of PHP
unit, especially as releases don't get actually have
to change log included. When PHP Unit releases its
actual release, it doesn't actually post a release on
GitHub. So people who watch the PHP unit repo for
releasing doesn't, don't get notifications, let alone
a changelog. So they actually have to go into the
repo to find out what has changed. Most people don't
do that. They just get you know depend-a-bot update,
which won't say much, because again, it doesn't have
release information.
Derick Rethans 16:28 It'd be nice, maybe if Composer
,when you upgrade packages, that it can show like the
high level changes when you do an upgrade. The Debian
project does that if you upgrade packages that have
like either critical or behavioural changes, you
actually get a log when you run the update.
Juliette Reinders Folmer 16:43
And then the change should have been in major or
minor, because in a patch release, you don't expect
it kind of changes. I also know the struggle there.
They've been going through to four PHP units and
which is similar to what I'm struggling with with the
amount of changes from PHP 8.0 and 8.1 which has to
be deal dealt with. Projects are being delayed, we're
having trouble keeping up as an open source
community, we still need to look after our own mental
health as well.
Derick Rethans 17:10
What has the feedback been to far on the RFC or non?
Juliette Reinders Folmer 17:13
The feedback on this particular RFC has been next to
nothing. And that's not surprising. I mean,
basically, the discussion has happened before. And I
started the discussion six weeks ago, eight weeks
ago, which led to this RFC. So far the responses,
which I have seen, either on Twitter or in private or
in our people will read through the RFC. They're
like, yeah, it makes sense.
Derick Rethans 17:37
I think this is quite a nicer way of getting RFCs
done, you discuss them first. And if there's then
found a need actually spend a time on writing an RFC.
In other cases, the other way around happens, right?
People write a long, complicated RFC, and then
complain that nobody wants to talk about it.
Juliette Reinders Folmer 17:53
When I started the previous discussion, it was I see
this, I noticed this, was this discussed? And then I
got back: yeah, nobody actually discussed the
previous RFC and I'm like: Okay, so what's this whole
point about under discussion if nobody's discussing?
Derick Rethans 18:10
Well, you can't force people to talk, of course.
Juliette Reinders Folmer 18:14
It does make me wonder, again, what we were talking
about before, people who work in managed environments
versus people who will have to support multiple PHP
says, I sometimes wonder how many people who actually
have voting rights work in those closed environments,
and think, you know, upgrading is something you do
with Rector. Now I have a feeling that often open
source gets a little forgotten.
Derick Rethans 18:38
Yeah, that's perhaps true. Thank you for taking the
time this morning to talk about this RFC then.
Juliette Reinders Folmer 18:44
Thank you Derick for having me. It was a pleasure to
do you like always.
Derick Rethans 18:49
Thanks.
Derick Rethans 18:54
Thank you for listening to this installment of PHP
internals news, a podcast dedicated to demystifying
the development of the PHP language. I maintain a
Patreon account for supporters of this podcast as
well as the Xdebug debugging tool. You can sign up
for Patreon at https://drck.me/patreon. If you have
comments or suggestions, feel free to email them to
derick@phpinternals.news. Thank you for listening,
and I'll see you next time.
Show Notes
RFC: Expand deprecation notice scope for
partially supported callables
Credits
Music: Chipper Doodle v2 — Kevin MacLeod
(incompetech.com) — Creative Commons: By
Attribution 3.0
Mehr
24.03.2022
1 Minute
PHP Internals News: Episode 100: Sealed Classes
Thursday, March 24th 2022, 09:04 GMT
London, UK
In this episode of "PHP Internals News" I talk with Saif
Eddin Gmati (Website, Twitter, GitHub) about the "Sealed
Classes" RFC that he has proposed.
The RSS feed for this podcast is
https://derickrethans.nl/feed-phpinternalsnews.xml, you
can download this episode's MP3 file, and it's available
on Spotify and iTunes. There is a dedicated website:
https://phpinternals.news
Transcript
Derick Rethans 0:14
Hi, I'm Derick. Welcome to PHP internals news, the
podcast dedicated to explaining the latest
developments in the PHP language. This is episode
100. Today I'm talking with Saif Eddin Gmati about
the sealed classes RFC that they're proposing. Saif,
would you please introduce yourself?
Saif Eddin Gmati 0:31
Hello, my name is Saif Eddin Gmati. I work as a
Senior programmer at Les-Tilleuls.coop. I'm an open
source enthusiast and contributor.
Derick Rethans 0:39
Let's dive straight into this RFC. What is the
problem that you're trying to solve with it?
Saif Eddin Gmati 0:43
Sealed classes just like enums and tagged unions
allow developers to define their data models in a way
where invalid state becomes less likely. It also
eliminates the need to handle unknown subtypes for a
specific model, as using sealed classes to define
models gives us an idea on what child types would be
available at run time. Sealing also provides us with
a way for restricting inheritance or the use of a
specific trait. For example, if we look at logger
trait from the PSR log package that could be sealed
to logger interface. This way, we ensure that every
use of this trait is coming from a logger not from
any other class.
Derick Rethans 1:24
I'm just reading through this RFC tomorrow, again,
and something I didn't pick up on reading to it last
time. It states that PHP already has sort of two
sealed classes.
Unknown Speaker 1:35
Yes, the throwable class in PHP can only be
implemented by extending either error or exception.
The same applies for DateTime interface, which can
only be implemented by extending DateTime class or
DateTime Immutable class.
Derick Rethans 1:52
Because PHP itself doesn't allow you to implement
either throwable or DateTimeInterface. I haven't
quite realized that that these are also sealed
classes really. What is sort of the motivation behind
wanting to introduce sealed classes?
Unknown Speaker 2:06
The main motivation for this feature comes from Hack
the programming language. Hack contains a lot of
interesting type concepts that I think personally,
PHP could benefit from and sealed classes is one of
those concepts.
Derick Rethans 2:18
What kind of syntax are you proposing?
Saif Eddin Gmati 2:21
The syntax I'm proposing actually there is three
syntax options for the RFC currently, but the main
syntax is inspired by both Hack and Java. It's more
similar to the syntax used in Java as Hack uses
attributes. Personally, I have been I guess, using
attributes from the start as I personally see sealing
and finalizing similar as both effects how
inheritance work for a specific class. Having sealed
implemented as an attribute while final uses a
keyword brings more inconsistency into the language
which is why I have decided not to include attributes
as a syntax option.
Derick Rethans 2:56
In my opinion, attributes shouldn't be used for any
kind of syntax things. What they should be used for
is attaching information to already existing things.
And by using attributes again, to extend syntax, you
sort of putting this syntax parsing in two different
places , right? You're putting it both in the syntax
as well as in attributes. I asked what the syntax is,
but I don't think he actually mentioned what the
syntax is.
Saif Eddin Gmati 3:20
The syntax the main set next proposed for the RFC is
using sealed and permit as keywords we first have the
sealed modifier which is added in front of the class
similar to how final or abstract modifiers are used.
We also have the permit clause which is basically a
list allows you to name a specific classes that are
able to inherit from this specific type.
Derick Rethans 3:43
So when you say type here, is that just interfaces
and classes or something else as well?
Saif Eddin Gmati 3:48
It's classes interfaces and traits. Traits are
allowed to add sealing but they are not allowed to
permit. Okay for example, an interface is not allowed
to permit a trait because a trait cannot implement an
interface
Derick Rethans 4:03
In the language itself, when does this get enforced?
Saif Eddin Gmati 4:06
This inheritance restriction gets enforced when
loading a class. So let's say we are loading Class A
currently if this class extends B, we check if B is
sealed. And if it is we check if B allows A to extend
it. But when loading a specific sealed class, nothing
gets actually checked. We just take the permit clause
classes and store them and move on.
Derick Rethans 4:32
It only gets checks if you're trying to implement an
interface.
Saif Eddin Gmati 4:36
This gets enforced when trying to implement an
interface, extend that class, or use it trait.
Derick Rethans 4:41
Okay. What are general use cases for this feature?
Saif Eddin Gmati 4:45
General use cases for a feature are for example,
implementing programming concepts such as Option
which is a type that can only have two subtypes. One
is Some, other is None. Another concept is the Result
where only two subtypes are possible, either success
or failure. Another use case is to restrict
inheritance. As I mentioned before, for example,
logger trait from the PSR log package is a trait that
implements some of the method methods in logger
interface, and expects whoever is using that trait to
implement the rest. However, there is no restriction
by the language regarding this, we can seal this
trait to a logger interface ensuring that only
loggers are allowed use this trait.
Derick Rethans 5:34
When you say that Option has like the value Some or
None, just sound like an enum to me. How should I
think differently about enums and sealed classes
here?
Saif Eddin Gmati 5:43
Enums cannot hold a dynamic value. You can have a
value but you cannot have a dynamic value, however,
tagged unions will allow you to implement option the
same way. Tagged unions are that useful only for this
specific case, there is some other cases such as the
one I mentioned for traits that cannot actually be
implemented using the tagged unions. There is also
the I don't know how to say this. Let's say we have a
type A that sealed and permitting only B and C. And
this case A on itself, as long as it's not an
abstract class, is by itself a type. Can be used as a
normal class, you can create an instance and use it
normally. However with tagged unions, the option
itself would not be a type, you either have some or
none. That's the main difference between tagged
unions until classes
Derick Rethans 6:37
A tagged union PHP doesn't have them. So how does a
tagged union relate to enums?
Saif Eddin Gmati 6:43
With tagged unions as the, there is an RFC that's
still in draft, I suppose that uses actually it is
built on top of enums that that's why.
Derick Rethans 6:55
I reckon once that gets closer to completion, I'll
end up talking to the author of that RFC. So
something I'm wondering, can a sealed type permit
only one other type? Or does it have to be more than
one?
Saif Eddin Gmati 7:10
No, it can permit only one type. Let's say we have
class A that only permits B. However, another thing
is class B does not actually have to extend A, like
if A is permitting B, B does not actually have to
implement A. It's still useful because another class
called C can extend B and implement A, so an instance
of A B can still exists.
Derick Rethans 7:36
I'm not quite sure whether I understood that. If you
have an interface that says A permits B, then B is
not required to implement A, mostly because the
moment you loads class B, you don't even know it
exists, right? Because it doesn't refer to it.
Saif Eddin Gmati 7:54
Yes.
Derick Rethans 7:55
It's just going to break anything?
Saif Eddin Gmati 7:57
Hopefully not. The only break would be in the new
reserved keywords which are sealed and permits. So
those cannot be used as identifiers any more, but
depending on the syntax choice, if for example, the
second syntax choice wins which that would only take
the permits keyword. If the third syntax choice is
chosen then no new reserved keywords will be
introduced so there will be no breaks.
Derick Rethans 8:29
From what I see in the RFC the first syntax is using
both sealed in front of a as a marker and then using
permits. With the second syntax, you don't use seal
but you infer that it is sealed from the permits
keyword I suppose. And then in the last option you
use the for keyword instead of permits and also don't
use sealed yet?
Saif Eddin Gmati 8:51
The third syntax choice is will be the one with no
breaks as we will not be introducing any new
keywords; for is already a reserved keyword in PHP.
Derick Rethans 9:02
What is your preference?
Saif Eddin Gmati 9:03
Personally I prefer the first syntax choice as it's
the most explicit. When you start reading the code
you can tell from the start this is a sealed class
without having to continue reading until you reach
permits.
Derick Rethans 9:15
I think I agree with you there. Beyond the syntax is
there anything else that needs to be changed in PHP
itself?
Saif Eddin Gmati 9:22
The only other change that will be introduced in PHP
is in reflection class. A new method called isSealed
will be added to reflection method, which allow you
to check if a class the class being reflected is
sealed. Another method will be added called
getPermittedClasses which returns the list of class
names provided in the permits clause. Also a new
constant should be added to reflection class that is
is_sealed constant which exposes the bit flag used
for sealed classes. Some changes will happen to the
getModifiers method in reflection class. This method
will return the bit flag is sealed set, if the class
being reflected is sealed. The getModifierNames
method will also return the string sealed if the bit
is set, that should be about it.
Derick Rethans 10:12
Basically everything that you need in reflection to
find out whether it's a sealed class and other
permits.
Saif Eddin Gmati 10:18
Yes.
Derick Rethans 10:20
See, I see the name of getPermittedClasses has to
use, has the word classes in it. Does that mean that
the types after permits have to be classes?
Saif Eddin Gmati 10:32
No, they can be either classes or interfaces. But PHP
refers to both classes and interfaces as classes in
the reflection. So we have a reflection class, but
that's actually a reflection trait class interface.
And basically everything is class-ish.
Derick Rethans 10:47
Class-ish. I like that. Did you look at some other
alternatives to implementing the same feature or just
the three syntax choices that you came up with?
Saif Eddin Gmati 10:56
I did not consider any other alternatives precisely
as the alternatives might be type aliases, tagged
enums, package visibility. But I think each of these
RFCs focused on a specific problem and expanding that
area, while sealed classes focuses on all the
problems mentioned on in this RFC tries to solve them
in a minimal way. But only in relation to inheritance
in classes, interfaces, and traits.
Derick Rethans 11:24
Keeping it short and sweet. What has the feedback
been so far?
Saif Eddin Gmati 11:29
The feedback has been pretty mixed. Some people are
against adding more restriction to types and
inheritance. But in my opinion, this is not about
adding restriction, but rather providing the user
with the ability to add restrictions. And we already
have final classes, which a lot of people seem to
dislike.
Derick Rethans 11:48
I don't understand why. But fair enough.
Saif Eddin Gmati 11:51
I have created a community poll a couple of weeks ago
to gather feedback on Twitter. The results were 60%
for with over 150 participants. Another poll was
created by Peter on Facebook ended with 54 of people
voting yes. However, such polls that do vary
depending on the audience. So it can be really an
accurate representation of the PHP community.
Derick Rethans 12:15
Polls on Twitter are never scientific, or they? I see
that the RFC is in voting already. So for people
listening to this, and if you have voting rights,
then you have until when exactly?
Saif Eddin Gmati 12:28
Until the end of the month.
Derick Rethans 12:30
March 31. It says yes. Okay. Well, thank you very
much for taking the time today Saif about sealed
classes.
Saif Eddin Gmati 12:37
Thank you for having me. Hopefully, I get to be here
another time in the future.
Derick Rethans 12:42
I hope so too. Thank you for listening to this
installment of PHP internals news, a podcast
dedicated to demystifying the development of the PHP
language. I maintain a Patreon account for supporters
of this podcast as well as the Xdebug debugging tool.
You can sign up for Patreon at
https://drck.me/patreon. If you have comments or
suggestions, feel free to email them to
derick@phpinternals.news. Thank you for listening,
and I'll see you next time.
Show Notes
RFC: Sealed Classes
Credits
Music: Chipper Doodle v2 — Kevin MacLeod
(incompetech.com) — Creative Commons: By
Attribution 3.0
Mehr
10.03.2022
1 Minute
PHP Internals News: Episode 99: Allow Null and False as
Standalone Types
Thursday, March 10th 2022, 09:04 GMT
London, UK
In this episode of "PHP Internals News" I talk with
George Peter Banyard (Website, Twitter, GitHub, GitLab)
about the "Allow Null and False as Standalone Types" RFC
that he has proposed.
The RSS feed for this podcast is
https://derickrethans.nl/feed-phpinternalsnews.xml, you
can download this episode's MP3 file, and it's available
on Spotify and iTunes. There is a dedicated website:
https://phpinternals.news
Transcript
Derick Rethans 0:15
Hi, I'm Derick. Welcome to PHP internals news, a
podcast dedicated to explain the latest developments
in the PHP language. This is episode 99. Today I'm
talking with George Peter Banyard, about the Allow
null and false at standalone types RFC that he's
proposing. Hello, George Peter, would you please
introduce yourself?
George Peter Banyard 0:36
Hello, my name is George Peter Banyard. I work on the
PHP language, and I'm an Imperial student in maths in
my free time.
Derick Rethans 0:44
Are you're trying to say you're a student in your
free time or contribute to PHP in your free time?
George Peter Banyard 0:49
I feel like at this time, it's like, both are true at
the same time.
Derick Rethans 0:53
Let's hop into this RFC. It is titled allow null and
false as standalone types. What is the problem that
it is trying to solve?
George Peter Banyard 1:02
This is the second iteration of this RFC. So the
first one was to just allow null initially, and null
is the unit type In type theory parlance of PHP, ie
the type which only has one value. So null is a type
and a value. And the main issue is that when for
leads more with like inhabitants, and like the Liskov
substitution principle. If you have like a method,
like the parent method, which can be told like either
null or an object, and your implementation in a child
class always returns null, for various reasons, maybe
because it doesn't support this feature, or whatever
is out, or... If your child method only returns null,
currently, you can't document, that you can't type
this properly, you can document it in a doc comment
or something like that. But due to how PHP type
handling works, you need to specify at least like
another type with null in the union. Basically resort
to always saying like mimicking the parent signature,
when you could be more specific. This was the main
use case I initially went into.
Derick Rethans 2:08
If I understand correctly, you can't just have an
inherited method that has hinted as to just return
null?
George Peter Banyard 2:14
Exactly. If you always return null, maybe because you
always work or something like that, then you must
still declare the return type as like null or
exception, which is not a concrete because you say
what, like why never fail. And like static analysers,
if they can figure it out that you're using a child
class, they can't maybe like do some assumptions or
work further down that like what you're doing is
redundant or things like that. So that's one of the
main reasons I initially went with it. And I didn't
add false initially, because it was like, well,
false, it's not really a type properly. It's, it's
what's called a value type. False is one value from
the Boolean type. And I was like, Well, okay, we're
just going to limit it to like, being the type theory
purist, limited to proper types, where null is a
proper type, although it's a bit sometimes
misunderstood, I feel in the PHP community at large.
And then people were like, well, if we add null, then
by the only type-ish thing, which you can use in a
type declaration, or whatever, which can't be used in
a return type on its own, is false. And it's just
weird. So why not add it in full. So that was the
second thing as to why I added it. Some of PHP
internal's functions being terribly designed because
they were designed back in the early noughties,
return null on success and false on failure, which
you can't probably type at the moment. Currently, we
need to type them as like Boolean or null, but true
can never be returned in this case. And there are
some other some other people have reached out to me
it's like, well, yeah, but I always return false in
this case. Or I also return always true in this case,
although true, we have this weird asymmetry that we
have false as a value type and not true.
Derick Rethans 3:49
What was the reason for having false but not true?
George Peter Banyard 3:53
When the union type RFC got discussed and passed for
PHP 8.0, false was added, because a lot of
traditional behaviour of PHP internal functions, was
to return false on failure, instead of the
technically more correct thing would be to return
null. Because loads of functions return a false on
failure, and saying that like in returns, these
types, or a Boolean would be basically lying because
you could never have true, false was included in it.
With the restrictions that you can only use false as
the complement with other types. So you need to do
for example, array, or false, you couldn't just use
false.
Derick Rethans 4:37
Would it also mean that you can define a return type
of a method that inherited a method that returns a
bool, as false?
George Peter Banyard 4:48
Yes, that would be now possible with the amended
proposal. Yeah, which goes back to this weird a
symmetry, we're probably. Adding true to make a
complete would be a future RFC to do.
Derick Rethans 5:00
Now, we've talked about return types. But I guess the
reverse applies to arguments?
George Peter Banyard 5:06
Arguments and property types also would, would be
allowed to, like declare themselves as like null or
false. The usefulness here is way more limited.
Because if you declare an argument to be of type
null, then basically you can only ever pass a null to
it. And then therefore, the type doesn't do anything.
Derick Rethans 5:26
But in an inherited method, you could then widen it.
George Peter Banyard 5:31
Yes, exactly. You could always say: Well, this
argument exists, it's always null. If you extend like
your class or message, then you can add other types.
But in theory, you can already do that by adding like
an argument at the end of the message, because that's
LSP compliant. The case for, and properties of those,
because they are typing, they're in like their beads.
Kind of debatable why you would do that. But it's
just that like, well, if you accept types at one
point, just restricting them like somewhere else gets
very weird. At this point is more like look at the
human review, or like use static analysis for the
analyser to tell you like this argument is redundant
and just remove it or this property doesn't make any
sense. Because if it can only ever be null, why does
it even exist in the first place?
Derick Rethans 6:13
Right now, you can already use false in union types,
but why not with null or false?
George Peter Banyard 6:19
That goes back to the when a union type RFC got
introduced. Null got added as a keyword. Before you
could only use the question mark, before a type to
make the type nullable. If you have a more complex
union type, to not use the question mark in front of
it. Therefore, the null keyword got added as a proper
type. And because the logic was, Well, you shouldn't
ever be able to return just null. Because then that
function is kind of equivalent to void. Because of
that, it was said that like, Well, okay, null and
false basically have like kind of the same status is
that like, if you just want to use null on its own,
you're doing something kind of weird. And if you're
returning more than false, like that signature is
very strange. I think when that was discussed, nobody
knew initially that an actual PHP function within one
of the extensions, like in core had such a weird
signature. Which mainly, we just started discovering
that after this got, like accepted and we could like
actually start properly typing the internal
functions, and then you discover these weird edge
cases where sounds like, that's a bit strange, can't
properly document it. We just need to make like a
note on the PHP documentation side. And like the type
signature kind of lies to you. PHP's type hierarchy
is a bit strange, void kind of lives on its own. So
if the function is marked as void, it must always
like any child inheritance, or whatever needs to be
void. And when you type return in the function body,
you need to always use return with like a semicolon
afterwards, you can't even return null. Although,
under the hood, PHP will always return a value when
you call a function, even if the function is void,
which will be null.
Derick Rethans 7:58
The RFC also talks about question mark null, what is
that supposed to be? Is that null or null?
George Peter Banyard 8:03
PHP has this limited type redundancy checks at
compile time. It will basically check if you're
duplicating types. So if you write for example, int
or int, even if it's capitalized differently, PHP was
like, okay, just specifying twice the same type in
this union. This is redundant. And then it will throw
a compile error, we're basically saying, maybe you're
just doing a mistake, maybe you meant something else.
In the same vein, basically the question mark, gets
like, translated into like, any seeing pipe null. And
so if you write null with a question mark in front of
it, it's just saying like, well, you're doing null or
null, which is basically redundant. Therefore, you'll
get like a compile time error telling you like.
Derick Rethans 8:41
That seems sensible to me. What's been the feedback
so far?
George Peter Banyard 8:45
The most feedback, I think I've got it when I first
proposed it in October. And at the time, it was like,
Yeah, this is useful. And it's kind of needed because
well, having always more type expressiveness is I
think, good in general. But the main feedback at the
time was like, Well, why not include false? The other
feedback I got was basically, well, for consistency,
what shouldn't you also add true? Yes, I do agree
with this. I frankly, find it very strange that we
landed in this situation where we only have one of
these value types, either true and false, or none of
them would make more sense to me. But that's
expanding the scope. And it's kind of not this is not
really concerned with this specific detail. Probably
next, another RFC to do, for either myself or
somebody else. It's just like propose true as a value
type.
Derick Rethans 9:33
Is the implementation of this RFC complicated?
George Peter Banyard 9:36
It's very simple. It basically removes checks,
because currently in the compile step, which checks
for like type signatures, it needs to check that
like, Well, are you declaring false or are you
declaring null, and these checks get removed, so it
makes the code a bit more streamlined. Oh, there's
one change in reflection. For backwards compatibility
reason, because of the fact of the question mark, any
union type which is composed of a only two types,
where one of them is null,will get converted in
reflection to use the question mark notation, which
is kind of a bit weird because it then gets converted
into like a name type instead of a union type in
reflection. But that's there, because of backwards
compatibility reasons. I am breaking this into the
more sensible reflection type. If you have a type of
null and false, then you'll get a reflection union
type instead of a named. From my understanding from
reading the reflection code, the intention was always
probably in PHP 9, to remove this distinction. So if
you get a named type, it's only a single type instead
of a possible nullable type. And any nullable types
get converted into like a reflection union type when
you have like null and the other type. Maybe this is
a good test case to see if your code breaks.
Derick Rethans 10:50
I would probably call that a BC break though.
George Peter Banyard 10:53
This only happens if you do false union null. You
can't use false currently on its own. And I think
like, if you get false, as a named argument type,
with like a question mark in front of it. Because it
would be completely new, and you would never deal
with it. It's like, well, this can also break because
false can be in the Union type. If your library or
the tool supports union types with the reflection
thing, it will automatically know how to deal with
false because it needs to know how to deal with it.
And null. So that was kind of also the logic. It's
like, well, okay, like if the tool supports that,
which it needs to, then if you put this case into
that bracket, it will work. It makes kind of the
reflection code a bit more complicated at the moment.
The whole fact that we need to juggle between like
figuring out like, should we use the old like the
backwards compatible thing reflection of like using a
name type instead of the Union type, if there's a
null and depending on the type, makes a reflection
code unwindy and everything. And we have like a
special function in C, which is basically just like,
okay, which object do I need to create, depending on
this type signature?
Derick Rethans 11:53
When do you think you'll be putting this up for a
vote?
George Peter Banyard 11:56
I suppose I could put it up for vote immediately now.
I am planning on maybe putting this on to vote at the
end of the week or something like that.
Derick Rethans 12:04
Well, thank you for taking the time today to talk
about this RFC.
George Peter Banyard 12:09
Thank you for having me on the podcast.
Derick Rethans 12:13
Thank you for listening to this installment of PHP
internals news, a podcast dedicated to demystifying
development of the PHP language. I maintain a Patreon
account for supporters of this podcast, as well as
the Xdebug debugging tool. You can sign up for
Patreon at https://drck.me/patreon. If you have
comments or suggestions, feel free to email them to
derick@phpinternals.news. Thank you for listening,
and I'll see you next time.
Show Notes
RFC: Allow Null and False as Standalone Types
Credits
Music: Chipper Doodle v2 — Kevin MacLeod
(incompetech.com) — Creative Commons: By
Attribution 3.0
Mehr
Über diesen Podcast
This is the 'PHP Internals News' podcast, where we discuss the
latest PHP news, implementations, and issues with PHP internals
developers and other guests.
Kommentare (0)