Video details

The Next Exciting JavaScript Feature: Records & Tuples

JavaScript
11.05.2022
English

Immutability and deep equality. Two keywords that were initially relegated to strict functional programming environments, but have been popularized across the JavaScript community thanks to libraries like Immutable.js, Immer and React. "Records & Tuples" is a new ECMAScript proposal that brings two new immutable primitives to the language: what do they look like, what capabilities do they bring, and when will you finally be able to use them?
https://2022.jsconf.kr/en/speakers/nicol-ribaudo
Nicolò is an Open Source Developer, a maintainer of Babel — the JavaScript compiler — and a contributor to different JavaScript tools. They are a TC39 delegate, and they helped develop different TC39 proposals over the years. When offline, he is a math student in Turin, Italy.

Transcript

Okay. Hello everyone, I am Nikola and I will talk to you about records and tattoos, which is a proposal for a new JavaScript syntax. I'm sorry, it's lunchtime. I'm with hundreds now and and those are not food. However, if you stay here with me, I have stickers at the end of represents apple. So then come and ask me, I have a lot of them. So I worked on this Asgardia in partship with Bloomberg. Before seeing what argues on Apples, let's first see what we are trying to solve with this new JavaScript feature. So what is our problem? When programming, there are two main parties, two main ways to manage your state, your data. You can have mutable objects or immutable state, and those are usually used in object oriented programming and in functional programming. However, in many languages, such as JavaScript, you can use both. You don't really have to choose, you can mix them. And in JavaScript, while the immutable part is really good, like we have classes, we have a lot of different things, we don't really have good primitives for immutable state. There are some very good JavaScript libraries that gives us disclibility, they are just not included in the language itself. For example, the most popular one is Immobile JS. How many of you have ever used immotal JS? Well, quite a few. Nice. I've almost never used Emotable JS, just like one or two times. However, just for those who don't know what it is, it's a library that provides some structures parallel to the builtin Joseph objects, but it works in an immovable way. For example, we have maps which are kind of like objects, and you can update those immutable data structures without modifying the original ones, but creating a copy. So for example, we can create a new map where b is four instead of two, the original map is not modified, and then immotal JS provides some utilities to compare to immutable data structures. For example, we can check if two maps are equal according to multiple GS, and it has some utilities to convert code back to plain JavaScript. Another proper library is emer. Emer uses plain JavaScript objects, so you don't have to learn these parallel immutable data structures, and it provides some utilities to update them in a way that looks mutable but doesn't really change the original object. So the original object remains unchanged, and we get a new object with updated property. And this is again a really good library. The only problem is that, well, it has some problems that I'm going to show. The main one is that it's a library, so you have to include it in your code and it does not just work. And so as I was saying, every library has its own problems, and this is not because of the library, it's because JavaScript does not give them the tools necessary to build something that works perfectly. So one of the problems that you have to learn a completely different things. You have to learn like this new equivalent of objects and arrays, you have to learn how to get properties from them, how to update them and so for example, you cannot write code that works both with plain Jjusted objects and with immutable JS, you have to choose. And another big problem is that equality is not agreed upon across everything because JavaScript has its own definition of equality, while for example Immutable JS has its own different definition of equality. So for example, if you create a JavaScript set containing an immutable list that only contains zero one, according to JavaScript, this set does not contain this other list which looks the same. However, according to the Mutable set, to the set in the Immutable Jsors, the quality here works. And so we're like checking if this list is contained returns true, and this is a problem because every library you use, you have to check if it works or not. With the immutable library that you're using. As a practical example, assuming that you have a React component and you are using Immutable JS, this is a component that at some point gets rendered the game. And let's assume that map renderer is a component that renders the map the Mutable map that we have, and that for some reason this is a very expensive operation, so we want to skip rendering the nested component if possible. However, and we're always passing the same map, or at least a map with the same content to this component, so in theory React could keep rendering it. However, the map is always the same according to Mutable JS. Like if we use the quality function of Mutable JS, it's true for all those maps. However, according to JavaScript, they're all different maps because they're all different objects. And so React rerenders the map render component every time. Okay, so with the Records and Apples proposal, we are trying to solve these problems. Records and Apples are a new type of adjustive value. They are similar to object and arrays. They are just immutable, they even have the same syntax, except that it's perfect within hash. And Records and Apples are compared by their contents rather than by reference or by pointer, which is how objects are compared. Like if you have two objects with the same content, they can still be the same object. This is not true. For example, two records of Tupples with the same contents are really the same. Records and Tuples are primitive, so they have their own type of so you can easily check if something is a record rather than a classic object and they can only contain other primitives. Why there are primitives? Because primitives are already compared checking their contents. For example, two strings are equal if they contain the same charters, even if you didn't create them in the same place. And it's easy to define what deep equality means some primitives because you can just recursively check their contents. For objects, this is more difficult because as we saw, for example with Moto GS, there can be different opinions regarding the quality of objects. And also only using primitives gives us deep immutability guarantees, so giving us some stronger security guarantees without having to remember, for example, to freeze every object every time and a bit more in detail. How are records and tables compared? Well, first recursively so two records are equal if every property of the record is equal. And if you find other records and tuples, you check their contents again, tuples are ordered because they're like a race. So two tuples are the same not only if they contain the same values, but also if they are the same order and records have not. So for records you only check that for every given property name you have the same value regardless of the definition order. In records and tuples, N-A-N values are equal and positive and negative zero are equal. And this is kind of an edge case, but it's something very important to preserve because it makes it possible to not worry about zeros and nuns instead of deep records and tables structures. And you can still see if two records or two tuples containing positive zero and negative zero are different using Object or TS, which is how we already check if you have a positive or negative zero like outside of regular tuple. And finally, since we defined what it means to be equal for record or tuple directly in the language, it works everywhere. So for example, it works in mapping sets. You can have an app where the key is a tuple or a record. So you can have like a compound key, multiple value in the same key. And also every library, every JavaScript library that exists would already know how equality for records and tapas works because it's just the normal JavaScript quality. So if we look again at our React example that's using a record rather than an Immutable JS map, this time, React does not render the map render component every time because the data is the same like for real or actually according to React, because it's the same according to JavaScript and not to some specific library that we chose. Okay, so records and tables are immutable, but apps are dynamic, so we need to update our state. How do we update records and tables? We do it in the same way as we update it's not going to update as we create new objects with different contents without modifying original objects. So we can just use spread for example, to replace some properties records. And for tupels we can use all the ray methods that do not mutate the ray such as filter or map or produce all the methods that return a new value without modifying the original value. And what about the other array methods? So for example, array to push array to shift. Well, for those it's easy. You don't really need those methods. You can just use spread. So there are no alternatives of those methods that modify the original array. You just create a new tuple, spreading the contents of the original one and adding one element at the end or at the beginning. And the same is for pop and shift. You can just use lies. Then we have a red dot field and a red dot copy within. Those methods are really specialized for Mutating stuff, and it does not make sense to have any Mutable version of them because they are used for some performance critical things where you need to do as few operations as possible. And this is just not possible with Tuples. So they're not supported. And what about the other four array? Well, there are three ariments and then the setting and all elements. It makes sense to sort a tuple to get the sorted version of a tuple. The problem is that the sort array method modifies the original array. And so this doesn't work on immutable things. So let's put aside for a moment the recordsintoplas proposal and let's take a quick look at the change array by copy proposal, which was born as a child proposal of the records in tapas one, have you ever written like array slice, reverse or red slide source? I have many times, because many times I want to reverse an array without modifying the original version and having to use two methods. It's not really clear, like if I'm reading this code, why do you need to firstlice it without any arguments to then reverse it or to then sort it? So the changing Rebecca copy proposal introduces alternatives to these methods that mutate the array when it's not really necessary. For example, instead of array sort, we now also have array two sorted which returns a new array which is just well sorted. And the same for array that reverse, we have to reverse and instead of spliced, we have to splice. And lastly for setting a specific element in the array or well, for creating a new array where a specific element has been replaced, we could use to splice in theory, but that's probably the most hard to use array methods. So we are also introducing these array twists that like to specify a specific entry of the array. And all these methods have been produced array to type the array so that they are all consistent. And you can also use them on topples so that you can just memorize this shared API that works across all the arraylike builtins. And you don't have to learn different things depending on whether you want multiple things or immutable things. Okay. Back to records and apples. We've now seen that records and Apples are compared recursively that they are primitives, contain primitives. We've seen which methods we can use on them. There is one more problem. There are primitives technology contain other primitives. But JavaScript is full of objects like the Dom is objects. Every function is an object. We have mapsets, text arrays, promises, almost everything is an object in JavaScript. And we cannot just say, okay, you chose to use Records and Tables, you cannot use objects anymore. That's not realistic. So how do you reference objects instead of Records and taboos? Well, we have explored different alternatives and none of them was really good. Preserving the security and the mutability guarantees the Records and Tables give, because if the goal, like referencing mutable objects and having full immutability are conflicting goals and there is a good way to make them work together. So the solution is Records and Tuples still cannot contain objects, but you can reference objects using like symbol placeholders. So you can put a symbol in the record and have a side table saying, okay, this symbol corresponds to this object, and then a side table the other way around saying this object corresponds to this symbol. So that you have this one to one mapping and you can translate every object to corresponding primitive. So if I want to guess the Onclick function for this record, I get this corresponding symbol and then I check, okay, what is the function corresponding to this symbol? And this might seem really complex, but in practice all that part can be easily obstructed by a library. There is no library in NPM yet to do so just because Records and Table is only a proposal and it's not implemented yet in most engines. But there is a problem with this approach, which is garbage collection. So, garbage collection is a mechanism that the browser has to avoid using a lot of memory. So if you create many objects, as soon as those objects are not reachable anymore from your code, which means that they're not like stored in a variable or stored in a property of another object, the Java setting can delete them from memory, those frames and memory. The problem with using maps is that if you put objects in maps, they cannot be garbage collected anymore because you could always like as long as you have access to the map, you could iterate over its elements and get all its keys. So these objects would last forever and your application would start leaking a lot of memory and at some point it might crash. And the solution to this is to use wigmaps rather than maps. The only problem is that weak maps cannot contain symbols. So a third proposal, which again was born from Records and Tables, is a symbol, since with Maki's proposal. So what does it do? Well, the name might be self explicit. So we can divide JavaScript values into categories. We have values that can be garbage collected and values that cannot. I'm calling them forgible values and I'll explain why in a moment. So well, I explained. Now let's first look at the Fortune values column forcible values are values that you can recreate. So if you have a string that contains some contents and then you later stop referencing the string, in theory the engine could garbage collective but then you can create the same string again, like even outside of a program, you could fetch it from a server or create it from the number component. So you could create the same string again and you could observe using maps or weak maps that the original value had been deleted. On the other hand, garbage collectible values are values that you cannot recreate anymore. So if you have an object and you lose access to the object, there is no way you can create an object which is the same one. Because for two object to be the same one, it means that not that it had the same contents, but that it was really created in that single place. And so we have unique symbols that were a unique by definition, so they're also garbage collectible. And we have on the other side registered symbols which are symbols that are not unique and you can recreate the same symbol every time by giving to the symbol to a function the same description. It's kind of like symbol iterator or symbol dot symbol iterator that you can reuse many times. And lastly, with this proposal we will also have records and tupels which are kind of both sides and this is strange, but if you think about it, a record that contains a symbol that you cannot recreate can be garbage collected because if you don't have the symbol anymore, you cannot recreate the same record anymore. While a record that contains, for example in strings is forgivable because you can just take the same string, like recreate the same strings, put them together to create the record. At the moment, only objects are allowed as weak map keys. And as you might imagine, this proposal extends the valid weak map keys to also unique symbols so that we can now use wickmaps to actually have this symbol to object mapping, to put things in records and tapas. So this was a very left way to review, I didn't but there is one big question like I've shown you like records and tables change methods, symbols with Mackays but is this something that you can actually use? Like can you go home and start using them tomorrow or on Monday, tomorrow and Sunday don't use them? Well, yes and no. So those are only proposals. They will hopefully be added to a JavaScript version in the future. They are at a good stage in the standardization process so it's at this point certain that will be added. It will take time. However, you can start using some of them for the change. Arabic Copy there are already many polyfills such as Eschims and Coregs. Core JS is quite popular, maybe some of you already use it and also it has been released in Safari 16 last week. So I just updated this live yesterday night because I saw a tweet about these Safari's notes and changing my copy was there. So you can already start using this proposal in a browser right now. Symbols with McKees is unfortunately not polyfillable because it's adding a real new capability to the language like something that cannot be done in any other way and it has not been released yet in any browser. So we can only leak memory for now. One day, hopefully next year browsers will start implementing symbols with no keys and we can actually have these mappings in a memory safe way. And finally records and Tables well represented ups is new syntax so you cannot really just polyfill that. However, there is a Beabel plugin. Does any one of you use Be Able? Nice. Okay. There is a Beable plugin for those who know Be Able lets you compile mother JavaScript syntax or proposals to all their syntax so that you can use them even in other browsers or in browsers that do not support the syntax that you want to use. There is a Beble plugin that allows you to compare a constant apple so that it works in every browser. And I don't suggest using that in production because proposals can change. There are a lot of very cool just proposals and it's very important that people test them so that we see if they work or not. Like if it's a good idea or not, but please never use those in production yet. And also we have an online playground. So there is an online playground where you can actually type your records in Apples code and see how it works, see how quality works, test. I think it also supports the change directly copy methods. So it's kind of like a browser console except that it supports circles and tuples. You probably don't need to copy the link. Like if you Google for record Tuple playground it will be one of the first few results and that's all. If you have any questions, I'm happy to answer them. Just look for me around. I'm the one with the pink hoodie. You can also contact me on Twitter for any question related like either to these proposals I'm working on or to other just proposals or anything that I do that you might want to know to want about. Or you can contact me on Matrix which no one knew but no one uses, but in my opinion is the best chat protocol that exists. And that's all, thanks and let's all have lunch now.