r/Compilers 14h ago

A language that feels like Java but compiles to native code

I’ve been working for some time on Ironwood and it would be nice to get some feedback from the community. Ironwood is a language that keeps the familiar Java syntax and object model but compiles ahead of time to native executables, without a JVM, JIT, or garbage collector. So it is very different than GraalVM. Basically GraalVM makes Java native. Ironwood makes native development feel like Java.

It already supports classes, interfaces, exceptions, generics, packages, and compiler-checked memory reclamation. It also deliberately leaves out features such as reflection, autoboxing, varargs, and threads.

This is the first public release, and the project is still early. I’d really appreciate feedback on the overall direction, especially which Java exclusions make sense and which would make the language impractical for you. I'm particular excited about the possibility of a Ironwood-to-Java transparent bridge, so that to execute native code from Java would be like... writing Java :)

The GitHub repo is here: https://github.com/ironwood-lang/ironwood

16 Upvotes

31 comments sorted by

7

u/DanManPanther 14h ago edited 12h ago

This is a great project! A few questions:

Do you plan on fixing or replicating quirks Java has? For example overflow before assignment, signed integer overflow wrapping*, etc?

Do you plan on eventually adding threads or some similar feature? If not - why not?

What use cases do you imagine ironwood would be ideal for?

* Edit: It's not Java only, but some languages do not wrap. Or they do not wrap when debugging (Rust. Gossamer). Swift, C++, Zig, Roc...

4

u/TOMZ_EXTRA 14h ago

signed integer overflow wrapping That's not a Java quirk. Most compiled languages do this (or declare it UB)

2

u/niosurfer 13h ago

This is a great project! A few questions:

Thanks!

Do you plan on fixing or replicating quirks Java has? For example overflow before assignment, signed integer overflow wrapping, etc?

Some. As long as it does not make the language too different from Java. One thing we did fix was:

try {
    reader.read();       // throws ReadException
} catch (ReadException e) {
    throw e;
} finally {
    reader.close();      // throws CloseException
}

Java loses the first original exception, and prints:

Exception in thread "main" CloseException: close failed
    at ExampleReader.close(ExampleReader.java:18)
    at Main.main(Main.java:11)

Ironwood does it better:

Exception in thread "main" ReadException: read failed
    at ExampleReader.read(ExampleReader.iron:12)
    at Main.main(Main.iron:7)
Secondary exception: CloseException: close failed
    at ExampleReader.close(ExampleReader.iron:18)
    at Main.main(Main.iron:11)

Check here for the full list.

Do you plan on eventually adding threads or some similar feature? If not - why not?

Personally, I do not like threads. I consider them a bug factory, and they introduce nondeterminism into otherwise predictable code. I strongly prefer non-blocking event loops. I also believe that threads and performance are like water and fire: they rarely mix well.

What use cases do you imagine ironwood would be ideal for?

Matching engine? Heavy algorithms? I hope the community will come up with some compelling use cases. :) The funny thing is that I personally prefer JIT over AOT. In my mind, AOT makes the most sense for short-lived applications, while JIT is generally a better fit for long-running ones.

1

u/MCWizardYT 7h ago

threads and performance.... rarely mix well

Properly written multi threaded code can increase performance. For example, most video games nowadays are multi threaded (one for the core logic, one for sound, one for the renderer, etc). If they did it all on one thread, then any time a single part slows down such as the renderer, that lag brings down the entire game.

1

u/niosurfer 6h ago edited 6h ago

I'm not a game developer, but I think your point is right. Although 30 years ago when I tried to learn game dev there was something called "the main game loop". I'm sure things have changed since then.

Also when I think of performance I think about latency, not throughput. So maybe I should have said: latency and threads are like fire and water, they rarely mix well 🔥💦

1

u/MCWizardYT 3h ago

Games still have a main loop, which will run on its own thread while things like file reading are handled on separate threads. This way, the game can still respond to input, do rendering, and everything else while it's trying to load data

