All right, last session.
Paul Evans, working hard behind the scenes to make sure we have a soul through 5, 6, 7.
What is going to be at 3?
Don't you change it?
Let's call it, I don't know, 100.
Who knows?
All right.
Man, I'll be dead by then.
All right.
Hello, welcome.
Hello.
So this is updates from the Pearl Steering Council.
So a bit of history first.
We've had some yearly releases of Pearl for a very long time now.
532, that was out in 2020, middle of the summer.
And then kind of, you know, every year or so, kind of like Clockwork.
We've had new releases every year.
This is a thing.
People maybe don't realize this.
Some recent changes we've had.
So in 532, we added this Easey operator.
That was kind of cool.
534, we added this Try Catch syntax.
These are some new things we've had.
536 was a lot of new stuff.
So we added loads of things here.
Brief list here.
First big headline thing, Stabilized Signatures.
So finally, that nice little signature syntax there.
That's now stable part of the language.
You can just use that.
You don't have to fiddle with the at underscore array anymore.
It's very, very nice.
We added this multi-variable for each mechanism here.
Come in, come in.
So if you want to iterate over multiple variables at once out of an array, for example, you can just pull multiple of them and it works.
It's especially nice for iterating on hashes.
So you have a hash here.
You get each key and each value inside the body of the free-trilute.
It's wonderful.
I love it.
What else have we got?
We've got defer blocks.
So you use feature defer.
And now you've got this defer thing here.
So you can put a piece of code.
If you're familiar with Go, this is not like the Go ones.
If you're familiar with any other language that has defer, it's exactly like that.
In Go, they decided that defer blocks would always push onto a stack and then at the end of the function, it would run the block.
Whereas every other language said, no, that's kind of crazy.
We'll just do it lexically scoped.
So you have a defer and then you get to the end of the block and it runs it.
Every other language does it that way.
Even C, some people are discussing adding defer to C.
Because if you don't have this crazy array for the function, you can do it mostly statically in the compiler.
And it's just kind of shorthand for putting it in the compiler.
And every other language does it this way.
Don't know why Go does it its own weird way.
It's a bit weird.
Anyway, so we have defer blocks.
And you can put finally blocks on try catch as well.
It's basically the same as a defer.
But people seem to expect that if you can do try catch, you can do try catch finally.
OK, we added it fine, whatever.
Another thing we added in 536 is this built in namespace.
So for years and years and years, if people wanted things like we can and blessed and ref adder and so on,
they'd have to get them out of scale util, which is another module you'd load off the file system.
It's a bit annoying.
These are now built in to the language.
So you don't have to use anything.
It's just right there.
It's already you can always use it.
But if you want, you can you can import it as well.
So for example, we have this nice index function that plays very nicely with the multi variable for each, for example.
So this indexed, you give it a list of things.
It gives you back a list that's twice as big where the first value is prefixed with zero.
The second one is prefixed with one, someone and so forth.
So if you're iterating a list out of an array at every element, you can see the index of that item out of the array.
It's really, really nice.
And it's built into the language.
It's this here is really just telling the parser for this scope here.
I want to have this indexed word available, but it built into the interpreters always available.
And who is it?
People were talking about lexical imports earlier.
These built in is lexical.
So basically what that means is you've just written some code here.
You can just see it, but it's not putting that word indexed into your package.
So if you're writing an object class that you don't get the word indexed visible as a method.
It's not visible from outside.
It's only visible within this scope.
Really nice, really handy, excellent way of working.
So these built-ins are very nice.
Alongside the built-ins, we finally, finally have actual Boolean values.
C originally didn't have Booleans either.
And then eventually in C99 they realized whoops, we should have Booleans.
It's taken us until 536 to realize we should have Booleans, but we now have them.
So we've got this built-in true and false.
Look at that, look at that, my T equals true.
Guess what that does?
There won't be a prize.
It's not that subtle.
But specifically we have this isBool test.
So you can ask, here's a value, is it Boolean or not?
So one and the string one, well they're not Booleans, but this real true, well it really is a Boolean.
That's kind of handy.
It's particularly handy because things like data dumper knows about it.
So if we print this array here, got 2 plus 2, 2 concatenated with 2 and 2 is equal to 2.
Well that gives you the fairly obviously expecting 4 and the string 22.
But it also gives you this pling-pling-1.
That's not very nice, but the reason for that is because everyone uses data dumper wrong.
Data dumper, it's one goal is to output valid Perl code.
It doesn't know that it's trying to output debugging values for humans to read.
It's sole purpose is to output valid Perl code.
And it doesn't know that you might not be running this on an older Perl,
you might not be loading it back in to an older Perl that doesn't know what the true keyword is for example.
So it's going to print pling-pling-1 because it has no other choice.
This is really more a comment of please stop using data dumper for human debugging.
What you want to use is something like data printer.
Data printer is specifically designed for outputting pretty things to humans.
And this slide doesn't show it, but that comes out in color.
It colors the strings and the numbers and the keywords and the surrounding shapes all subtly different.
And it looks really nice on the screen and it's lovely and data printer is so much nicer.
If you're debugging stuff for humans, use data printer.
So thank you to Breno for implementing.
It's true, it's so good.
That's not all.
And the JSON-PP also knows about, yeah, there we go, JSON-PP.
You encode this very same array, you get 4 string 22 and true.
The JSON-XS version, Remy is still looking at it.
Last time I looked it was about three, four days ago.
It's not been merged yet, but he's working on it.
Hopefully that'll come soon.
The YAML modules is Tina around? Tina was around earlier.
Yes, hello Tina, thank you.
This slide is for you Tina, look at that.
Four, the string 22 and true.
And JSON-PP as well, they all do it.
So thank you for Tina, for doing that one.
So yeah, real Booleans, use them, use them, they're nice.
Moving on to 538, the newest one that's currently around.
Somebody wrote this class thing, I don't know if you've heard of it.
Have you heard of it, Ovid? I don't know.
Ovid obviously talked quite a lot about this class system earlier.
So I'll just go through and brief.
Here's a small example of, here's a small piece of code that you can write
to implement like an object class.
You can create here, we have these points, and they have some values.
Yeah, they're great, you can have another point.
What kind of behaves in the obvious way you'd expect from looking at the code?
There's several things about this that I kind of want to point out
as again, kind of covering similar stuff to Ovid earlier.
There's a lot of low level stuff that this thing just does for you.
So you don't have to write sub-new anymore.
You don't have to write a bless, sorry, wait for that, noise outside to finish.
You don't have to write a bless expression anymore.
You don't have to call these accesses to get at your instance fields.
They're just accessible directly as lexical variables.
They're nicely there straight away and you can just use them.
Specifically thinking of Java programmers, Python programmers in particular,
this slide is for you.
You write a class, you declare that it has some fields, x and y,
here are the default values, and that's it.
Nowhere did I have to unpack self.x equals x or args.x or whatever and work out
did they pass a value in, take the argument otherwise take a default value.
No, you don't have to do any of that.
Here's a method.
I've just straight away got access to the local fields and I've got the self
and notice that I didn't have to put dollar self in the signature here.
So I didn't have to put dollar self.
No, I didn't even have to shift self in old school style.
I was writing some Python class code lately and I kept forgetting to put
def method open, self comma.
Why would I put the self in the arguments, in the parameters to the method
when I don't put the self in there when I'm calling the thing?
As soon as you start getting used to using method,
you forget about taking self as an argument.
It goes out of your head.
It's again nice and neat and lovely and it just takes things away that you
don't have to think about.
More things you don't have to think about anymore.
So as I said signatures, we added these in 536.
So here's an example of a signature subroutine and here we're taking a parameter.
Are we taking this optional parameter?
This one's fine.
I'm shaking all over the place.
This y here.
If you don't pass in a value for that y, you get this default.
So here we have an x is 20 and a y, well, you just take the default of 10.
That's all very well, but the way these work inside,
if you specifically pass in an undef, well, you've passed in a value, right?
But that's probably not what the author of this code really intended.
So it kind of breaks a bit if you pass in an undef.
It gets a bit worse if you're just passing in variables because now,
well, you'd have to check carefully is $a1 defined or not.
And if it's not defined, then I'll just not pass it in.
And it's messy to write some code like that.
So new in 538, you can now use the defined or a sign operator
to declare your signature parameter.
So you don't pass, it sort of internally behaves much like this,
where you look at is the value defined or not
rather than just did it exist or not.
So as you'd expect, you pass in one value and you just get the default.
If you specifically pass in an undef, Pearl goes,
ah, you've passed in an undef, that's the same as if you haven't passed it in at all,
I will take the default, which means that passing in two arguments is a lot neater.
I have another talk where I go into more detail about specifically what's in 538
and I actually point out if you were to have, say, five parameters to your function
and four of them were optional ones,
you literally couldn't do it without this operator
because you can't literally not pass the middle parameters and still pass the last one.
Whereas you have to pass in an undef.
And so suddenly with this operator, you can have those kind of middle ones missing
and still put in a value at the end.
So it makes that kind of thing possible that you literally couldn't do before.
So pretty much any time you're using default values in a parameter,
to be honest, you probably wanted this defined or because specifically passing in undef
is almost never a thing you want to distinguish from just not having a thing at all.
So that's quite nice.
And these two things combine together quite nicely.
So for example, when you have a class and you have some default values on parameters,
you can of course just use the defined or operator there.
So once again, it means that things like this,
where you're constructing an object by just passing in whatever values you have in variables,
if those happened to be undef, it wouldn't matter.
It would say, okay, I'll just apply this default of zero.
So that's all very nice and handy.
Other new things in 538, we have these plugable infix operators.
So for event and time in part, we've had plugable keywords,
which is how a lot of the weirder syntax modules like syntax keyword try
and future async weight and object pad,
those will work with this keyword mechanism where they can tell the parser,
I want to implement a whole new keyword, give me control for a bit.
So new in 538, we've added more support for doing similar kind of stuff like that
with infix operators.
So that means that we can have even more cpan modules to experiment with
things that might become new syntax in Perl at some point.
And we've got a few things to play around with those.
So people want like an EQ operator.
People always ask for an E in operator and I've explained in great detail why
that's not as easy as it sounds, but there's a few examples there.
And there's things like a zip and a mesh and there's a few other modules there.
But for example, this one in particular EQU is a nice behavior
that at some point we might add into Corp whole.
So behaves very similar to the normal string EQ operator,
but it knows that undef is different from the empty string.
So this is really cool.
And here it's literally this new infix operator.
So you use it very much like EQ in that two strings,
two strings that are either the same or different, it tells you about those,
but it knows that undef is equal to undef.
It knows that undef is not equal to the empty string.
And in none of these cases, it will print a warning.
So that's quite often the sort of thing that you want.
It's slightly nicer than using EQU and defined tests all the time.
And this is exactly this kind of experiment that's really useful
to be able to test it on CPAN first and say, hey, do we like it?
We'll use it in a few places, go along, maybe decide eventually.
Yes, we'll put that into the language, maybe not.
Kind of depends.
So one thing you might have noticed from pretty much all of these examples so far
is that every single one of them starts with this use, the 536 at the top,
all these other ones, or use V538.
There's a reason for that.
The use version mechanism.
It allows you to configure effectively the language from the very first line
of your source code.
So rather than you just deciding this is the version of Pearl I want to use today,
your file says I want to be 536 or 538 or whatever.
And it's a thing we've always had in Pearl,
but people haven't necessarily used it as much as they should.
And I keep trying to point out how good and how useful it is
and why you should do it all the time.
Because, for example, it implies a feature bundle.
So you say, for example, use V536, you get all of the features that were enabled in 536.
So rather than having to ask for all of these things individually,
if you just write use V536, you get all of this good stuff,
like say and signatures and maybe some of the other ones are good as well,
but those two by far are the ones that I just tend to use all the time.
Everything is just say and sub-routine signatures.
So those are all very nice.
But it gets better.
It's very similar to things like when you compile some C code,
you tell the C compiler which version of C I want to be using here.
So it means that just because you've installed a new version of GCC,
if you don't tell GCC that I'm compiling C99 code,
well, you can still compile C89 code or whatever.
Just because you've updated your compiler, you can still compile old programs.
It's even better than that because it's not just applying to a file,
it applies anywhere.
You can just put a use version inside a block.
And you can say, inside this block, I want to behave as if it was 536.
But I'm not going to put use V536 for the whole file just because
I still happen to have some older code here.
For example, this thing using prototypes.
I didn't want terminal signatures here.
So rather than going to fix up my entire code base all in one go to work on 536,
I'll just do a small bit here today and then maybe tomorrow I'll do a bit more.
And so I'm like an incrementally update to using the new stuff.
It gets even better than that.
So not only does it imply a feature bundle,
but ever since 512, it turns on strict.
So any time you write use strict at the top of your file, you always do that, right?
You can instead just put use V512 and you've already got strict.
Oh, and you've got all the features.
Oh, but new in 536, we added warnings.
So if at the top of your file you would write use strict, use warnings,
by the way, you should always do that.
We don't have to.
You just can put use V536.
And now you've got strict and warnings and all of those features.
So it's really, really nice.
It gives you your choice of the latest features.
It means that we can maintain that compatibility of the language.
We can add new stuff in Perl.
So like you noticed 536 added a lot of those keywords like try and defer and so on.
If you don't write use V536, you don't get those.
But that's fine.
It means that if in any of your code you had something called try or defer,
well, we haven't broken that.
We can add new stuff in Perl without breaking your code.
All you have to do is put use V536 or use V540.
What that means is, yeah, we can update Perl without breaking your code.
That means you can update your Perl binary without breaking any code.
Hands up if you've ever installed a new Perl and something has broken.
Interesting.
That means we failed.
If you install a new Perl and something works.
A few years ago.
Yeah, yeah.
Like really early ones, sometimes they didn't go so well.
But more recently, I mean, you know, so for example,
I think it was about last month or so,
I updated a bunch of stuff on my email box and all of my email scripting stopped working.
And I looked into it and I discovered actually Proc Mail has,
there's a little bug in Proc Mail now, it's a bug,
something in Proc Mail had changed that meant that a piece of Perl code I wrote
over 15 years ago is not being invoked properly.
And so all of this thing stopped working.
But the script that I wrote 15 years ago for handling all my email
works perfectly fine to this day.
Like I haven't bothered touching it.
I'd almost forgotten that I wrote it.
It just works.
And it's all because of this use version mechanism.
And so when people say, oh, why do I have to put use version
or use feature or whatever to turn on new stuff?
This is exactly why.
It means we can update Perl and you can update Perl
and not break your stuff.
But it means you have to ask for new things.
Speaking of asking for new things, I've been mentioning a lot of these things
are quite experimental.
So some terms here.
So stable stuff means it's long-term guaranteed.
What that means is if we put something in the language and we say it's stable,
that means in a decade's time, in two decades,
like all of the stuff we're talking about now has been stable for like the last 20 years.
And it's all of the stuff that if you update your Perl,
you don't have to think about because it's all the stuff that's there and stable and working.
Experimental simply means a lack of that guarantee.
All the experimental means is we don't guarantee that this will still work in 20 years' time.
But it's no worse than random stuff I downloaded anyway.
Like if you install stuff off GitHub or C-Pan or other languages,
things like NPM or PIPI or whatever,
if you just download it and the author says,
oh, actually next week I've changed my mind, it's going to work something else,
that's only the same level of guarantee here.
So don't be afraid of experimental.
We're not saying, oh, it's crazy, it might break and blow up your code.
That's not what we're saying.
What we're saying is if you use it now, we don't guarantee it'll still be around next year.
But maybe it will.
It's not about does it work.
We know it works.
We have lots of tests.
Things don't get merged at all unless they actually work.
So things like the object system and try catch and all of this lot, it works.
We know it works.
People use it in production.
The question is do we like it?
It means you.
Do you like it?
If people come back and say, yes, we like this, this is great, then wonderful,
we'll take the experimental tag off.
Nobody comes back and says, hey, we've used this, we like this.
How do we know whether we should commit on it?
There's things, literally this week that we've been staring at to do with lexical subs,
that if more people had been using them over the last eight or nine years
since they were made non-experimental, we might have encountered sooner and said,
actually, yeah, that's a bit of a design flaw.
Whoops, that's a shame.
But hardy knows when to be using them.
So we didn't know.
So now it's a little bit late to change them.
So this is a request.
This is the one takeaway from this talk.
If you learn nothing else to learn this, please use experimental features.
Not necessarily in your production, I still want this to run in a decade code.
But if you're writing some small little test thing that maybe is only going to last for today
or a week or whatever, or you're just grabbing some data and mangling it
and fiddling around with it on your laptop,
and you're going to throw away the script after lunch anyway,
please play around with these experimental features.
We're not saying they don't work.
What we're saying is they might not exist next year.
But if you're writing some code that doesn't exist next year, who cares?
So please try them out.
So with that said, what are the current experiments?
Well, we've got try catch.
That's still a bit experimental because ideally I would like when you catch an exception,
you get more information out of it than just the string of what the exception was.
So we might expand a bit on that.
Differ is experimental.
There's a few reasons for that.
To do with if you throw an exception while you're deferring,
while you're unwinding another exception,
you've got this kind of double exception collision thing going on.
It's a bit weird.
Multi-variable for each, that's just because it's new.
Some of the built-in functions, they're currently experimental,
but they probably don't need to be.
Class is obviously very experimental because we're changing a lot of stuff around.
That will change and devolve over time.
There's one particular experiment that I do want to draw attention to,
and that's when we got rid of, when we unexperimented subroutine signatures overall,
we did leave in one thing, and that's if you use the default arguments array for some reason
inside a signature sub, that does currently print an experimental warning.
The reason being it's kind of annoying to implement,
and if people stop doing this, then we can get rid of a whole bunch of the implementation
and make all of functions faster in Perl.
Please stop doing this, and then we can make your Perl faster.
Sonia?
Any mac-in-the-feature?
We could, yeah, it could become a feature.
Maybe, maybe.
We'll see, it's complicated.
Talk to me at lunch.
Anyway, so we've only got 10 minutes left.
Coming up in 540, new release that we're expecting to be out sometime this summer,
most built-in functions should become stable.
So things like, at the moment, things like ref type, you get experimental warnings.
When you do use 540, you won't get an experimental warning anymore,
because hey, fairly simple, fairly stable, seems to be fine.
We also are going to get built-in bundles from used versions.
You know how I said, use v536 implies all of these things?
Well, use v540 will add another one.
So that means when you go, use v540, you get all of these built-ins for free,
which means you can write, use v540, say ref type, and you just get the thing.
And obviously, we're going to put that in with the capital E as well.
So you can just do pearl-capital E, say ref type.
Look at that, that's lovely.
Everyone likes to do ref type in their one-liners.
Yeah, I don't know.
These ones are all bit, like, it's hard to come up with small examples,
but it's nice that they're there.
It's nice that you don't have to ask especially for them.
You just get them.
So yeah, use v540.
So I want to talk a bit about the process behind some of these things.
So we have this thing, the proposed pearl changes.
It's, you speak a lot of C's.
It's a formal process where people can request changes in the language.
So already we've seen in v536 we had the enter time for, that was run by Nick Clark.
We have deferred the Booleans and the name, and, well, no, that says Booleans.
That should say built-ins.
That's a bug.
I wrote those ones.
Xenu wrote the command line flag for slurping.
It's just a small little bit.
Rick wrote the built-in index to one.
These are all the people who wrote the documents.
These aren't necessarily the people who implemented the code.
These are the people who wrote the documents.
So part of the whole PPC process is about saying, if you have an idea for pearl,
but you don't know how to implement it, well, that doesn't matter.
Write us a document to explain the kind of thing you want.
And if we accept it and we like it, we'll say, yes, we will work out how to get that implemented.
You don't have to implement it.
In v538 we got rid of the back tick for the package separators.
That was the Nicholas Mendoza who wrote that one.
Over here you did the module cruising.
And I can't remember who implemented that.
I did some of it, but someone else did.
Who?
Chromatic?
Yeah, Chromatic wrote that one.
Yeah, you just suddenly surprised us one day.
I said, oh, by the way, I've implemented this.
Wow, OK, fine.
Yeah, so we have the module cruising.
That's quite nice.
And the lexical exports.
Sorry about that.
We're going to have to change them.
Yeah, chat to me later.
We're currently testing.
There's only one little thing that we're PPC that we're testing at the moment for 539.
That's the load module built in.
It's going to be quite nice.
It's just a nicer way of doing require where you have a package name in a string.
It's just rather than having to do all of the horribleness of turning it into a file name.
You just go load module.
It's quite nice.
There's a few other ones that we're in the middle of implementing.
So things like English names for punctuation variables.
So rather than doing dollar splat like that, you could just ask for dollar eval error.
It's quite nice.
Template strings.
I'm almost upset you didn't.
You had a sprint if in your code earlier, Rick.
I mean, come on.
So if you would finish the implementation.
Yeah, it's hard.
Sublexing is hard.
So this horrible thing, especially with objects, like if you try to implement it,
if you try to invoke an object accessor inside of a quoted string, you'll know you can't do that.
And so you're always having to break out of the quoted string and stuff like that.
So we've stolen this thing from a few other languages like quote, quote, quote, template strings.
So now you can just put expressions in your code.
It's lovely.
It's nice.
It's horrible to implement.
If anyone knows how to implement it, let me know because I've had about three attempts.
Anyway, other ones that we're in the middle of implementing is optional chaining.
So a Python actually a couple of weeks ago said they were considering this thing.
They call them the none aware operators where you have, you want to do this method call or a hash lookup or whatever it is.
But the thing on the other side might be, well, in Python's case, it's none, but in Paul's case, it's undef.
And you want to just return undef instead.
So we have this wonderful idea of just put a question mark on the operator name.
So that there, if the hash key exists, it'll call name on it.
If the hash doesn't exist or if it's undef, this whole expression is just undef.
And that's often a thing you want to do as well.
It's nice and neat and tidy.
I like it.
And the metaprogramming API.
So all these crazy things that you do with no strict refs and glob refs and all this other stuff that's horrible and messy.
We're going to make that much, much nicer with just you get a meta package and you get the symbol out of it, you get the value in it.
It's all lovely.
It's all inspired by things like package stash.
And there's a bunch of other things on cpan, but we want to make this an official part of core pearl so that we can tie it into things like the object system as well.
It just makes that much more powerful.
A few other little upcoming ideas at some point, but probably not going to be in 540, are I'd like to have named parameters and signatures.
It'd be nice to be able to have these named things here.
But I want to do more stuff on class.
I've not really added anything extra in class for 540.
So roles would be nice.
The convenience accesses might be nice.
It's possible by 540 I'll get around to the easy one like reader, but even something like writer is going to be a little bit awkward.
But even just having readers in 540 might be nice.
I'll see if I can get around to it.
And I've got three minutes left.
Yep.
And the last thing I want to do at some point is renumber 5.whatever into 7 because I really want to be able to type use v7 and just have it work.
And with that, I'm going to say there's the end.
There's a link to the slides.
There's also a link down here to some slides and the video of my talk that I did what's new in 538, which goes into a lot more detail about the new things we added in 538.
And then I will say we will take some short questions, but our minds now the last talking here.
So afterwards I'm going to go for lunch.
If people want bigger chats, we can chat over lunch or in the hallway or something.
So with that small questions.
Yeah.
Question.
In the chat support for thoughts, do you expect or is it hard to plan to implement interfaces?
So the question is about interfaces.
Do we plan to implement interfaces?
I mean, in summary, no.
I mean, Java's idea of an interface is all about defining what kinds of methods you can call on a thing up front.
It's all to do with static typing.
That's exactly all that it is.
And Pearl doesn't have static typing in that sense.
Like if you have an object, you can always at compile time write the code to invoke any method you like.
I mean, maybe at runtime the method may or may not exist, but it doesn't.
Whereas adding the concept of static typing to a dynamic language like Pearl basically turns the entire language upside down.
So the idea of a pure interface isn't really a thing that we want to add.
But we definitely want to add roles because roles are statements about an interface,
but they can also have implementation with them.
So it's all about gluing small bits of functionality together to make a larger class.
So we definitely want roles, but pure abstract interfaces are not really a thing that fits in dynamic languages.
Oh, good.
Comment on the question.
Java allows default implementations now.
Oh, does it?
And residence.
So basically they're roles.
Yeah.
Okay.
They're much nicer than they are.
So we've only got one minute left.
For x, y at array, if dollar y happens to be in depth, how do we know because the value is in depth or because we hit the end of the array?
It doesn't matter at that point.
It's just, oh, with the default argument, the signature parameters thing.
No, the multi four.
Oh.
So I want to know that my array is even sized if I'm pulling out an even size.
Yeah.
So for the, for the, for each, for each when you have multiple arguments, yeah, if, if the size of the array doesn't exactly match, it's like a, not a whole multiple.
You will, you will get just undefs for those last missing positions.
We did think about other bits of behavior, but I think in the end we decided that it just doesn't match because like if you just did my x, y, z equals array.
Like when you get undefs in those last few values, you don't know whether that's because there were undefs in the original array or you just ran out of values.
And so you've got the undefs.
So it's kind of the same thing.
If we did consider implementing something where you could tell the difference, then we'd start to, you'd sort of start to ask questions about, would you put it in other features as well?
So like a, a, a large part of, of kind of trying to do language design is saying, well, we're not just going to do this one isolated feature.
We have to consider how does it play with all these other things?
And so running out of the array is a thing that happens in a lot of places.
So I think that's, that's the end of, of questions now.
So we'll stop there, but if people want to chat more, I'm, I'm happy to chat over lunch, but thank you very much.
Thank you.