-
Notifications
You must be signed in to change notification settings - Fork 62
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Can I use? #379
Comments
Hi @mikeal, Would you be able to provide some details on how you are hoping to make use of R&T? With the latest feedback we have received from the committee we will need to make changes to the design before next attempting to move forwards. The more use-cases we have to draw from the better we can measure how well different designs meet different use-cases. |
Well, one use case is doing ClojureScript in JavaScript, as I've been doing for many years now. ClojureScript provides maps and vectors instead of objects and arrays. Apart from Immutable.js, JavaScript lacks these. As you know, until records and tuples there were no value type counterparts to objects and arrays. Therefore, I've pretty much been using objects and arrays as if they were records and tuples. That is, as a rule of discipline, I write only pure functions against them. So Imagine using the ClojureScript paradigm in JavaScript but without maps and vectors. When records and tuples finally arrive, I can finally cease this practice. I'm thrilled to know records and tuples are on the horizon! I too have been eagerly anticipating their arrival as this will only improve what I've been doing. |
Thanks for those details @mlanza!
If Records and Tuples were still reference values ( |
In my library I define protocols (my own version of the proposal for first-class protocols). In it, I define an equivalence protocol so that I can do exactly what you suggest. In effect that protocol treats the reference types as value types. But I'm looking forward to doing this legitimately and not by working around the difference between reference and value types. In my library I treat objects and arrays as value types using this technique in the large. I break out of using the library when I want to treat them as the reference types they actually are. |
I want to use records and tuples as keys to Maps and Sets for their value comparison semantics.
For me this defeats the whole purpose. |
Same for me. Particularly for use in describing coordinates as pairs/triplets of numbers. I've resorted to using array tuples of numbers and using |
R&T is the most exciting proposed change to javascript i’ve even seen, and my excitement, though grown dormant from years of waiting, is easily reawakened. my use case, at a high level, is using javascript as a functional programming language. more specifically, i want to use it for:
const areEqual = (a instanceof Record || a instanceof Tuple) && (b instanceof Record || b instanceof Tuple) ? RT.equal(a, b) : a === b; though i suppose this theoretical |
I'd like to talk to workers without the structured clone overhead or sharedarraybuffer burden! I'd also like to do the CLJS interop things, in order to have access to clojure libraries. |
You may be interested in https://github.com/tc39/proposal-structs?tab=readme-ov-file#shared-structs |
Is someone / a group championing this proposal and moving it forward? I also feel like this is probably one of the more exciting proposals to come to JS in a long time (maybe biggest since modules?) and would "complete" the language in terms of a complete set of immutable values. This would be especially valuable in reactive libraries, like Vue, where the "reactiveness" becomes difficult and unpredictable depending on what type of value is passed to a property. |
Hi @matthew-dean, this proposal was discussed at the most recent TC39 meeting. The notes for that are available here: https://github.com/tc39/notes/blob/main/meetings/2024-04/april-09.md#discussing-new-directions-for-rt
This sounds interesting, would you be able to provide an example? |
My bad, I think this is more strictly a React issue, where props are passed as-is and components do shallow equality checks to see if props have changed. Vue actually wraps all values / props in proxies, and as a result, crawls objects deeply to recursively set proxies in order to detect changes (I think). That said, I feel like that system is not perfect and I had issues setting values when the values are objects, but I can't currently reproduce it. 🤷♂️ So, I think the use case for a Record would be in systems where values can be anything, and there's some sort of memoization or "change" detection between values. |
I may be a bit out of the loop but I'd like to throw in my two cents. I remember hearing about this proposal years ago. I think I was introduced via this blog post: https://sebastienlorber.com/records-and-tuples-for-react In the time since then, the react team has built a compiler to solve the problem this post outlines. From my understanding, this new compiler is checking to see if you are treating your variables in a way that matches tuples and records. If you are it is able to change your code and optimize it in such a way that your values hold equivalency. To me - this seems like a huge work around to something that would largely be solved by this proposal! I imagine having it directly built into the language would also be way more optimal from both a DX perspective, and a performance perspective. I imagine this use case is already well documented within this proposal. Typescript also has a work around for these features via the I guess I'm just surprised there isn't more excitement around getting this proposal going! It seems like the JS community has been tip-toeing around the idea of immutable data structures for decades at this point. Is the main thing holding this proposal back really more use cases? I'd love to see this proposal become a reality. I'm not super familiar with the proposal process, and have huge respect for everyone working on this. I don't mean to come off as harsh. I'm just really excited for this feature, and a little surprised at the rate of progress. |
Oh, it's definitely needed/wanted. The most popular libraries are using immutable data and there's already a proposal for making signals a primitive. If that happens it'll only lend support to records/tuples because nothing sits inside a state container better than an immutable of some kind. Via this approach one provides a functional core in which effects are simulated. ImmutableJS exists and continues to find an audience. Records/tuples would subsume a portion of that audience. It can't subsume the entire audience because, like Clojure, their maps and sets use the standard hashing strategy (everything provides a hash and a means of equality checking). Records/tuples do not. They can use only primitives as keys. Still, they would be useful for anyone building an app from a functional core, which is what I've been exclusively doing for the past decade. In my library, I've found that for performance reasons one chooses the simplest data structure that works. For example, I recently modeled a game where I started with ImmutableJS and found hashing to be too expensive for hundreds of lookups per second. I dropped ImmutableJS structures and chose simpler one that didn't require hashing lookups and greatly improved the performance. This is a gap that records/tuples would fill. |
The React compiler is able to handle cases that R&T can't. For example the React compiler can memoize functions. It also shifts some of the work to compile time instead of runtime.
The committee have been discussing this idea since at least 2012. Excitement is there, but it's spread out over a decade.
Use cases can be the most valuable way to input into a design. We can compare how the proposal changes both how the code is written and how it performs. They provide the evidence that the proposal solves real issues. They also can help us test if an alternative design would be more/similarly effective.
The proposal hit a roadblock in the design, this has made meaningful progress difficult. Sometimes the most valuable thing to do in this situation is to wait, let the dust settle and see if a new direction opens up. The "easiest" way to reduce the complexity of this proposal is to drop support for I have been wondering if a reduced scope for the proposal of only being considering equal when used in The other core change that could be re-considered is dropping support for |
That's true, but Records and Tuples can also handle things that the React Compiler will never solve. For example: fetching twice the exact same data (ie refreshing it). Even if the React Compiler optimizes the memoization of derived data, the initial "source" of derivation remains newly created objects. https://sebastienlorber.com/records-and-tuples-for-react#:~:text=Fetching%20and%20re%2Dfetching One advantage of Records and Tuples is the ability to "stabilize" the data at the edges of the program (when fetching, parsing search params, reading localStorage etc). If the source is unstable in the first place, derived data will also be unstable. |
Absolutely. Though I don't think external sources are necessarily the best use case for R&T as the access to these requests are usually already being centralised through one place which can be cached and de-duplicate identical responses. One thing that R&T do very well at is when there are separate sources of the same type of data. React's memoization is local to the hook/component, whereas R&T have essentially a global cache. Though that is also where the performance trade off comes. |
Are there ways to add new entries to the Record behaviour here? https://www.npmjs.com/package/@bloomberg/record-tuple-polyfill import { Record } from '@bloomberg/record-tuple-polyfill/lib/index.esm.js'
const rec1 = Record({})
const rec2 = rec1.set('key', 'value') // TypeError: rec1.set is not a function It would be nice if a package exported interfaces resembling the proposed Record and Tuple behaviour without polyfills and without need of any transpilers, frameworks or other sources of churn. |
That package is a polyfill for the proposal so it will only contain functionality that is also part of the proposal. The other limitation is that the current proposal says that records do not have a prototype so there isn't a place to have such a |
thanks I see also that AddPropertyIntoRecordEntriesList is not defined at the polyfill |
Presumably you'd want |
I see, however, it would be less useful if a package interface required one to use spread as it is slow. The benchmark below demonstrates the slowness of spread as compared with immutable-js' map.set, This benchmark can be used with node's official benchmark.js scripts The last number is the rate of operations measured in ops/sec (higher is better). note: node's common.js benchmark file uses one or two commonjs-specific things and those must be updated before the benchmark will run successfully immutable-vs-spread-benchmark.jsimport common from '../node-v22.9.0/benchmark/common.js'
import { Map as ImmutableMap } from 'immutable'
const TYPESIMPLEIMMUTABLE = 'map set (simple), immutable'
const TYPESIMPLEJSSPREAD = 'map set (simple), js spread'
const bench = common.createBenchmark(main, {
n: [1400],
mod: 0,
type: [
TYPESIMPLEIMMUTABLE,
TYPESIMPLEJSSPREAD
]
})
async function main(conf) {
// save then return this obj to try bypassing any
// runtime optimisation identifying this code as unused
let obj = Math.random()
const type = conf.type
const mod = conf.mod
if (type === TYPESIMPLEIMMUTABLE) {
bench.start()
obj = ImmutableMap()
for (let i = conf.n; i--;) {
const key = 'key' + (mod ? i % mod : i)
const value = 'value' + i
obj = obj.set(key, {key, value})
}
bench.end(conf.n)
}
if (type === TYPESIMPLEJSSPREAD) {
bench.start()
obj = {}
for (let i = conf.n; i--;) {
const key = 'key' + (mod ? i % mod : i)
const value = 'value' + i
obj = {...obj, [key]: {key, value}}
}
bench.end(conf.n)
}
return obj
} type="map set (simple), immutable" mod=0 n=1400: 187,746.68573457768
type="map set (simple), js spread" mod=0 n=1400: 4,405.721938920839 |
does the proposed interface assume object spread will be used each time a new record is created from an existing record? |
The proposal is already a large change to the language so anything that wasn't part of the core semantics has been left for a follow on proposal. One idea was a There is also this: https://github.com/tc39/proposal-deep-path-properties-for-record |
acknowledged, thank you for the reply |
Been waiting for this for a long time, am pretty bottlenecked right now on how well I can write new cryptography in JavaScript until this lands in at least v8.
This doesn’t seem to be progressing at the pace far less meaningful language additions seem to make it in. Any thoughts on what is taking so long and anything I can do to help? Most of the beneficiaries of this are uninvolved in standards but if it helps to get more people involved I can do that.
The text was updated successfully, but these errors were encountered: