22 comments

[ 2.6 ms ] story [ 49.9 ms ] thread
> Linux is not yet tested, Most development is done on MacOS or Windows, if any issues arise with building on Linux, please open an issue.

That is good to know.

Best of both worlds of Rust and Zig? C/C++ ABI compatibility? No runtime? Borrow checking with less strictness? Seems too good to be true.
I know that there's no such thing as a unique name, but the fact that https://helix-editor.com/ is a living project, first released in May 2021, might mean that using "Helix" for this project, first released in November 2024, isn't the best choice of name. Or at least it might be worth a disclaimer in the readme!
It's the shopping list of dreams. As fast as it gets. Borrow checker, but friendlier than "other languages' borrow checkers" (I wonder which other ones they mean). Readable. What's not to like. I look forward to v0.1

Is anything known about the key developers? I would imagine such a project needs firepower. Rust had Mozilla's heft from the get-go. Most successful languages have another big sponsor.

This is cool, I think the most novel/unique aspect is the advanced memory tracking: https://github.com/helixlang/helix-lang?tab=readme-ov-file#a...

Still, I don't really see this going anywhere. There are already so many "slightly better C++" languages out there, e.g. D, cppfront/cpp2, Carbon, Zig and they pretty much all don't see wider adoption for the same reason. No matter how simple or ergonomic the interop with C++ is, the switching cost is still high and the benefit tends to be marginal. Almost all of them either include garbage collection or don't fully guarantee memory safety. Choosing a restricted subset of C++ and an opinionated, enforced linter & static analyzer goes a long way and gets you most of the benefits of these new languages, so organizations tend to just do that.

The exception is Rust, because in spite of all its downsides it has the killer feature of guaranteed memory safety without garbage collection, so that's the one seeing constantly increasing institutional support.

Bold to announce without finishing the documentation.
Sorry for the petty comment, but if you design your language syntax with `x if cond else y` with the condition in the middle like in the Python if-expression syntax, I cannot trust your judgment and we cannot be friends. (from one of the images of code)

I will take an actual look into it later though, seems interesting.

I also found the conditional syntax (with the condition in the middle) a bit awkward compared to Rust's or even C's. But there is certainly a common operation. And this is the syntax chosen by the Python language designers.

I'm curious. Why do you think it is bad? What are the possible issues that this style can cause?

Hey, I'm one of the dev's; We thought about this ALOT like 4 hour long arguments, we asked a lot of our friends and its was really mixed, some people hated the python style ternary, others loved it, we weren't sure what to choose but the numbers were 38.5% for `<expr> if <cond> else <expr>, 27.25% for `<cond> ? <expr> : <expr>`, and 34.25% for `if <cond> { <expr> } else { <expr }` we did also look at a few other styles like: `if <cond>: <expr> else: <expr>`, but we decided that the python style is the best for a simple reason, its easier for new dev's who want to come into systems programing or writing low level code. If a lot of people do really hate it, we can swap it out before an alpha it would be a really simple AST node parser change...
I don't know if your language is expression based like Rust, but in Rust the nice thing is that it's just the regular if syntax being used as an expression and not some additional special case syntax, which is elegant.

I actually like the Haskell syntax more because it's looks nicer when formatted in multiple lines:

  -- Haskell:
  -- single line
  if ... then ... else ...

  -- multiline
  if ...
      then ...
      else ...

Where in Rust with normal formatting it because 5 whole lines:

  // Rust:
  let result = if ... {
      ...
  } else {
      ...
  };

  // or, 4 lines but less common:
  let result = match ... {
      true => ...,
      false => ...,
  };
I think the strongest argument for condition-first that's unrelated to "taste" is the fact it is consistent with the order of execution:

  # Python:
  # you have to read "from the middle out" to read with the order of execution
  x = a() if b() else c()

I also strongly suggest you look up why Python ended up with the syntax it did. The reasons were not really good and not really applicable to a new language. Maybe my memory is bad because I can't find the source, but I remember one of the justifications was simply that it turned out faster in cpython (the python interpreter) in the test implementation they did.

I might also consider asking Chris Lattner (Mojo language). They did use that syntax in Mojo, because it is made to be a superset of Python. But I remember hearing him say he really disliked it.

I don't understand how the docs say:

"Non-Nullable Types by Default: In Helix, all types are non-nullable by default, significantly reducing the risk of null-pointer errors. Helix uses a novel approach to nullable types - Questionable Types, which allows for more explicit handling of null or panicking values."

But then when you look at questionable types, it says:

