Video Summary and Transcription
Welcome to the future features of JavaScript, including proposals for array operations, throw expressions, records and TPUs, pipeline operators, and more. The talk covers the introduction of type assertions for imported files, non-mutating array operations, and the simplification of error handling. It also explores the concept of immutability with records and TPUs, and the use of the pipeline operator to simplify code. Other proposals include Map.implace, IteratorHelper, slice notation, type annotations, Array UNIQBY, number ranges, and the Function 1 proposal.
1. Introduction to JavaScript Proposals
Welcome to the future features of JavaScript. We will go through different proposals from stage 0 to stage 3. The TC39 process includes different stages, starting from Stage 0 to Stage 4. Let's dive into and surprise ourselves with some of these proposals, starting with Stage 3. One proposal is about finding values from the end of an array. Another proposal is about finding the last index. The proposal suggests adding convenient methods to the array object. The proposal has been discussed and different names have been suggested. The implementation can be found in the Chromium source.
Hello, JS Nation. Welcome to the future features of JavaScript. In this talk, we will be going through different proposals from stage 0 to stage 3, understand why they exist, what they are trying to solve, and hopefully, they get implemented in all of the JavaScript environments.
I'm Hemant. I'm an Engineering Manager here at PayPal, a TC39 delegate, a Google developer expert for Web and Payments Domain. You can hit me at Newman on Twitter or find me on html.com. The ECMA International has a lot of technical communities, out of which TC39 happens to be the ECMAScript community, which is responsible for the specification that JavaScript implements. The TC39 process includes different stages, starting from Stage 0 to Stage 4, where Stage 4 is finished and implemented in different environments, and Stage 1 is more like a strawman with just a basic idea of what the proposal is. In this talk, we will go from Stage 0 to Stage 3 and see different proposals.
If we look into the current state of how the proposals are, on Stage 0, we have 18 proposals. On Stage 1 we have 91 proposals, and Stage 2 we have 22, and Stage 3 we have 17, and Stage 4 we have 59 proposals. Let's dive into and surprise ourselves with some of these proposals, starting with Stage 3.
Say we have an array of objects and you want to find a particular value from the end of the array. What would you do? You would probably do a reverse on that array and do a find. In this case we're trying to find the values which are not divisible by 2. And in this case we got value as 3, but we had to reverse the array and then do a find. What if we had a convenient method which says, array.findFromLast, array.findLast, here is the condition, give me the element. What if you were to find the last index? You would again do a reverse and do a find.index, and take the array.length and subtract minus 1 from it and subtract that value from whatever we found if the result is matched to find the index. In this case the value was 2. Suppose the condition is not met, where we are looking for number 42. The value should have been minus 1, but it is 4. What if we had a method that said, array.findLastIndex, and when the condition is met it will find the index if it's present, and if not it would give minus 1. Wouldn't it be convenient? Here on the left is a comment from one of the issues in GitHub for this proposal where the idea was to discuss and come up with names. As you can see, some of the names that were popped up in the issue was like findRight, findIndexRight, findLast, findIndexLast, findEnd, findIndexEnd, and so on. Finally we have findLast, and last, findLastIndex as the proposal today. On the right side we can see an implementation from the Chromium source which says fastArray findLast. If you want to open up spec, you could do a one on one mapping to each of those lines in this code to understand how the specification is implemented. For example, on the comment where it says 4, that's point four in the spec which says let k be length minus one, and that's where we have the exact implementation here. That is findLast and findLastIndex. Then we have import assertion.
2. Import Assertion and JSON Modules
This proposal introduces a way to assert the type of imported files in JavaScript. It allows for explicit type declarations when importing JSON, JavaScript, or WebAssembly files, providing increased security and clarity in code. Additionally, there are separate proposals for JSON modules and the decoupling of JSON modules from import assertions. These proposals undergo modifications, discussions, and decisions during the proposal phase. It is important to stay updated on the progress of these proposals, and a recommended talk by Julia at GSNation provides insights into how JS modules work from a browser's perspective.
This kind of proposal gives a way to assert the type that we are importing. For example, in this case, we are saying import json from foo.json, assert type json. We are saying okay this is of type json, and if you were to use dynamic imports, you would say import foo.json and say assert type json when you are doing a dynamic import, and if say you have a JavaScript which is kind of exporting a value and you want to export that value from your file, we could say export value from foo.js which is exporting that value and then you are asserting type to be JavaScript.
And finally, you could use type to be WebAssembly and this can also be used in the script tag. And you might be wondering why should I use an assert type? Why do I need this inline thing? Why not just import JSON from foo.json? Well, it happens just relying on the MIME type to figure out whether it's JSON or not causes a security bug. There's a lot of concerns around security on just importing a random file. And then you have to make sure that you're asserting this is of type JSON and if it is something else, apart from JSON, it would throw and say, hey, I don't know because I'm trying to assert this to be a JSON file and it's not, thereby keeping the code more secure and giving us an easy way to express ourselves in line on what type is this in terms of asserting that this is what we are really importing.
Similarly, we have JSON modules, which is very much similar to what we saw previously, you can import JSON as modules and having this assertion type. But you might be wondering why two different proposals? Is it very much similar to what we just saw previously? Well, if you dig into the notes from the meeting, you can notice here that there was a discussion and the decision that was made it said, one of the decisions was that decouple JSON modules, so that makes into stage two, and then progresses to different states, right? This is a common thing that happens during the phase of a proposal, it might get modified, it gets changed, can branch into two different proposals, it can also get discarded and rejected to a lot of reasons. So here we saw that JSON module was part of the import assertion, but became an independent proposal in itself so that it can accelerate quickly towards the finish. Definitely don't miss this talk from Julia, which is part of GSNation this time and she's talking about how JS modules work from a browser's perspective. She's sitting in the 39 delegate of course and as an implementer in Firefox, this talk is a must watch if you want to know more about how JSON modules work.
3. Change Array by Copy and Array.fromAsync
The proposal 'Change Array by Copy' allows for non-mutating operations on arrays, such as reversing, sorting, splicing, and grouping. These operations return the desired results without modifying the original array. Additionally, the proposal introduces the 'Array.fromAsync' method, which simplifies the conversion of async generators and promises into arrays.
The other proposal we have is Change Array by Copy. As the name says, you're changing an array by copy. Let's see some of the examples here. Say you have a sequence, an array which one, two, three and then you say two reversed, of course you get three to one, but interestingly, the sequence remains the same. We are not mutating the sequence and that's where it's super useful when you're not mutating the array but still getting the reverse of the array.
Similarly you have sort, two sorted, where you want to sort, say in this case, you want And you could say out of order two sorted, you get the sorted array but the original array stays as is. And we have the bit method where if you want to make a change to the array. In this case, the correction needed is the array where we have one, one and three and we want to have 42 in the index one rather than one. So we say correction needed with 142, we get 142, three, but the original array remains as is, that's one, one, three.
Say you want to splice. You could also call the two spliced. Here we have two-on-two so we get 5, 4, 1, 0 from an array which was 5, 4, 3, 2, 1, 0. So we splice that but the input original array remains same and is not changed. This is a very convenient method. We have array groupby and array groupby to map. Let's say we have an inventory with veggies and fruits. In this example, with different quantities. Then you could just say inventory.group by type and you get an object which has the keys as fruit and veggies and the values are array of the respective values from the inventory. Then we have inventory.groupby to map which does very much similar to what groupby addressed but it returns as a map. So we can do all the map operations on the return value unlike like inventory.groupby gives you an object, here you get a map. So this is for convenience so you could do all the map operations. And this has many use case and this is super useful.
Up next, we have stage two. Say we have a generator which kind of yields multiples of two. And, if you were to convert that into an array, so what would you do? Today you would take an array and use an for of await syntax and use it on the generator and keep pushing it to the array and then you would you'll probably get an array. So basically converting that into an array. So what if we had an easy method? Array from async. You give an async generator, you await on it and what you get back is an array. Wouldn't that make life easier with just this one-liner, which is super convenient? And yes, if you are thinking about promises, well, we could do the same thing as we did in the previous example. If your generator is yielding promises and then you want to convert that into an array of promises, then you could just say async from array, generate promises, await on it and you would get an array.
4. Throw Expressions
This proposal introduces throw expressions, allowing for in-line error throwing based on conditions. It simplifies error handling and assertions, providing more concise and readable code. Additionally, it enables throwing errors within function bodies, logical operators, and conditional expressions. The proposal covers various use cases, such as invalid values, unsupported encoding, and avoiding specific methods.
This makes life very easy if you're trying to work on generators and async operators and trying to convert them into an array. So it's async from an array. It's very much similar to an array.from where you could pass an object which is like an array and get it converted to an array if you give it a length. And similarly, it's an extension of that which says array from async rather than just array from and pass it to an object.
The next proposal what we have is throw expressions. Say we have a function which says save, and it is receiving a parameter. And what if the user doesn't pass in a parameter or what if the user passes a parameter that you don't want them to pass. You would probably check the type and do all the required assertions and then throw if it's not matching your expectation. What if this can be done as an expression in line? In this case, you say file name equals throw new error type error argument required. This would throw automatically if that argument is not passed into the save function. Right. You could also have it within the error function bodies. In this case, you have an extract syntax-free AST which has a width method as one of its keys. If someone's trying to use the width method, then you could just throw saying that avoid using the width method which statements. Right. Then you can also use it in logical operators. Say you have a getter and setter within the class. And for setter, you would say if the value is not passed, then you could throw saying that invalid value. Right. If it's not the value that you're looking for. And you could also use it in conditional expressions. And if all your conditional expression exhausts, you can just throw anywhere or saying that unsupported encoding. In this case, you have the different encoders from use the duty of a to so on. That is through expressions.
5. Records and TPUs Proposal
Records and TPUs bring immutability to JavaScript. The syntax with a hash followed by an R represents an object, while a hash followed by an array represents an array. Converting objects to records and arrays to TPUs is possible using the record constructor and TPU from method. Robin Richard's talk provides detailed insights into records and TPUs as immutable data structures in JavaScript.
Records and TPUs are one of the most important and interesting proposals in the recent past, which kind of brings in immutability to JavaScript. So we all know when we do strict equalities of two objects with the same key value pairs. They are false. Even the same holds for others with the same values. But we do a strict equality tells false. But if you have this syntax where you have a hash followed for an R, for what looks like an object, you tell, and your hash followed for what's look like an array. They both equate themselves to be true. And that is according to feel where you could say type of hash, which looks like an object, which is immutable. It's a record. And type of hash, which looks like an array, which is immutable. Again, is a TPU. So if you have an object, which you want to convert into a record, you could just call it with a record constructor and you would get the record back. Or if you have an array, which you want to convert into TPU, you could set TPU from, pass it an array and you'll get TPU back, and definitely do watch Robin Richard's talk on records and TPU immutable data structures in JavaScript, where he goes in detail. Also a TC39 delegate. And this is part of the JS Nation talk series, too.
6. Pipeline Operator and Competing Proposals
The pipeline operator simplifies nested and convoluted code by allowing a more straightforward syntax. It replaces complex nesting with a concise and readable format using the pipe symbol and the dollar-dollar percentage notation. This operator makes it easier to read, understand, and modify code. Currently, there are two competing proposals for pipeline operators, hack and fsharp pipelines, with a third proposal being withdrawn.
Next, we have the pipeline operator. Say, we have this example, which is Funzal logging chalk.dim, object.keys of environment variables, mapping or environment variables and taking the key value pairs and joining it. Finally, joining the entire arcs to kind of create an output. Isn't this bit confusing? Like, let's see how the brain probably would try to evaluate this. It would say object.keys from left side, then we have map on the right side and we have join on the right side. And the template on both sides, the tilde characters and then we have chalk.dim and then we have Funzal log. It's a lot of nesting and convoluted code. What if we were to make it more easier? Right? Let's see this example, where it's just object.keys of environment variables, map the environment variables, return me this string, and then join. And then you have this strange looking syntax, which says pipe and then it says dollar-dollar percentage where percentage basically is the value that evaluated in the previous step. Then we say chalk.dim percentage. Again, that percentage is replaced by the value that was evaluated in the previous step. And finally, we console.log percentage. That's the value that's evaluated in the previous step. Thereby making it easy to read and understand, also to change different methods. And that's a pipeline operator. There are two competing proposals for pipeline operators, hack and fsharp pipelines. But there was another proposal, which was kind of a smart mix of these two proposals. But that has been withdrawn. This is what we have currently today for the pipeline operator.
7. Map.implace Proposal
Map.implace proposal simplifies the process of checking if a key exists in a map before setting it. It avoids unnecessary lookups by providing convenient methods for inserting and updating values within the map.
Map.implace is another interesting proposal where, say, you have a map and you want to check whether the key exists within the map or not before setting it, which is a common use case, right? You'd say, if notMap hasKey, then map.setKeyValue, then you do map.get and do things. What if it could be more simpler? And you say, map.implaceKey, insert, value, do, do that thing. Right? It would basically check if that is there or not and then insert. And probably would have also done this. map.get get the old value. If the old value is not there, then set the value and set the updated value. What if we could avoid these two lookups and make it more simpler? You could do map.implace and put a, you have put a key there and you have update and insert methods within implace and you could operate based on your use case and map.implace makes more simpler in avoiding those extra lookups.
8. IteratorHelper Proposal
IteratorHelper proposal provides a way to perform various operations on iterators, such as mapping, filtering, taking, dropping, and iterating over index pairs. It simplifies the process of working with async iterators and provides convenient methods like forEach, every, some, and find. This proposal is currently at stage one.
IteratorHelper is a large proposal and let me try to fit it in this slide and see the different things that IteratorHelper provides. Right? So say you have a function, which is a generator function, which kind of generates all the natural numbers and of course you would do result.next, you keep getting those numbers. What if you were to do a map on this iterator? Yes. This proposal talks about it. You could do a map. Want to do a filter. Yes, we could do a filter. And here's an example, you have an async iterator from URLs, you get an async iterator, then do a map on it, then do an await fetch and then get the response JSON and finally do a two array so you have an array of all of those responses as JSONs, right? And then you want to do a take, if you want to be a bit lazy, and just take only three of them from those huge series that this generator is providing, you could do that. If you want to drop three, you could do that. If you want to have an, if you do dot values, you get that iterator and you want to do as index pairs, then you get an index pair, that that would be like a zero x and one y and two z and so on. Alright, and then you could also do a for each, every, some, find, all of this on value, so that's Iterator helper, where it makes life easier to iterate over all of these different methods and having a sync iterator. Now we have stage one.
9. Array Slice Notation
Slice notation in JavaScript arrays allows for easy extraction of elements based on index ranges. It provides a more intuitive way to retrieve specific elements, whether from the beginning, end, or any position in the array. This feature is widely used in other programming languages and would be a valuable addition to JavaScript.
Say you have an array of a, b, c, d and you want to do array.slice one, three. What do we get? Is it b, c, or b, c, d? Say we have an array of a, b, c, d again, and we do array.slice. What do we get? a, b, c, or d, right? Well, this can be easier with slice notation, which is bit intuitive, where you say, array 3 colon, that means give me d, and if I say 1 colon 3, that's b, c, give me b, c. If array of minus 2, start from the end, give me a, b. Array of minus 10, which is out of bonds, give me an empty array. So if you were from the Python land, you would have probably used this a lot, and many other languages do provide this facility, and it would be super nice to see this in JavaScript.
10. Type Annotation Proposal
Type annotation is a proposal that introduces syntax similar to TypeScript for adding annotations to methods. It allows for explicit type declarations, including optional parameters and default values. This proposal is an evolving one and worth exploring for more details.
Type annotation is a big proposal and created a lot of noise in the public domain, and too many people are interested because of how people are used to TypeScript these days in terms of using type. Here's what the proposal basically is about. If you're used to JS doc, you'd probably have used something like this, where you said, hey, here's my string, string, string function, right from the reading of the proposal, where we have p1 as a param, which is a string, and p2 is a string, and so on. And this is how you'd probably put it in your JS doc. And with this proposal, it looks very much similar to the TypeScript syntax here, where you'd say p1 is string, p2 is optional, where it's a string, and p2, p3 is optional in string, and p4 is defaulting to test. And this function returns a string, right? So type annotation gives us this kind of a syntax where you could add the annotations to your methods. And it's an evolving proposal, and it's very interesting to read about the details.
11. Array UNIQBY Proposal
Array UNIQBY allows you to get unique values from an array with duplicates, including objects and specific properties.
Next, we have array UNIQBY. As the name says, say you have an array of, in this case, we have 1, 2, 3, 3, 2, 1, and we do an UNIQBY, we get 1, 2, 3. But it gets interesting if the array has objects, right? And you want to do UNIQBY UID, then you could do that. Or you want to do an UNIQBY which takes an ID UID, you want to do those pairs and probably get UNIQBY, you could get that too. You could get that too. So UNIQBY is a very convenient method to kind of get unique values from an array which has duplicates.
12. Number Range and Big Int Range Proposal
Number range and big int range proposal introduces a range method for big int and numbers. It allows for creating number ranges and filtering them based on specific conditions.
Number range and big int range is a proposal that kind of gives this range method on big int and numbers. See in the first example, we have a far-off loop where the big int range starts from 0 into 43N, which kind of locks 0 to 42N numbers. If you're referring to the iterator helper that we previously saw, where you can do a number range from 0 to infinity, take say a thousand of them and filter the numbers that are not divisible by 3 and then do a 2-array, you would get those numbers.
Or say you have a generator which generates just even numbers from 0 to infinity and you just want numbers from, like get all the odd numbers from say 1 to 99. You could do number range 1 to 100 of 2, then you would get those. So that's the range method on number and begin range.
13. Function 1 Proposal
Function 1 proposal allows for executing a function only once, returning the same value when invoked subsequently. Interesting discussions revolve around recursive code and async operations. It is recommended to read the issues and contribute opinions.
Function 1 is in other proposal that's interesting. Say you have a function that you want to execute only once for various reasons, and then say in this example, you say a function once and you say F.1's as F1's and F1's of 3 would print 3 and return 6. That's the console log that's there in the function. But F, again, if you call the same function, F1's with 3, it doesn't print anything but return 6. F1's of 2, also return 6, doesn't print anything, right? So it's just execute once and if you keep executing it, again, you just get back the same value that was evaluated when it was invoked for the very first time. There are some interesting discussions in the issue for function 1. Say you have a function which is a recursive code and you do a G.once, and then you call G once, what should happen? Or say you have a function, g of x, which does stuff which takes a lot of time and has async operations in it. Then you say, async G once and you get promise 1 and you do async G once again, you get a promise 2, what should happen? So these are some of the interesting discussions that happens in proposal and it's a good practice to go and read the issues and try to dig and understand what's happening and we could also contribute with your opinions.
Comments