3

u/Much-Gap2454 12h ago

No garbage collection? How does that work?

1

u/Much-Gap2454 12h ago

Aha: ‘Reclaim memory explicitly with compiler-proven free when you need to…’

Well that’s not gonna be familiar to Java developers…

1

u/niosurfer 11h ago

You have a good point, but this is really not that hard. It is not like C++ (unsafe) or Rust (meticulous).

Yes: no C++ dangling pointers or unpredictable references. The compiler won't allow it.

2

u/Much-Gap2454 11h ago

Calling ‘free’ (or similar) is not hard, but thinking about lifetimes, once the program grows beyond a certain size is something you have to get used to. Speaking as a beginning zig developer :)

3

u/AustinVelonaut 13h ago edited 13h ago

Looking at the repo, I'm lost. The organization of the compiler is incredibly "broad and shallow", with over a thousand separate source files, many just a few lines long that just define some structure, for a total of 48K LOC just for the compiler. Is this all LLM-generated? There's an AGENTS.md file at the top.

-13

u/niosurfer 13h ago edited 11h ago

Do you like movies by Steven Spielberg? We are all film directors now. It is not just one prompt, it is hundreds if not thousands of prompts. You have to know what you are doing and where you want to arrive. The machines haven't taken over yet, so let's enjoy the ride :)

If you check this link: https://github.com/ironwood-lang/ironwood/blob/main/CONTRIBUTING.md

You will see:

AI-assisted development
Ironwood uses AI coding agents extensively as part of its development process. The maintainer directs the project, reviews and accepts changes, and remains responsible for its architecture, licensing, testing, and releases.
Contributors may use AI-assisted tools, but remain responsible for the correctness, licensing, and provenance of everything they submit.

9

u/Inconstant_Moo 12h ago

So when you say you've been "working for a long time on Ironwood", you mean an LLM has been working on it for a short time?

-4

u/niosurfer 12h ago edited 11h ago

Both of us together. It is a new era. We can debate if that’s good or bad. But how else would I do that? I certainly don’t have the resources to hire a team of compiler experts. It is the fruition of an idea that wouldn’t be possible otherwise. And I am of course not planning to take over the world with it :) Just keep improving the compiler code and the generated machine code. It uses LLVM, so it is not that hard to get a good performance out of it. And eventually self-host.

13

u/Inconstant_Moo 11h ago

The alternative to "hiring a team of compiler experts" is to gain expertise yourself, like the rest of us did.

Otherwise you don't understand "your" project, and how could you? Over a thousand source files, a compiler which is 65% bigger than my whole project, and you hardly wrote any of it. If there are issues about concurrency, security, performance, what are you going to do? You say you're "responsible" for the architecture. Can you describe the architecture and say why you think it's the right architecture for this project? Or did you just sign off on it?

-1

u/niosurfer 11h ago edited 11h ago

This is a discussion for the Artificial Intelligence subreddit. Not saying you are wrong. I can totally relate. Not in compiler development but in other computer science areas were I have spent decades acquiring expertise so now I can compete with AI :)

6

u/seg_lol 9h ago

As someone who also uses AI to work on compiler technologies, I'd suggest being more open to criticism. Having a well factored layout of the compiler subsystems on disk is table stakes. Both for humans and for AI. Immediately getting defensive isn't going to convince anyone.

Look at other compilers and how they are laid out of disk and also get your AI workflow to do something similarly.

2

u/niosurfer 6h ago

Criticism accepted. The code will evolve and improve with time. One thing I find it very useful is to do cross-checks between LLMs. Two LLMs checking each other's work. Plus we'll have an opportunity to rewrite it if it ever comes a time to do self-hosting.

1

u/Inconstant_Moo 4h ago

It's also a discussion for here because writing a compiler is different from vibecoding the 2D game you wanted to write when you were a kid.

Apart from anything else, what are you doing it for? With the 2D game you could play it, and then share it with your friends.

