Hacker News new | past | comments | ask | show | jobs | submit login

I hope Records & Tuples[0] land before this does. It would have meaningful and far reaching positive effects for the language, without much controversy. Like most of these things, it takes about 5-7 years for it to permeate through enough of the engines to be meaningfully useful in the day to day of web developers (node / deno typically 12-18 months tops). It would drastically speed up existing code once wide adoption is gained though.

I don't think the Pipe Operator would be as useful in comparison.

I really hate how long the Records & Tuple proposal has been languishing at stage 2. It could've shipped years ago if not for a syntax debate that dragged on and on without being fruitful[1]

EDIT: there is a class based version of this, that is stage one, for adding structs[2]. They behave similarly.

[0]: https://github.com/tc39/proposal-record-tuple

[1]: https://github.com/tc39/proposal-record-tuple/issues/10

[2]: https://github.com/tc39/proposal-structs




The one I really want is “do expressions”. I also can’t understand why anyone wants private fields over these kind of improvements. Lack of private fields in JS has literally never caused a big for me in 15 years of JavaScript development, but I have to use mutable variables and ugly if-else chains to 3 case assignment all the time!


"do expressions" is one of the things mentioned in the Pipe Operator proposal as possibly a pre-req and one of many reasons the Pipe Operator seems to have stalled out at Stage 2 despite multiple attempts to push it past.

Personally, for the 3-case assignment I would love to see one of the smarter pattern matching "match" expression proposals make it further along over the over-generalized "do expressions". Especially now that C# has pattern matching switch expressions.


You might already know, but until do expressions land, you can use IIFEs for ifs and switches assignments:

    const isSomething = (() => {
        switch (...) {
            case ...:
                return ...
        }
    })()
They are a bit ugly but the do wonders at avoiding `let`.


I've been wishing for this for years.

It opens up whole new ways of doing stuff when tuples and records are just primitives. They never mutate, so representing them efficiently right from the start is possible. They are primitives, so passing should be easier to do.

Concurrency becomes a lot easier to add to the language because if you limit it to primitives (which are all immutable), you get a lot of safety guarantees. I'd love to see either channels or actors baked into the language in the future.

Unfortunately, I don't think the pipe operator is the issue. Both of the proposals are just syntax. Implementing either just consists of a very straight-forward translation into already-existing syntax then running through the rest of the JIT as normal.

The real issue is stuff like the private variables in classes garbage. It added unnecessary complexity to the syntax (and is very ugly and perl-like). I've never met a senior JS dev (outside of TC39) who actually wanted or used it. Despite this, the JIT engineers couldn't wait to refactor all kinds of stuff all across the JIT to make this happen.

The time spent implementing private variables SHOULD have been spent implementing the infinitely more useful records and tuples.


alot of the class stuff is driven by the vendors. Google & Microsoft tend to be the big champions of the class stuff, like decorators, decorator metadata, the new struct proposal, private fields.