"You can use a questionable type like a regular variable. If you use it in a context where a non-questionable type is required, Helix implicitly checks its validity: If the value is valid, it proceeds. If the value is null or an error, the program stops with a crash."

Is that not exactly the same behavior that Java has?

> Non-Nullable Types by Default

So glad to hear this. I now consider this the single most important requirement when am evaluating a new programming language.

Error handling looks well thought out as well.

Very interested in how the borrow checker logic would shape up.

If this delivers on the promises made, it would pretty much be my dream programming language.

I'd love to learn more about how AMT works. How would a doubly linked list work in this language for example?

Does the conversion happen during run-time? Isn't that going to be super expensive?

It depends on the context of the code, its after the optimization pass but before emission of LLVM IR, But in most cases it shouldn't be super expensive, the conversion itself happens at compile time, but the usage is at run-time, it can be turned off entirely, but we need testing to see the actual performance implications. Right now the AMT is written down in an internal MD document, we've figured out the whole theory bit, the implantation comes after we written the error-handler (for compiler side error messages), lexer, pre-processor, parser, symbol table, Optimizer passes, Borrow Checking IR (haven't decided a proper name on it yet for now its just BCIR), and then the AMT, followed by the Emitters. Then the rest of the features follow suite. but we will test each stage individually once their complete and have a proper dataset of numbers to see performance memory usage and other metrics, with proper real world cases (it very well may be slower or not, we don't know yet in theory, there's no immense performance implications at runtime other then a a couple ...ns extra).
(comment deleted)
> Lack of OOP Support: Both Rust and Zig lack comprehensive OOP support

To some of us that is a major feature of Rust and Zig, but good luck.

> Both Rust and Zig lack comprehensive OOP support, which is essential for certain domains like AI or game development.

Just curious, what benefit OOP offers over other paradigms in AI dev?

I always say this, but my opinion when it comes to bootstrapping a compiler is that it's generally a waste of time unless your language is meant for writing compilers.

While dogfooding your language is a great to stress test it, you want to make sure you're testing the right stresses -- the ones your target users will encounter. A compiler may be that thing, but chances are it is not; most people do not write nor do they aspire to write compilers. So showing off that you've written a compiler in your language doesn't attract users, because they're still left with the question "Okay, well what does it do for me?"

Another reason is that there is an infinite amount of work that goes into writing a compiler. It's really endless, especially for a small team or solo dev. If you try bootstrapping, it means essentially maintaining two compilers until you can fully bootstrap, which may never come. One compiler is really enough work.

The final reason I will say is it takes forever, and puts a total brake on new features, which can really kills project momentum. Writing the first bits of a language can really fly. But when you sit down to bootstrap, it means you're not releasing new features, but redoing everything you've already done. This leads to unfortunate messages like "We’ve now started work on the self-hosted compiler, using the current rudimentary C++-based implementation as a bootstrap.", which followers take to mean "the project is currently in hiatus for the foreseeable future until this background work on the bootstrap compiler is done". This is a critical point in language projects where the bootstrap might never be completed.

When I look at a new language project I always check out the "contributors" section. Whenever I see an initial flurry of activity from one or a few devs, followed by a taper off into nothing, I know the project is in trouble. It seems like maybe this author has hit a wall, and I would be highly suspicious of the bootstrap endeavor causing it.

Hey, I'm one of the dev's; I do definitely agree with you, while bootstrapping isn't always the best idea and it does take forever, we are doing it for a really simple reason, the project was initially a passion project by me, and then A few friends wanted to jump-in as well and make it a fully fledged thing, so we thought it would be a good idea for me to get the bootstrap compiler completed before moving on to the main compiler, So my bootstrap compiler right now works, its not the best tho riddled with bugs and problems; but its enough to write a compiler with. As to the taper off, We all are working summer internships right now, so the development is definitely slowed down a little, but, we are working on it, albeit slowly for the time-being, but the bootstrap endeavor, isn't causing a stop but rather a slow down also paired with the summer internship, it will pick up back to speed once college starts in Fall...

Our timeline for a early working alpha with the bootstrap is Q1 2026 (or Q2) but no later.

P.S A reason why only I mainly wrote the initial compiler, and also we decided to make a new compiler is since this was a passion project, the code wasn't maintainable AT ALL, I didn't plan on anyone else working on it, the code is really bug prone C++, a lot of lines of code that make 0 sense, and no structure in the codebase. The new compiler however, we are properly structuring it, documenting it, following code guidelines, and working on it as a team as opposed to 1 man writing all the code and the rest of the team only stating ideas.