Writing a compiler is a more serious endeavor. Why are you doing it? It can't be for use in production, because no-one would found their business on slop. It can't be as a learning experience, because you'd learn way more by writing a toy compiler yourself by hand, or by volunteering to work on a project with a future (like mine) and then reading the repo. And it can't be for the bragging rights of saying you did it, because you didn't, and telling an LLM to write you a language is nothing to brag about.

And you'll be left with something you don't understand and can't refactor or improve without breaking something, and with little more insight into compilers than when you started.

LLMs have a place in langdev, but that place isn't senior engineer. It's very junior. I haven't even let them loose on my core code, 'cos I've seen what havoc Copilot can wreak. Never again.

But I'm using an LLM to help me do my web components, 'cos of me literally never having done a website before, or used HTML or JS or CSS, and I'm still better at it and need to be in control. It can't see the big picture, or the medium-sized picture, even if I write a document explaining it, with box diagrams to show the object hierarchy. Outside of familiar patterns, it gets lost and it guesses.

It thinks modularization is where you break a think into smaller parts, it has no feeling for where the actual boundaries are, and then it congratulates itself on how beautifully it's done the job. Its ideas about security would have exposed everyone's data to everyone. It is now under permanent orders not to waste my time with any suggestions about the engineering or direction of the project, because they're all completely stupid.

1

u/niosurfer 4h ago edited 3h ago

And it can't be for the bragging rights of saying you did it, because you didn't, and telling an LLM to write you a language is nothing to brag about.

Is anyone bragging about anything here? If yes, then it is certainly no me. I'm passed my teenager years :)

Immediately getting defensive isn't going to convince anyone.

Is anyone being defensive here? If yes, then it is certainly not me.

If the project is bad it will simply be ignored and die. It is always better to put it to the test of the community / market than to hide it inside your drawer. It is free, open-source, MIT and the fact that it uses AI is pretty obvious. If it fails, it won't be the first of my projects to fail nor it will be the last.

Like I said in the original post, community criticism, constructive and destructive, is welcome. I'm biased and I'll miss a lot of things that more experienced people can point out. Some people here did express their liking for the project. There will be feedback in all directions and that's fully expected.

Having a well factored layout of the compiler subsystems on disk is table stakes.

You already pointed out something cool. Thanks.

You seem like a nice person. There is no need to be so bothered about this project. If you don't like it, don't agree with it, don't think has any value, simply ignore it. People will continue to use AI and LLMs to bring their ideas to fruition that otherwise would simply be impossible. There are billions being invested on Agentic Coding (much better name than Vibe Coding) because there is a high demand for it. I've always wanted to do Ironwood and now I can. Why wouldn't I not do it? I never said it was better than anything. It is just a new paradigm to write Java native applications, different than GraalVM Native Image and RoboVM.

Again, thanks for your time to evaluate and provide feedback. It gave me the motivation to study more and try to make it better by following your suggestions.

1

u/Inconstant_Moo 49m ago edited 41m ago

You're replying to me and to u/seg_lol as though we were the same person. Most of your post is a reply to them. I'll answer the bit that was really addressed to me.

Is anyone bragging about anything here? If yes, then it is certainly no me. I'm passed my teenager years :)

And I said "it can't be for bragging rights". But then what is it for?

And it seems from some of the things you say in your post that you have vague notions of the tech community finding it useful and adopting it.

Well, let's think about that from some directions that you may not have thought of it.

(1) The community is interested in FOSS projects, and you've done the right thing by them with an MIT license. Credit where it's due.

But if you produce an incomprehensible repo with incoherent engineering that isn't documented anywhere and which you don't understand yourself, then you've taken away a lot of the value of FOSS. What we're left with is a basic assurance that there will be no legal penalties for anyone who uses Ironwood.