They want classes to be more unique than plain objects (when initially introduced, aside from easy extending via `extend`, they really weren't in practice). You can see it in their frameworks and how they do development. I'm not shocked those proposals get traction quickly because they come from the vendors themselves. I imagine internally their many thousands of engineers take advantage of these features. It just isn't as common (since `class` received a ton of backlash in the JS community mindshare with a broader move to functional style programming)

Records & Tuples on the other hand, came from Bloomberg (largely) and later Meta endorsed it. Unsurprisingly, it solves real problems they face. React (and anything that uses diffing today in the same manner) would be sped up dramatically if they could just `===` two objects vs what they do today. Yet this languishes, despite being a nearly universal upside for the average language user.


Is the point of those just immutability or something else too?

Wouldn't it be better to have a way to easily make any type deeply immutable?

EDIT:

I think tuples and records are also supposed to have value semantics, which is great, but why not a mechanism that turns on value semantics for arbitrary object type instead.

Also value semantics is separate concept from immutability. Maybe there should have separate ways to get expressed rather that combining them in form of just tuples and records.

To sum up, I can see why the community is hesitant.


Objects carry prototypes and mutability with them. Further, all primitives/value types of the language are immutable.

Adding those features to an Object would still necessitate creating a new primitive.

At that point, you might as well make that primitive record that is directly available and just use the constructor to convert (just like you'd use `Number("123")`, you'd use `Record({my: "object"})`).


> Objects carry prototypes and mutability with them.

You can deep freeze the objects and freeze their prototypes.


It's not the same as tuples and records. You can do the following with them:

  const first = #{a: 'b', c: 'd'};
  const second = #{a: 'b', c: 'd'};
  first === second // true
So no more deep comparing of objects if they have the same properties.


Fascinating. I would have guessed that identity checks are still possible with ===, and == would use a structural comparison.

I suppose V8 for example could easily optimize records and tuples which are structurally equal into the same memory, then separate them into distinct memory blocks when they differ. This way an equality check could have the speed of an identity check if the two are known to be structurally equal.

I could be talking out of my ass here – maybe this isn't a performance concern at all and has been addressed far better already, and this is nonsense. But I do wonder how you'd quickly check for equality of, say, a large state tree to discover changes. On one hand that could be addressed by architecture. On the other, it is nice sometimes to simply know if two variables reference the same memory.


Yes. This fixes only immutability. For the value semantics (=== comaprison of content) pls check sibling comment thread.


as outlined in the proposal, these new types have engine level guarantees, and support using a plain triple equals (`===`) statement to test structural equality. It also follows that strucural equality trickles down into objects that do this implicitly (Map, Set etc) as well.

Doing this for plain arrays and objects has been said by the vendors for years to be too hard and possibly break alot of existing applications, and a new type for this purpose would need to be proposed. This is an answer to that. There isn't much to be semantically gained by allowing this on frozen objects, you'd need to do something identical to this anyway, from an engine perspective. Using a new syntax / types to represent it semantically makes it easier for developers to understand it too, I'd argue.


You could create class that keeps static Map with references to all created objects of given type. On creation it would check if structurally same object was already created and return reference to previous instance instead of creating a new one.

If you freeze those objects and their prototypes you get exactly your records. === becomes structural comparison that costs exactly the same as reference comparison.

I made something like that for my own purposes for very simple types like Point.

To implement this you'd just need to be able to turn contents of an object into a key for a map. I agree that it's probably better to do this rather in the VM than in the library.


I believe this approach was proposed, but implementers pushed back because the initial interning is expensive and there are a lot of scenarios where it would never be used. You don't really expect creating a Point to do a hash lookup.


Sadly, JS weirdness interferes with value semantics:

    > 0 === -0
    true
    > #{v: 0} === #{v: -0}
    ???


That's not a JS thing, 0 and -0 comparing as equal is specified in the IEEE 754 floating point standard.


It's the JS `===` operator. The semantics are defined by JS. JS can use IEEE754 semantics for numeric `===` comparisons if it wants, but it'll be at a cost if there are other ways to distinguish the two. In this case, if you make `#{v: 0} === #{v: -0}` then you're ok with `a === b && 1/a.v !== 1/b.v` being possible.

I could see an argument for `0 == -0 && 0 !== -0`. It would have made it possible to say "if a === b, then a and b can use the same bitwise representation internally without losing information". But that's not what we have.

I guess for maximum consistency, we could use the 3rd JS equality operator `Object.is` with records, so that

    0 == -0
    0 === -0
    ! Object.is(0, -0)
    #{v: 0} == #{v: -0}
    #{v: 0} === #{v: -0}
    ! Object.is(#{v: 0}, #{v: -0})
but then all Record-comparing code would have to do `Object.is()` in order to avoid a recursive comparison. (Well, maybe the engines would get clever and include a "contains -0" flag in Records, and skip the recursion if it's false for both sides?)




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: