Hacker Newsnew | past | comments | ask | show | jobs | submit | _old_dude_'s commentslogin

For the record (sorry), I believe C# uses the clone operation because records support inheritance.

For me, this is where lies the design flaw, trying to support both inheritance and be immutability at the same time.


The inheritance + immutability combination forces the compiler to use field-by-field copying rather than constructor chaining, which bypasses the property initialization logic that would maintain consistency between related fields.


Cloning anything creates a new object of a known type (well, the runtime knows at least) and so if the object re-runs the init-properties of the known type then it will be the same as constructing that type afresh.

You could even imagine a compiler generated virtual method: `OnCloneReinitialiseFields()`, or something, that just re-ran the init-property setters (post clone operation).

Is there some other inheritance issue that is problematic here? Immutability isn't a concern, it's purely about what happens after cloning an object, whether the fields are immutable or not doesn't change the behaviour of the `with` operation.


It was never immutable? You can have collections on there that are perfectly mutable as well as being able to set values? You CAN use the with keyword, but that's a choice.

I've seen people use records for value based equality and to use for things like dictionary keys. Immutability in c# just doesn't exist, any attempt to achieve it is flawed from the start


> Libya was bombed primarily by France and then other NATO countries for no good reason

https://en.wikipedia.org/wiki/Alleged_Libyan_financing_in_th...


The old mantra of bombing for peace and having sex for virginity, no?


And AISQL is a thing ...

  https://quickstarts.snowflake.com/guide/getting-started-with-cortex-aisql/index.html
I'm glad i'm retired.


Java Almanac has a list of all features https://javaalmanac.io/features/

And the main page let you compare API versions https://javaalmanac.io/


> at least I know that the phone will be supported for at least 4 years

It's 4 (mid) to 7 years (flagship) for Samsung.

https://www.androidauthority.com/samsung-android-updates-114...


OOP is great for libraries but leads to overly complex codes for applications.

For a long time, Java was like, every classes is a library, i do not think it's a failure of OOP, it's a failure of Java.

But I'm optimistic, I choose to see recent additions like records and pattern matching has a step in the right direction.


I've just tried to change a field of an instance of a record using reflection, and it's not possible, even with setAccessible(true).


ooops, my bad. I remember, that Aleksey Shipilëv explained why even `final static` fields are not constant-folded by JIT, and thought that record classes which were introduced later, is the same.

It means, that `StableValue<>` can be used in simple classes (where `final` fields are still not constant-folded) and, additionally, supports late initialization.


Yeah the Java team took the opportunity while introducing the new record feature to explicitly forbid mutating their final fields. Their intention is to eventually enforce the same invariants across all Java fields unless the JVM is passed an explicit opt-out flag. And if you're not reliant on any frameworks using reflection to modify final fields, there's already a flag you can use today which will instruct the JVM to "trust" and apply optimizations to `final`.


In Java, constants are declared as static final.

  static final Complex CONSTANT = new Complex(1, 2);
If you want a lazy initialized constant, you want a stable value

  static final StableValue<Complex> STABLE_VALUE = StableValue.of();

  Complex getLazyConstant() {
    return STABLE_VALUE.orElseGet(() -> new Complex(1, 2))
  }
If you want the fields of a constant to be constant too, Complex has to be declared has a record.


In JavaScript, a 'let' inside the initializer of a for loop is captured by value, all the others are captured by reference.

I think it's fair to call that semantics "implicit".


No, that's a mistake in the article. The variable is still captured by reference, but `let` is causing it to be re-declared on every iteration of the loop, not mutated.

The following code prints 1, 2, 3. It wouldn't do that if the variable was captured by value.

    for (let i = 0; i < 3;) {
        setTimeout(() => console.log(i));
        i++;
    }


The behavior of "let" with for loops where the variable is declared more times than it is initialized, despite the source code having one declaration that is also the only initialization, is not very explicit.


Fair, but that's a property of for loops. Variable in closures are still always captured by reference.


consider this

   for (let i=0;i<3;i++) {
       i+=10;
       setTimeout(_=>console.log(i),30);
       i-=10
     }
Capture by value would print 10, 11, 12 that's the value when it was captured

Capture by reference would print 0,1,2

It's much easier to conceptualise it as

     for (const i=0;i<3;i++) {
      setTimeout(_=>console.log(i),30);
     }
which is fine because i never changes. It is a different i each time.

fancier example

    for (let x = 0, y = 0; y < 2; x=x++<3?x:y++,0){
        x+=10;
        y+=10;
        console.log("inline:",x,y);
        setTimeout(_=>console.log("timeout",x,y),30);
        x-=10;
        y-=10;
    }


> which is fine because i never changes. It is a different i each time.

This is clearly a super weird hack to make closure capture behave more like you'd want. There's a reason this level of weirdness isn't needed in C++.


Parroting something i have heard at a Java conference several years ago, tail recursion remove stack frames but the security model is based on stack frames, so it has to be a JVM optimization, not a compiler optimization.

I've no idea if this fact still holds when the security manager will be removed.


The security manager was removed (well, “permanently disabled”) in Java 24. As you note, the permissions available at any given point can depend on the permissions of the code on the stack, and TCO affects this. Removal of the SM thus removes one impediment to TCO.

However, there are other things still in the platform for which stack frames are significant. These are referred to as “caller sensitive” methods. An example is Class.forName(). This looks up the given name in the classloader of the class that contains the calling code. If the stack frames were shifted around by TCO, this might cause Class.forName() to use the wrong classloader.

No doubt there are ways to overcome this — the JVM does inlining after all — but there’s work to be done and problems to be solved.


Is there? As you say, there's already inlining, and I don't see how tco presents a harder case for that.


There are similarities in the problems, but there are also fundamental differences. With inlining, the JVM can always decide to deoptimize and back out the inlining without affecting the correctness of the result. But it can't do that with tail calls without exposting the program to a risk of StackOverflowError.

We've been using TCO here ("tail call optimization") but I recall Guy Steele advocating for calling this feature TCE ("elimination") because programs can rely on TCE for correctness.


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

Search: