Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Helix Editor plans to use Steel as a plugin language [0].

[0]: https://github.com/helix-editor/helix/pull/8675



emacsers looking deep from afar


I would use Helix in the terminal if it supported Emacs keybindings tbh but I don't want to relearn another set of keybindings. Still I'd be interested in what it becomes.


The keybindings is one of the major selling points of helix. It uses kakoune style object-verb action (like vim visual mode) by default with multiple selections. If you're comfortable with emacs bindings then you're better off with a lightweight emacs alternative.


As an evil user, this is potentially huge to me. Emacs happens to have the best balance of easy to setup configuration (relatively), powerful package ecosystem, and proper hackability of all editors I've found. It's not very fast though and has some conventions that feel archaic.


I'm also an Emacs evil user (neovim too). I think the kakoune editing model has the potential to surpass even the vim/evil model. Its default object-verb order makes it easy to preview and change selection before proceeding with the action. That's not possible with vim's normal mode. Vim does have the visual model. But then kakoune model also uses multiple cursors, making it more powerful. I really wanted to try kakoune model in Emacs. But the package needs a bit more updates.

Another issue I have with evil is that it changes a lot of Emacs' default bindings, making it hard to do certain tasks. Some operations simply don't work at all. The kakoune package doesn't do this - at least not in insert mode.

> It's not very fast though and has some conventions that feel archaic

Sadly, multithreading is an afterthought for Emacs. There is just too much legacy stuff to make it easy. The language design is also from another era. The default dynamic binding feels very alien when almost every language everyone knows is lexical binding by default. On the other hand, scheme feel very modern due to very careful language design. But the effort to switch Emacs to scheme didn't find much steam.

There is one aspect where none of the new editors (hx with scheme and nvim with lua)can match Emacs. Emacs is entirely written in elisp with the C parts acting merely as libraries. The extension language for other editors is just an addition to their core editing code.


> Sadly, multithreading is an afterthought for Emacs

It is, but it's usable. I'm actually amazed that, even after three major versions, the built-in threading is not used by the community.

Yes, the threads currently are not usable for number crunching in the background. And yes, there are bugs, and trying to do many things from the background thread doesn't work, sometimes in unexpected ways. You can still block the main thread from the background thread since some things block the event loop, no matter where they were started.

But, the threads do give you independent control flows. Whatever you cannot do in the background, you can offload to the main thread with a timer and a queue of lambdas.

The built-in threads are very, very bare-bones - it's around 15 functions, for threads, mutexes, and condition variables. They are very limited by their "mostly cooperative" nature. However, with a bit of sugar, they are usable for at least one thing: async processes and network communication.

In a background thread, you can "block" to wait for a child process to do something. It's natural and requires no macrology (async.el...). The same is true for network communication. You can block and wait for a response while the rest of Emacs does whatever. With just two functions, you can write code without blocking as if you used `call-process`. Sequential actions - call this, wait for it to finish, call that, wait for it to finish, etc. - can now be coded in a sequential way, without having to worry about callbacks, sentinels, and a poor-man FSM implementation that invariably appears in Elisp that doesn't use threads.

The threads built into Emacs, currently, are closer to green threads or coroutines, functionally, than to OS-level threads. But that's still a huge help in a bunch of important and pervasive scenarios. It's really strange that nobody seems to realize this.

With threads (as they are), the Continuation Passing Style compiler macro (in generator.el), and dynamic modules (for actual parallelism where needed) Emacs now has everything it needs to make it non-blocking by default. Of course, that would entail rewriting everything on top of these abstractions, so it's unrealistic - but for new code and packages? I think we're just one package (along the lines of dash, s, etc.) away from convenient concurrency and parallelism in Emacs. The problem, of course, is that someone needs to design and code that package...


"Yes, the threads currently are not usable for number crunching in the background. And yes, there are bugs, and trying to do many things from the background thread doesn't work, sometimes in unexpected ways. You can still block the main thread from the background thread since some things block the event loop, no matter where they were started."