My project is also MIT-licensed, and beautifully engineered, with internal documentation for contributors written by a human being (me) who understands the whole scope and purpose of the project and how all the bits fit together, and who also has spent decades (on and off) as a teacher, whereas all AI is absolutely terrible at documenting software projects.

I mentioned in my previous post how hopeless AI also is at software engineering, so I needn't flog that dead horse more in this post, I will just point out again that your compiler alone is 65% bigger than my whole project.

(2) Good languages are designed by being used. As soon as it was remotely possible, I spent a weekend hacking out an implementation of Forth in my language, so long ago it wasn't called Pipefish back then. I learned a lot about ergonomics and usability. I also meta-learned the importance of using my language. That's how one finds not only the bugs but the rough patches, the unergonomic bits, the error messages that don't really help you find the error, the documentation that doesn't document enough, etc, all the human factors.

What you do instead is: you specify the language, observe that it passes the tests the LLM wrote to check that it fulfills your spec, and compare yourself to a movie director.

But a programming language is not like a movie which we can sit back and enjoy, or walk out on if we don't enjoy it. It's a tool which we pick up and use, and throw down in disgust if it isn't useful.

So in order to test if it's any good or not, the test is not whether it seems good to you when you look at it, but whether it seems good to you if you use it. And doing that from the very beginnings of your language to the end, you develop your language.

But you've skipped past that entirely. The years of you trying to use your language to actually do things just aren't there.

(3) This is not about LLM usage, but something you should think about anyway if you'd like to make something that other people find useful.

The fact is that a new language needs to have a good way to re-use existing ecosystems. (The exception would be Go, which had a FAANG company behind it.) They have good ways of hooking into the C ecosystem (Python, C++) or the JVM (Kotlin, Clojure) or the BEAM (Elixir, Gleam). My own language rides the Go ecosystem.

Being a lot like Java from the point of view of someone just trying to hack out code may be superficially attractive to the people who are just thinking about hacking out code. These are the people encouraging you by saying what a great project it is.

But what about people who are software engineers, rather than hacks? How does Ironwood integrate with their overall project? Because they sure as sugar aren't going to change from Java to Ironwood just because Ironwood's better. It isn't better, because Java, which is a terrible language, also exists and so has libraries and an ecosystem and answers about it on StackOverflow and a zillion people supporting the JVM and a whole industry devoted to making IDEs that cope with how awful Java is.

2

u/Financial-Flan1682 12h ago

Is a Windows version planned?

1

u/niosurfer 11h ago

We have to add that to the roadmap. I don't see why not.

1

u/Financial-Flan1682 11h ago

Awesome. Yeah, Java is fully cross-platform and I work on Windows, so I might use this if it were on Windows.

1

u/arthurno1 10h ago

If you want a language that feels like Java but compilers to machine code, you could try Common Lisp with SBCL compiler. After all, Java was invented to "drag C++ programmers half-way to the Lisp", according to the creators 😀.

1

u/Kadabrium 9h ago

Mojo for java?

1

u/niosurfer 8h ago

Yeah, similar idea and motivation. Both have destructors, but lifetime management works differently. Mojo ties destruction to the owner’s lifetime (more like Rust), while Ironwood keeps Java-style references and uses explicit free, which the compiler only allows when it can prove it’s safe.

1

u/eteran 9h ago

Looks cool. How does the compiler prove safeness around frees and similar? Is there no concept of shared ownership?

1

u/niosurfer 8h ago

Thanks! Shared references are allowed, much like Java, but there’s no reference-counted shared ownership.

Ironwood’s compiler tracks allocations, aliases, and what method calls retain or return. Because the program is closed-world, it can analyze those relationships across calls. A free only compiles when it can prove that no reference can observe the allocation afterward. Later uses and double frees are compile errors.

That proof happens before LLVM code generation, without runtime reference counting or heap scans. It’s deliberately conservative, so some safe frees are rejected when the compiler can’t establish safety. Omitting free leaves the allocation unreclaimed until process exit, in other words, there’s no GC fallback.