News for hackers. Stuff that matters.

someone_19

Re:Java 27 Released on Thursday September 17, 2026 @04:23AM (#49737903)
> The whole point of abstraction is that the implementation details aren't known.

I disagree with that phrasing; it is better to say that abstractions allow a programmer to ignore unimportant details. For example, when developing two modules (possibly even by different teams), all they know about each other is a lean interface, without any implementation details. However, the compiler might know everything.

> So in C++ we always suffer from this problem called "zero overhead abstractions" or "abstraction costs"

This is another odd term. In Rust, the term used instead is "zero-cost abstractions," referring to cases where the compiler can generate instructions for higher-level code just as efficiently.

> So the main practical benefit of a JIT isn't that at all, but that it can do the "mother of all optimisations" - inlining - far more aggressively.

I’ll reiterate that I disagree with this: inlining is performed very efficiently during monomorphization. And monomorphization is used very frequently in Rust.

> After more than 25 years of working with C++

I don't have much experience with C++; I mostly use Rust. I can only assume that the C++ development experience is far worse than Rust - especially when trying to write software that is both reliable and fast. This may be particularly relevant to older C++.

So, a Dog, a Cat, and an Abstract Mammal walk into a bar...

https://godbolt.org/z/dEsW1sfM8

I didn't want to do this, but I went ahead and created a small example showing that monomorphization and inlining work remarkably well. (Obviously, this example does not address memory management)

Re:Java 27 Released on Wednesday September 16, 2026 @08:13AM (#49725728)
> Assembly (which is faster than anything in the same sense: for any program in any language, there exists and Assembly program that's at least as fast)

At least you aren't claiming that the JVM is ~1.5 faster than perfectly written assembly :)

I disagree with a lot of what you’re writing. However, we’ve reached the point where we need to run benchmarks and analyze the generated code (this is easy to do for compiled languages using https://godbolt.org/, but for the JVM, it can be a bit more complex, given the warm-up factor).

So, there is one fundamental point I started with:

> JIT is effective for languages where the source code lacks sufficient information (dynamic typing, where anything can be null)

And your answer is:

> A JIT can make such languages decently fast, but that's not how it's used in Java. In Java it is used for speculative optimisation, which allows far more aggressive optimisation than an AOT compiler can do.

Essentially, you are saying that the compiler can apply aggressive optimizations when it knows what is happening in the code.

But I say that JIT is needed so the compiler can figure out what is happening in the code and perform aggressive optimizations.

There are many things that can be inferred from the code without needing to execute it. The question is how difficult it is to make such an inference: in one scenario, the compiler might attempt to track whether specific data changes-and, if it can prove this, mark the data as immutable and apply certain optimizations-whereas in another, it might already possess the information that the data is immutable.

Moreover, information about immutability is useful not only to the compiler but also to the programmer. Just like information about types: it benefits both the compiler and the programmer. Imagine a fan of JS or Python joining our conversation and claiming that both Java and Rust are low-level languages because you have to specify types - something they view as complex and a hindrance to development speed.

The same applies to the GC: the compiler can perform more optimizations when it knows when memory needs to be cleared (move it to stack or even place the data on registers). The JVM attempts to do this (via escape analysis), but there are limitations; consequently, data ends up on the heap, and GC operations come at a cost (due to data movement).

Rust simply makes it easy to obtain far more information, enabling aggressive optimizations that are both immediate and guaranteed.

There remain a small number of cases, such as `switch` statements - where one branch executes 99% of the time, while the other 99 branches execute only 1% of the time. In such instances, the JIT could indeed perform further optimizations; however, I am not even sure if the overhead of monitoring wouldn't outweigh the benefits. And the question is when and how to perform PGO, or whether to perform it at all.

Re:Java 27 Released on Wednesday September 16, 2026 @06:14AM (#49724308)
> I have seen it mentioned everywhere, but is this actually true?

Yes, it just adding or subtraction int to stack pointer register. I’m not certain, but the only thing that might be faster is accessing data at a fixed address - that is, global variables.

Re:Java 27 Released on Tuesday September 15, 2026 @05:27PM (#49719121)
Thank you for the reply.

Do you mind a reasoned discussion?

> A JIT with speculative optimisation and a moving GC.

Idiomatic Rust, through its concepts of ownership and borrowing, encourages a pattern where you receive data as an argument or create it directly, perform operations on it, and then discard it via RAII. This bears some resemblance to functional programming. This approach does not apply to buffers of unknown size, which still require heap allocation; unfortunately, Rust lacks automatic buffer reuse. However, such optimization is theoretically possible. The stack is definitely faster than anything else.

> This means that you need to translate data storage or code storage to hardware addresses, and that interferes with both moving collection and with JIT compilation.

You don't need GC if you allocate data on stack. You also do not need to dereference the pointer.

> dynamic dispatch

You mentioned templates. In Rust, traits that are monomorphized - much like templates-are the standard approach; using vtables or `dyn trait` is a relatively rare use case. This stems from the fact that all code is known at compile time and there is no dynamic loading, allowing the compiler to eliminate polymorphism from the code entirely.

> and shared heap objects

This might be considered convenient, but in my view, it also leads to code that is harder to maintain when objects can be modified from multiple places. However, I think that is outside the scope of the current discussion.

> We get around it with selective use of templates in C++ (or comptime in Zig), but it has to be selective, and it's viral.

Yes, monomorphization is the default solution in Rust. It is not always viral either, because when using it, you often define specific types, and they do not spread beyond that scope.

I suppose you could say that the programming style I am talking about is complex, inconvenient, unmaintainable, and so on. What I mean is, assuming this programming style is sufficiently convenient—and perhaps even has its own advantages - then none of the optimizations you listed offer an edge, and the Rust code will definitely be faster.

Re:Java 27 Released on Tuesday September 15, 2026 @03:12PM (#49717373)
> because of essential constraints of low-level languages that prevent them from doing certain optimisations that matter mostly in large programs

Which specific optimizations are you referring to?

In my experience, this is largely a myth; compared to Rust, you actually get even faster code right away.

JIT is effective for languages where the source code lacks sufficient information (dynamic typing, where anything can be null).

Re:Go: Support for Generic Methods on Thursday May 28, 2026 @03:59AM (#48306012)
Ada is the lost knowledge of a past, more advanced civilization.
Re:Go: Support for Generic Methods on Thursday May 28, 2026 @03:20AM (#48305733)
> No, you’re clearly wrong; golang was always going to add support for generic functions.

Let's get this straight. I'll give you a long quote from Rob Pike's article where he describes the history of the go language:

""" One thing that is conspicuously absent is of course a type hierarchy. Allow me to be rude about that for a minute.

Early in the rollout of Go I was told by someone that he could not imagine working in a language without generic types. As I have reported elsewhere, I found that an odd remark.

To be fair he was probably saying in his own way that he really liked what the STL does for him in C++. For the purpose of argument, though, let's take his claim at face value.

What it says is that he finds writing containers like lists of ints and maps of strings an unbearable burden. I find that an odd claim. I spend very little of my programming time struggling with those issues, even in languages without generic types.

But more important, what it says is that types are the way to lift that burden. Types. Not polymorphic functions or language primitives or helpers of other kinds, but types.

That's the detail that sticks with me.

Programmers who come to Go from C++ and Java miss the idea of programming with types, particularly inheritance and subclassing and all that. Perhaps I'm a philistine about types but I've never found that model particularly expressive.

My late friend Alain Fournier once told me that he considered the lowest form of academic work to be taxonomy. And you know what? Type hierarchies are just taxonomy. You need to decide what piece goes in what box, every type's parent, whether A inherits from B or B from A. Is a sortable array an array that sorts or a sorter represented by an array? If you believe that types address all design issues you must make that decision.

I believe that's a preposterous way to think about programming. What matters isn't the ancestor relations between things but what they can do for you.

That, of course, is where interfaces come into Go. But they're part of a bigger picture, the true Go philosophy. """

Rob Pike, 2012

I can draw a few conclusions from this: firstly, he didn't want to add generics at all because he didn't think they were useful, and secondly, he doesn't understand programming very well and doesn't know what generics are and confuses them with inheritance.

Re:Go: Support for Generic Methods on Thursday May 28, 2026 @03:02AM (#48305597)
You already have iota. Type safety is not needed by design:

> Go intentionally has a weak type system... Go in general encourages programming by writing code rather than programming by writing types...

https://github.com/golang/go/issues/29649#issuecomment-45482...

Re:Go: Support for Generic Methods on Wednesday May 27, 2026 @12:38PM (#48296783)
...and Java didn't even have basic enums or sum types from the beginning. But it had null.

They added enums, they added sealed classes. They're trying to get rid of null (apparently it's really hard). The problem is that in 2012, when go 1.0 was released, this should have been obvious to everyone.

Here's a famous discussion from 2009, three years before the 1.0 release (tldr: facepalm)

https://groups.google.com/g/golang-nuts/c/rvGTZSFU8sY

Re:Go: Support for Generic Methods on Wednesday May 27, 2026 @12:32PM (#48296705)
So you mean to say that PHP5 and Js from 2007 had a well-founded design?
Re:Go: Support for Generic Methods on Wednesday May 27, 2026 @12:30PM (#48296676)
I agree that they were clearly not in a hurry. I disagree that they are doing everything right. I am interested to see how they will fix the 'million dollars mistake'.
Re:Go: Support for Generic Methods on Wednesday May 27, 2026 @12:27PM (#48296640)
Indeed, in 2012, it was not clear to anyone that generics were needed /s
> but the good parts of JS are better than anything else in existence

What you talking about?! I can't think of a single thing in Js that I could say is good.

Okay, two big corporations have invested a lot of money and effort into making V8 and TypeScript, and now it's useful. But I don't consider it exactly part of Js.

Re:Use Your Type System on Friday July 25, 2025 @01:34AM (#44679887)
Here is example of compile time error with wrong newtype argument:

https://play.rust-lang.org/?version=stable&mode=debug&editio...

rust-analyzer gives an error directly in IDE.

Re:Use Your Type System on Thursday July 24, 2025 @04:54PM (#44675981)
Unhappy way is a part of contract. So yes, that is what I want. If a function couldn't fail before but can after the update - I want to know about it.

In fact, at each layer, if you want to propagate an error, you have to convert it to one specific to that layer.

Re:Use Your Type System on Thursday July 24, 2025 @04:42PM (#44675844)
You can do this quite easily in Rust. But you have to overload operators to make your type make sense. That's also possible, you just need to define what type you get after dividing your type by a regular number and vice versa a regular number by your type. Or what should happen if when adding two of your types the sum is higher than the maximum value. This is quite verbose. Which can be done with generics or macros.
Maybe once or twice a month I'll get some language issue (like no negative trait bounds, restrictions on generic const, or something related to lifetime). I might spend an hour or more on each such case.
> It's essentially impossible to write a Rust program without relying on many of its escape hatches like RefCell and unsafe, that make the borrow checker go away.

This is very contrary to my experience. I don't use cloning or various primitives (except when I really need the shared state).

I can assume that you have this opinion because you see it as a low-level problem while its solution lies at the architectural level.

Use pure functions, group data in structures that own it, get all the necessary data in the controller, process it, and return or store it.

...and using it in function calls would also be inconvenient.

Now I have a clear understanding of what is happening and how.

Nevertheless, using something like this for educational purposes maybe could help. Author of the article In the example with Id literally complains that moving makes moving.

My first thought when I was learning Rust was "Why don't they use a different operator for move?", something like:

let a <= String::from("Hi");

let b <= a;

let j = 0; // Copy

let k = j;

This may not be convenient, but it could be useful for educational purposes.

> Second is an absolutely strict programming language, that incorporates not only memory membership Rust style, but every single object must have a well defined type that determines not only the set of values that the object can have, but the operations on that object, which produce other well defined types. Basically, the idea is that if your program compiles, its by definition correct.

That is Rust? This is how his system of types and traits works.

Look at vectors or hash maps. This is a perfect example of the Rust philosophy. Namely, writing complex low-level abstractions that offer a convenient and reliable high-level API.

It uses unsafe, but few people understand what it really is. Rust has clear invariants (for example, that a pointer to T always points to an existing and valid T) that are enforced by the compiler. Using unsafe code is shifting the enforcement of these invariants from the compiler to the programmer. The advantage compared to C is that unsafe code is limited to undefined blocks that are easy to test.

The data structure you describe should be done exactly this way. And it is not a task for the average programmer.

> Golang is basically just more concise Java

But golang/newsqueak was developed much earlier, in the early 80s by Rob Pike.

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

```

type point: struct of{ x, y: int; }

a:=mk(array[10] of int) // mk renamed to make

select{ case i = <-c1: a = 1; case c2<- = i: a = 2; }

```

In the late 2000s, this language updated somewhat (for example, the mk function was renamed to make).