PHP Internals News: Episode 103: Disjunctive Normal Form (DNF) Types

PHP Internals News: Episode 103: Disjunctive Normal Form (DNF) Types

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.


292:Laravel is a Wrapper for PHP

Links from the show:

This episode of PHPUgly was sponsored by:

PHPUgly streams the recording of this podcast live. Typically every Thursday night around 9 PM PT. Come and join us, and subscribe to our Youtube Channel, Twitch, or Periscope. Also, be sure to check out our Patreon Page.

Twitter Account https://twitter.com/phpugly

Host:

Streams:

Powered by Restream

Patreon Page

PHPUgly Anthem by Harry Mack / Harry Mack Youtube Channel

Thanks to all of our Patreon Sponsors:

Honeybadger ** This weeks Sponsor **

ButteryCrumpet
Frank W
David Q
Shawn
Ken F
Boštjan
Marcus
Shelby C
S Ferguson
Rodrigo C
Billy
Darryl H
Knut Erik B
Dmitri G
Elgimbo
MikePageDev
Kenrick B
Kalen J
R. C. S.
Peter A
Clayton S
Ronny M
Ben R
Alex B
Kevin Y
Enno R
Wayne
Jeroen F
Andy H
Sevi
Chris C
Steve M
Robert S
Thorsten
Emily J
Joe F
Andrew W
ulrik
John C
James H
Eric M
Laravel Magazine
Ed G
Ririe
lilHermit
Champ


Keeping review time down, deleting your .env.example file, and securing your secrets

Jake and Michael discuss some approaches to keeping pull request review times short, and using 1Password to free you of having to keep track of your environment variables.

This episode is sponsored by Workvivo.

Show links


291: Artificial Stupidity

Links from the show:

This episode of PHPUgly was sponsored by:

PHPUgly streams the recording of this podcast live. Typically every Thursday night around 9 PM PT. Come and join us, and subscribe to our Youtube Channel, Twitch, or Periscope. Also, be sure to check out our Patreon Page.

Twitter Account https://twitter.com/phpugly

Host:

Streams:

Powered by Restream

Patreon Page

PHPUgly Anthem by Harry Mack / Harry Mack Youtube Channel

Thanks to all of our Patreon Sponsors:

Honeybadger ** This weeks Sponsor **

ButteryCrumpet
Frank W
David Q
Shawn
Ken F
Boštjan
Marcus
Shelby C
S Ferguson
Rodrigo C
Billy
Darryl H
Knut Erik B
Dmitri G
Elgimbo
MikePageDev
Kenrick B
Kalen J
R. C. S.
Peter A
Clayton S
Ronny M
Ben R
Alex B
Kevin Y
Enno R
Wayne
Jeroen F
Andy H
Sevi
Chris C
Steve M
Robert S
Thorsten
Emily J
Joe F
Andrew W
ulrik
John C
James H
Eric M
Laravel Magazine
Ed G
Ririe
lilHermit
Champ



290: PHP Lambos and Lambdas

Links from the show:

This episode of PHPUgly was sponsored by:

PHPUgly streams the recording of this podcast live. Typically every Thursday night around 9 PM PT. Come and join us, and subscribe to our Youtube Channel, Twitch, or Periscope. Also, be sure to check out our Patreon Page.

Twitter Account https://twitter.com/phpugly

Host:

Streams:

Powered by Restream

Patreon Page

PHPUgly Anthem by Harry Mack / Harry Mack Youtube Channel

Thanks to all of our Patreon Sponsors:

Honeybadger ** This weeks Sponsor **

ButteryCrumpet
Frank W
David Q
Shawn
Ken F
Boštjan
Marcus
Shelby C
S Ferguson
Rodrigo C
Billy
Darryl H
Knut Erik B
Dmitri G
Elgimbo
MikePageDev
Kenrick B
Kalen J
R. C. S.
Peter A
Clayton S
Ronny M
Ben R
Alex B
Kevin Y
Enno R
Wayne
Jeroen F
Andy H
Sevi
Chris C
Steve M
Robert S
Thorsten
Emily J
Joe F
Andrew W
ulrik
John C
James H
Eric M
Laravel Magazine
Ed G
Ririe
lilHermit



289: PHP Multiverse

Links from the show:

This episode of PHPUgly was sponsored by:

PHPUgly streams the recording of this podcast live. Typically every Thursday night around 9 PM PT. Come and join us, and subscribe to our Youtube Channel, Twitch, or Periscope. Also, be sure to check out our Patreon Page.

Twitter Account https://twitter.com/phpugly

Host:

Streams:

Powered by Restream

Patreon Page

PHPUgly Anthem by Harry Mack / Harry Mack Youtube Channel

Thanks to all of our Patreon Sponsors:

Honeybadger ** This weeks Sponsor **

ButteryCrumpet
Shawn
David Q
Ken F
Tony L
Frank W
Jeff K
Shelby C
S Ferguson
Boštjan O
Matt L
Dmitri G
Knut E B
Marcus
MikePageDev
Rodrigo C
Billy
Darryl H
Mike W
Holly S
Peter A
Ben R
Luciano N
Elgimbo
Wayne
Kevin Y
Alex B
Clayton S
Kenrick B
R. C. S.
ahinkle
Enno R
Sevi
Maciej P
Jeroen F
Ronny M N
Charlton
F'n Steve
Robert
Thorsten
Emily
Joe F
Andrew W
ulrick
John C
James H
Eric M
Laravel Magazine
Ed G
Jackson W
Ririe


PHP Internals News: Episode 102: Add True Type

PHP Internals News: Episode 102: Add True Type

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.