Many dynamic languages have bodged on threading over the past decade. (I don't think dynamic languages are intrinsically unthreadable or anything, but their interpreters were pretty deeply based on not having threads.) What that has shown is that 90%-effective threading is useless, and 99%-effective threading is superficially appealing but always, always blows up at any sort of scale.

You really need threads that don't come with all those caveats.

I expect it will get there, but another thing we've learned from previous efforts is that telling the community it's ready before it's ready causes "$LANGUAGE threading" searches to be filled with posts telling people how bad it is, even years and years after it has actually been fixed. It's probably a blessing in disguise it's not something the community is pervasively trying to use.


Well, threading - shared-state parallelism, more precisely - is hard to do well, and retrofitting it into a program that wasn't designed with that kind of threading in mind is even more challenging. I don't think any popular languages solved this, save for Java. Especially in the recent versions, with the new virtual threads - as much as I dislike Java, I have to say they did an excellent job on this. Other languages and platforms (that I know of; what's the .NET story here?) are all shitshows to varying degrees, trying to catch up and failing over and over again.

I think the only sensible way to offer parallelism in Emacs is to exclude the "shared-state" part, the way Racket (places) and OCaml do it. I think Python also tries to do it with subinterpreters? Let another instance of the interpreter run in the same process and communicate via message passing. That's probably still a huge undertaking, but at least it seems more viable than going through the whole codebase and adding locks everywhere...

Still, the "threads" in Emacs, as incomplete and half-baked as they are, can be useful. And if nobody uses them, there's no incentive for the developers to improve them. So I think we need at least some early adopters if we want the threading support in Emacs to get better.


On concurrency, Java is the last to the party. Many languages had solved it in various ways before:

- async/await camp with C# and F#, Swift, Rust, Python, TS/JS (yes, I know, event loop)

- Erlang/Elixir and BEAM family

- virtual threads and/or coroutines - Go, Java, Kotlin

Generally, I don't feel like in 2023 concurrency and parallelism are problematic areas anymore aside from existing aged stacks. From a perspective of mainly .NET ecosystem resident, it has been a shitshow outside of it for a long time indeed with many architectural choices throughout the industry paying for the Java sins (e.g. Kafka) and imposing limitations that seemed nonsensical and embarrassing even 7 years ago.


We're talking about parallelism here, not concurrency. async/await solve concurrency, not parallelism (on their own). Kotlin coroutines solve parallelism only because they piggyback on Java threads. I'm not sure about Go, but it's probably M:N concurrency (so with parallelism) like what you get on the BEAM. Then again, on the BEAM you don't get to "share" anything (other than binaries, IIRC).

I'd say concurrency is largely a solved problem, yes; limited parallelism (e.g., with message passing) also mostly works. We don't need to worry about the "C10K problem" anymore. But shared (mutable) state parallelism is, I think, still far from solved - if it can ever be "solved", which is a pretty big assumption :)


> It is, but it's usable. I'm actually amazed that, even after three major versions, the built-in threading is not used by the community.

I'd say inertia. Emacs is large enough for people not to be aware of "simple" things, so the new threading primitives.. might take a few more years.


Considering that I only learned about `read-symbol-shorthands` last week by accident (the feature landed in 28, I think), that does rings true... :)


I feel like the scheme transition could go better if it had been proposed today. The dev community is much larger and there seems to be more activity around this stuff today.

Case in point, the recent async updates, native compilation and more. They often leave something to be desired but are nevertheless huge upgrades.


Fair enough


Out of curiosity, if you’re familiar with Emacs, what’s your motivation for wanting to use Helix? Built-in LSP support?


I use it despite a very complete emaxs setup because:

- curiosity

- seeing if I'm missing anything

- haven't had time to get tree sitter stuff configured, and wanted to see how good it is

- see if I want to move back to meow from vanilla (tons of previous vim, evil experience)


- Curiosity

- Speed, even though emacs with the emacsclient is pretty much instant

- Font support, for some reason the terminal with Emacs sometimes doesn't render as well as it should

- Some keybindings issues in the terminal like Ctrl-Backspace not working properly.

- See if there are good workflows/defaults I could use in Emacs


That's ridiculous!


I saw your comment on the thread.

A. Most WebAssembly implementations are bigger than Helix itself.

B. WebAssembly is immature.

C. Lisp is perfectly fine. Even if you don't like it, it's not the end of the world if you have to use it to configure Helix.


A. It's true that Wasmer is bigger, but even my phone has 16GB of RAM and 1TB of storage space.

B. There are probably more computers running WebAssembly loads today than Lisp ones.

C. Lisp is a mature language, almost too mature. I used it extensively in the '80s and it served me well then, but that was 40 years ago. At least have the decency to use a more modern language like Lua, which is what Neovim uses.


Lua is very weak for "programming in the large". It's just a little bit better than early JavaScript. Scripts are OK, but anything that requires more code with more structure requires incredible amounts of perseverance and discipline from all contributors. You can do fairly large programs in Lua as a small team of highly skilled hackers, but the barrier to entry will be much higher than if you did it in a language that offers ready-made abstractions.

I use AwesomeWM, which is basically Emacs of Window Managers, with Lua instead of Elisp. The code is very well written and documented, yet getting into it is much more complicated than if it was written in Python - even if that Python was written poorly.


A. Sure, but it isn't sufficiently beneficial for the cost.

B. WebAssembly is immature for developing a plugin system because of the lack of a sufficient ABI: https://github.com/WebAssembly/component-model

C. There aren't any other languages that meet the criteria. Lua was a no-go from the start. The maintainers did not like the language, and it necessitated adding more C code to Helix which could complicate building even further. https://github.com/helix-editor/helix/discussions/3806#discu...


A Lisp, a weird dialect of Lisp, is not better than Lua. Why use Rune [0]?!

[0]: https://rune-rs.github.io/


Rune is not mature and is developed by a handful of people. Please check the thread I linked, literally everything you could say has already been addressed.

Saying "X is better than Y" or "Why not Z?" is not constructive at all.


It is constructive, though - Lisp used to be fun when parsing complex language grammar was not as trivial as it is today. I loved it, and I worked with muLisp for many years... in the '80s and '90s. For AI (or, rather, "expert systems"), it was without a match... prior to the '80s, though. There were Lisp machines even... in the past.


Why? The properties of lisp make it suitable for both a config file, and an extension language. It can be easily embedded into existing languages and is extremely flexible.


Turing-completeness in a config file is an anti-pattern. Write an external program to generate the config file instead.


Putting arbitrary bounds on how people use their computers is an anti-pattern.


Right, but his concern about security is not just to restrict how you use your computer, but to try to keep it safe in a manageable way.

However, the current state of guarantees around it is not at the point where Turing-completeness is the biggest issue. You can have a simple config, but the rest of the system is still unverifyiable and running unknown blobs.

I think the reasonable way out of it is through restricted capabilities. We won't get a fully verifyiable system we can inspect anytime soon. Probably not before the dark days of mandatory and somewhat provable ad impressions.


Why? Emacs has Emacs Lisp and Neovim has Lua, it's great to be able to configure, write plugins and helper functions, in the same language.


So, if I use an editor config off the Internet, I need to inspect it for malware, because it's code, not configuration? Yes, there are languages for configuration - Jsonnet, Starlark, Dhall, which are execution safe - unlike Lisp and Lua!


Do you inspect all the code you run on your computer? You probably got all of it off the internet, except for the firmware blobs you couldn't even inspect if you wanted to.

And hell, even an "execution safe" configuration can contain malware if there's a parser bug.

At some point you have to choose who to trust and not to trust to write code that runs on your system, and all you can really do is try to verify that they did in fact write it, and run untrusted code in isolation from sensitive data.


I install software from credible sources. I do reuse configs from dotfiles off the Internet - just like most people do.

There are way more configs out there than there are software projects.


You already face the same threat then. Many, if not most, nontrivial programs have at least one way to escalate to arbitrary code execution from config. For example sway has exec, basically any useful editor has "on save actions", etc. No need for a Turing complete language when you can just shell out.


Why would you use an entire config off the internet?


Whenever I update my spacemacs config+packages I'm kind of doing that, there's no way you can honestly convince yourself that you thoroughly reviewed everything, but I guess the same applies to when you update your boring text editor's binary and forget to opt-out to some new feature you may not want as your old config might not mean the same thing now.

I think the real problem is around being able to trust your entire system. It'd help much more to have a better capability system so my rouge text editor can't upload my photos or credit card info from my browser profile to the internet, but today things kind of work because of tons of well intended and behaved people collaborating.


FWIW, the plugin system is supposed to have sandboxing as well.


Scheme has sandboxing in the form of environments. You can evaluate[0] / load[1] untrusted code by applying an environment specifier[2] with all of the symbols you trust the code to use. For example, if you don't want the code to be able to use IO, simply don't add (scheme read) and (scheme write) to the environment that you eval / load the code with.

[0] https://index.scheme.org/filterset/r7rs_small/%28scheme%2520...

[1] https://index.scheme.org/filterset/r7rs_small/%28scheme%2520...

[2] https://index.scheme.org/filterset/r7rs_small/%28scheme%2520...


If you're target audience are not progmmers versed in your wacky config language, it's a tremendous foot gun. Also a huge security hole.


It's possible to write declarative configuration in scheme. You can see that in Guix. Eventually someone will write a macro to create something purely declarative, like use-package in emacs.

> it's a tremendous foot gun

I've never seen this footgun in action with elisp in Emacs, lua in neovim or vimscript in vim. Is this anything more than hypothetical?

> Also a huge security hole.

If you put an editor in a position where its Turing-complete configuration is a security hole, you'll be in a lot more trouble than you imagine. Editors by definition are meant to modify stuff in a filesystem. With those privileges, it wont matter what the config language is. The plugins, even in webassembly, will cause serious issues.


It's a text editor specifically made for writing code.


This is why I always only put finite memory in my computers.


But then how do you write custom key handlers?


?


Certainly less ridiculous than YAML pretending to be a declarative key-value file store


It's perfectly viable as a MVP. WebAssembly plugins for Helix had been proposed many times but without an actual implementation being worked on.




Consider applying for YC's Summer 2026 batch! Applications are open till May 4

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

Search: