But then it has other weird features too, like they seem to be really emphasising "friendliness" (great!) but then it has weird syntax like `\` for anonymous functions (I dunno where that dumb syntax came from by Nix also uses it and it's pretty awful). Omitting brackets and commas for function calls is also a bad decision if you care about friendliness. I have yet to find a language where that doesn't make the code harder to read and understand.
Are the Roc people really doing everything they can to ruin everything they had going for them? Appealing to those who know nothing and won't be willing to touch anything FP at the cost of annoying those who actually want to try it is just so stupid
I say this as someone who enjoys reading Rust more than Haskell or Elm -- that looks like a really bad idea for aesthetic reasons anyway. I mean if you want the syntax to look like Zig or Rust, perhaps go all the way there instead of making a kind of a mutant hybrid like this. Syntax is superficial and the semantics actually matter, but that doesn't mean the syntax can be just anything.
Is there some deeper technical reason for making such changes?
I feel that he got a lot of pressure from the FP community and wrote a bunch of nonsense instead of being straightforward with them.
The only relevant reason he lists is point-free, but he doesn't go far enough. Point-free very often turns into write-only balls of unmaintainable nastiness. Wanting to discourage this behavior is a perfectly reasonable position. Unfortunately, this one true argument is given the most tepid treatment of all the reasons.
Everything else doesn't hold water.
As he knows way better than most, Elm has auto-curry and has been the inspiration for several other languages getting better error messages.
Any language with higher-order functions can give a function as a result and if you haven't read the docs or checked the type, you won't expect it. He left higher-order function in, so even he doesn't really believe this complaint.
The argument about currying and pipe isn't really true. The pipe is static syntax known to the compiler at compile time. You could just decide that the left argument is applied/curried to the function before the right argument.
I particularly hate the learning curve argument. Lots of great and necessary things are hard to learn. The only question is a value judgement about if the learning is worth the payoff. I'd guess that most of the Roc users already learned about currying with a more popular FP language before every looking at Roc, so I don't think this argument really applies here (though I wouldn't really care if he still believed it wasn't worth the learning payoff for the fraction of remaining users).
To reiterate, I agree with his conclusion to exclude currying, but I wish he were more straightforward with his one good answer that would tick off a lot of FP users rather than resorting to a ton of strawman arguments.
No trolling/nitpicking from me: You wrote <<The only relevant reason he lists is point-free>>. What do you mean by "point-free"... or did you write "point three" and it was auto-corrected on a mobile phone?
I also tried Googling for that term (never heard before), and I found these:
We recently changed Roc's lambda syntax from the syntax that languages like Elm and Haskell use...
foo = \arg1, arg2 ->
body
...to this:
foo = |arg1, arg2|
body
The reason for this change was that we have a new and extremely well-received language feature (landed but not yet formally announced) which results in `->` and `=>` having different meanings in the type system. This made it confusing to have `->` in the syntax for anonymous functions, because it seemed to suggest a connection with the type-level `->` that wasn't actually there.
The most popular syntax that mainstream languages use today for anonymous functions is something like `(arg1, arg2) => body` but of course that has the same problem with having an arrow in it, so changing to that wouldn't have solved the problem.
Rust uses `|arg1, arg2| body` (and Ruby kinda uses it too for blocks), and we'd all had fine experiences using that syntax in Rust, so we chose it as the new lambda syntax. You can see the new syntax in the code example at the top of roc-lang.org.
Yeah Rust compiles slowly, so we need two more half-baked languages - Zig and Roc, both of which I couldn't care less.
Rust's slow compilation comes from lots of features and an excellent generated machine code quality. Both Zig and Roc will be equally slow or slower if they match what Rust offers.
If all they want is fast compilation, they can just try Pascal.
this didn't faze me in the least because it's just a more easily typed λ, the lambda character, which has for a long time now (many decades) been used to describe anonymous functions (i.e. "lambdas")
did you never take any formal CS education? if not, that might explain it
so before you jump to calling it "dumb", maybe next time lean on Chesterton's Fence for a bit.
It's nice to see Zig continuing to gain support. I have no idea why I've ended up siding with Zig in the lower languages wars.
I used to root for the D programming language but it seems to have got stuck and never gained a good ecosystem. I've disliked Rust from the first time I saw it and have never warmed up to it's particular trade offs. C feels unergonomic these days and C++ is overfull with complexity. Zig feels like a nice pragmatic middle ground.
I actually think Rust is probably perfectly suited to a number of tasks, but I feel I would default to choosing Zig unless I was certain beyond doubt that I needed specific Rust safety features.
I wanted to like Rust (memory safety and performance - what's not to like?) but both of my experiments with it ended in frustration. It seemed a way too complex language for what I needed.
Recently, a coworker of mine made a great observation that made everything clear to me. I was looking for a replacement for C, but Rust is actually a replacement for C++. Totally different beast - powerful but complex. I need to see if Zig is any closer to C in spirit.
> I was looking for a replacement for C, but Rust is actually a replacement for C++. Totally different beast - powerful but complex.
I've seen this sentiment a lot, and I have to say it's always puzzled me. The difference between Rust and basically any other popular language is that the former has memory safety without GC†. The difference between C++ and C is that the former is a large multi-paradigm language, while the latter is a minimalist language. These are completely different axes.
There is no corresponding popular replacement for C that's more minimalist than Rust and memory safe.
† Reference counting is a form of garbage collection.
This is actually accurate. While Rust is a great replacement for C, so is C++. But Rust has tons of extra features that C doesn't have, just like C++. Moving from C++ to Rust is a sideways move. Moving from C to Rust is definitely an increase in complexity and abstraction.
Rust is a great replacement for C++ as it fits into the same place in the stack of tools.
Somewhat related, even when I work with C++ I use it as "C with RAII". What I actually want is a scheme (R5RS) with manual memory management and a borrow checker. I don't know how well such a monstrosity would actually work in practice, but it's what I've convinced myself that I want.
Last I checked, rust touted itself as a systems programming language, which C++ kinda is but mostly isn't (many C++ features just aren't appropriate or are poorly suited for systems programming).
I would never choose Rust over C++ for scientific programming, because Rust metaprogramming features are so poor.
However I'd probably choose Rust over C in many areas where C is best suited.
So to me the venn diagram of utility shows Rust to overlap with C far more than C++.
I've played with Hare, Zig, and Odin. Odin is my favorite. It's a fair bit faster to compile (similar to Hare), and has the nicest syntax (subjectively speaking). I wish it would get more traction. Looking forward to trying Jai if it ever makes it to GA.
Interestingly enough, it is one of the only "talked about" languages I have almost no experience with. Even Roc I've watched a few YouTube videos on. I've only really seen Odin mentioned on X, not even a HN post.
I suppose there is also Jai in a similar space as well, although I'm not a devotee to Jonathan Blow and I don't share much of the excitement his followers seem to have.
I do feel Zig has the current trend moving in its favor, with projects like Ghostty and Bun gaining prominence. I think Odin would need something like that to really capture attention.
It's questionable how attractive Zig really is, outside of people getting caught in the wave generated by its foundation, where there is a clear self financial benefit. A Zig language review indicates there are many questions as to its usefulness, and why it hasn't hit v1 yet, over just using C and the many available libraries of that ecosystem. Famous C programmers like Muratori, homemade hero, do not like Zig nor recommend it[1].
Rust is primarily aimed at more specific use cases, evolving around memory safety and low(er) level programming. So where people might dislike the confusing syntax and difficulty of learning either Zig or Rust (among other inconveniences), its harder to make arguments against Rust's usefulness for safety, maturity (v1 plus), or present job market popularity. Zig does not have the luxury of those bonus points.
When it comes to general-purpose programming, including for hobbyists or students, there are many other alternative languages that are arguably much more attractive, easier to use, and/or easier to learn. Golang, Vlang, Odin, Jai, C3, etc...
I'm happy for Zig to gain support for one major reason: Zig does not engage in "woke" culture the way Rust does.
Zig seems to take a neutral, technical-first stance, while Rust has a strong focus on inclusivity, strict moderation, and social policies like with the foundation drama (which is just nonsense for a programming language).
I really wish Nim would have won the language wars though.
Roc couldn't be optimized for writing the Roc compiler without sacrificing some of its own goals. For example, Roc is completely memory-safe, but the compiler needs to do memory-unsafe things. Introducing memory-unsafety into Roc would just make it worse. Roc has excellent performance, but it will never be as fast as a systems language that allows you to do manual memory management. This is by design and is what you want for the vast majority of applications.
There are a number of new imperative features that have been (or will be) added to the language that capture a lot of the convenience of imperative languages without losing functional guarantees. Richard gave a talk about it here: https://youtu.be/42TUAKhzlRI?feature=shared.
It still feels kinda weird. Parsers, compilers etc are traditionally considered one of the "natural" applications for functional programming languages.
FP is bad for computing the Fibonacci series unless you have the compiler optimization to turn it into a loop (as seen in imperative languages).
To be fair, most practical FP languages have that, but I never saw the appeal for a strictly functional general purpose language. The situations where I wished one could not use imperative constructs are very domain specific.
That's an interesting point, and something I thought of when reading the parser combinator vs. recursive descent point
Around 2014, I did some experiments with OCaml, and liked it very much
Then I went to do lexing and parsing in OCaml, and my experience was that Python/C++ are actually better for that.
Lexing and parsing are inherently stateful, it's natural to express those algorithms imperatively. I never found parser combinators compelling, and I don't think there are many big / "real" language implementations that uses them, if any. They are probably OK for small languages and DSLs
I use regular expressions as much as possible, so it's more declarative/functional. But you still need imperative logic around them IME [1], even in the lexer, and also in the parser.
---
So yeah I think that functional languages ARE good for writing or at least prototyping compilers -- there are a lots of examples I've seen, and sometimes I'm jealous of the expressiveness
But as far as writing lexers and parsers, they don't seem like an improvement, and are probably a little worse
> Zig came up with a way around this: compile directly to LLVM bitcode (LLVM has strong backwards-compatibility on its bitcode but not on its public-facing API) and then upgrades become trivial because we can keep our existing code generation the same.
i'm glad that zig helps offset what i find a rather worrying trend -- replacing protocols with libraries (and as a consequence designing new underlying protocols in view of their only being used through the vendor library).
protocols are traditionally designed to facilitate an independent implementation; in fact, many standards ratification processes require several independent implementations as a prerequisite.
libraries (or worse, frameworks) intermingle the actual api with their own design, and necessitate a single implementation.
just the other day i wanted to display a notification a linux desktop where it wasn't convenient to depend on a library (it was a game, so not a traditional application expected to have many dependencies). the protocol (there is one, wrapped by the library) is very unpleasant, but i got it working out of spite.
and of course, when there is a perfectly nice protocol available (llvm ir, in either the bitcode or text representation) why not choose it? at least on unix, where starting processes and interprocess communication is cheap. (and as an added bonus, you won't crash when llvm does.)
Go was built while waiting for C++ to compile- fast compilation was an implicit design goal.
Rust on the other hand didn’t prioritize compile times and ended up making design decisions that make faster compilation difficult to achieve. To me it’s the biggest pain point with Rust for a large code base and that seems to be the sentiment here as well.
The Rust ecosystem has long downplayed the importance of compile times.
Many foundational crates, serde for example, contribute much more to compile times than they need to.
I spent a long time reinventing many foundational rust crates for my game engine, and I proved its possible to attain similar features in a fraction of the compile time, but it’s a losing battle to forgo most of the ecosystem.
Thank you for your work! Rust is still a great language.
I think a significant portion of our pain with rust compile times is self inflicted due to the natural growth of our crate organization and stages.
I still think the rewrite in zig is the right choice for us for various reasons, but I think at least a chunk of our compile times issues are self inflicted (though this happens to any software project that grows organically and is 300k LOC)
But were any _language_ decisions discarded due to compile time concerns? I don't think anyone would claim the folks working on the rust compiler don't care.
On that note, thank you for your part! I sure enjoy your work! :)
From listening to Feldman's podcast, this doesn't really come as a surprise to me. The rigor that Rust demands seems not to jibe with his 'worse is better' approach. That coupled with the fact they already switched the stdlib from Rust to Zig. The real question I have is why he chose Rust in the first place.
Zig was not ready or nearly as popular back in 2019 when the compiler was started.
Not to mention, Richard has a background mostly doing higher level programming. So jumping all the way to something like C or Zig would have been a very big step.
Sometimes you need a stepping stone to learn and figure out what you really want.
> The real question I have is why he chose Rust in the first place.
If you read the linked post carefully you will know.
> Compile times aside, the strengths and weaknesses of Rust and Zig today are much different than they were when I wrote the first line of code in Roc's Rust compiler in 2019. Back then, Rust was relatively mature and Zig was far from where it is today.
This is more about simplicity, maintainability, and possiblity for new contributors to easily jump in and fix things. Our current parser is not fun for new contributors to learn. Also, I think parser combinators obsficate a lot of the tracking required for robust error messages.
Personally, I find parser combinators nice for small things but painful for large and robust things.
When I used parser combinations in rust years ago the compile times were really long. In also think it’s a strange reason to move away from rust as a language.
This decisions looks well-reasoned. It acknowledges the strengths of their current implementation, and explains why another language may serve their goals even better. I can follow along as someone interested in general software decision-making, even though I'm not particularly invested in either language.
One specific thing I like is that the motivation for this rewrite is organic, i.e., not driven by an external pressure campaign. It's refreshing to see a drama-free rewrite.
I had an issue where the corporate spyware thought my cross compiler was potential malware and running scans on every file access. My compile times went from a few seconds to 2 minutes. And my codebase has a dozen targets. Where building, fixing, running tests, checking in would take half and hour it now took the whole afternoon.
I think I'd rather stick with C and run static analysis tools on my code when generating release candidates than have to deal with that.
Strangely they don't mention that (allegedly? According to them at least) they already use Zig for the standard library[0]. Which was also started to be written in Rust, then it got rewritten in Zig:
I was talking about C++ 20 modules and C++23 modularised standard library, and that has little to do with LLVM, rather clang.
LLVM improvements will do little to improve Rust, because the major issue is the lack of parallelism and massive amount of LLVM IR that the fronted gives to the backend to handle.
> While we're at it, we also want to convert it to use recursive descent; it was originally written using parser combinators because that was what I was comfortable with at the time
Aren't parser combinators just one way to do recursive descent?
It will be interesting to compare compile times afterwards. I'm also interested if there's any Rust features that are 'taken for granted' which will make the Zig version less pleasant to read or write. Hopefully the old Rust repo is archived so I can reference it a year from now!
It will be impossible to ever truly do a one-to-one comparison. First, there may be a ton of code in the existing implementation that gets cut out by a mass rewrite due to better, more informed, architecture decisions. On the other hand, it may be possible that new features find their way into the rewrite that cause the fully Zig implementation to be larger than the original Rust-Zig implementation. I don't think any comparison will be fully "fair". A smaller implementation could be argued as only being possible in Rust or Zig because of its advantages over the other language. I expect results to be controversial.
I am also curios about at least some benchmark even if it is not one-to-one perfect comparison. There should be at least one case-study where they have shorter feedback loop with Zig than with Rust. It would be interesting to see that.
Maybe I need to give it another shot, but Zig was a terribly unpleasant language last I tried to use it.
Their motivation seems to justify the decision, but I'm pretty sure this will scare away potential contributors. I've been following Roc personally, and probably will continue to do so, but this definitely has killed a lot of interest for me.
Yeah, the compiler was started in 2019 when zig wasn't nearly as viable of an option. On top of that, you don't necessarily know what you want until you build something.
I'm part of the core team, and I'm honestly still a bit surprised we're switching to zig. I think it makes sense, it just kinda finally aligned and was decided now. I guess enough technical debt piled up and the core of the language was clearly discovered. So we really know what we want now.
Richard Feldman is an amazing educator. He got me into Elm back in the days.
I am a bit sad that Roc is following similar pitfalls as Elm in its quest to be simple by rejecting to have more advanced type system features. That just does not work. In dynamic languages you can opt for minimalism because everything goes by default but in fully statically typed languages things can painful quickly. Even golang had to add generics.
Still many amazing ideas in the language. The platform concept is pretty neat. Not doing implicit currying is a good move.
What particular type system featured do you miss? I think Elm proved that a restricted type system DOES WORK for large amounts of software. It's been a few years since I've written Elm, so I don't recall specific painful memories I've had with the type system.
One thing that makes Go's restrictive type system more bearable is a fantastic stlib package for analyzing and generating Go code. I wonder if Roc will get anything similar.
I’ve been reading a lot about comptime in Zig and it’s really cool, it unifies and simplifies a lot of metaprogramming concepts that normally end up being the ugliest and clunkiest parts of most languages.
I’ve been immersed in Swift for a couple years after working as a Go programmer, and I find myself pining for a language that’s more expressive than the latter and more concise than the former.
> Roc's compiler has always been written in Rust, and we do not plan to self-host. We have a FAQ entry explaining why, and none of our reasoning has changed on that subject.
because that's hard. also it's one choice I respect them for not making: don't self host until you have actual users and momentum (unless you want to to prove a point). if Roc intends to have industry usage, keeping it in another language for now is a good move.
Roc never plans to self host. We want roc the compiler in the long term to be as nice as possible for end users. A big part of that is as fast as possible. While roc can be fast, it can't compete with all the techniques that can be done in rust/zig/c/c++. It fundamentally is a higher level language to increase developer joy and reduce bugs. So it isn't the right tool when you are trying to reach the limits of hardware like this.
Yay for Zig! I'm a big Zig fan. It's seemless interop with C makes it super fun. I really enjoy writing code in a C-style way. Its still got some rough edges, but its definitely super fun and enjoyable to write imo.
It is interesting to note an intentional choice to go from a "safe" language to an "unsafe" language for a program whose use is not without security implications (even if it's not network-facing).
What matters is that the compiled program is safe. Who cares if the compiler leaks memory? It's only going to run for a bit and then shut down, the OS will take care of it.
I guess if you want to never limit yourself in terms of performance, you're kinda stuck with either C/C++/Zig/Rust/etc.
GCed languages can be fast, probably even as fast as any of the aforementioned languages with enough engineering effort, but in terms of high performance, it's actually easier to write high performance code in the aforementioned languages rather than in a GCed language.
The same reason why they're not self-hosting. They want compiler performance to be in the top C, C++, Rust, Zig tier. OCaml isn't slow, but at best it's in the second Java, C#, etc. tier.
Not only is it new, it has yet to reach version 1 as a language with any guaranteed level of stability. In every version release there's a disclaimer that seems to discourage its use in production software.
> Zig has known bugs and even some miscompilations.
> Zig is immature. Even with Zig 0.13.0, working on a non-trivial project using Zig will likely require participating in the development process.
But then again, it's a great thing for the language to be used in larger public projects (like tigerbeetle, bun, ghostty). This will drive its development and adoption.
Its honestly pretty dang good now. I can bootstrap Zig from C in about 3-5 minutes, and then use the self-hosted stage3 compiler to compile Zig very quickly (under 1 minute iirc) Once the cache kicks in, its even faster. I guess my overall gauge on this is the impression I get. Zig build times never annoy me and I never think about it... especially compared to clang, gcc and Rust.
Not necessarily! Each Roc app runs on a particular platform which is built in a host language for a specific domain. That host language could be Rust, Zig, C, C++, Go, Swift, etc. It's possible the basic-webserver platform will be rewritten to Zig but it doesn't need to be.
This is the first justification of not using Rust that I actually agree with. Well written.
I recommend reading Roc's FAQ too - it's got some really great points. E.g. I'm internally screaming YESSS! to this: https://www.roc-lang.org/faq.html#curried-functions
But then it has other weird features too, like they seem to be really emphasising "friendliness" (great!) but then it has weird syntax like `\` for anonymous functions (I dunno where that dumb syntax came from by Nix also uses it and it's pretty awful). Omitting brackets and commas for function calls is also a bad decision if you care about friendliness. I have yet to find a language where that doesn't make the code harder to read and understand.
The syntax came from Elm, which got it’s syntax from Haskell (where Nix also got it from) which got its syntax from ML.
It’s a syntax that’s several decades old at this point.
It’s different, but not harder. If you learned ML first, you’d found Algol/C-like syntax equally strange.
(ETA: speaking strictly about anonymous functions; on rereading you might be talking about the absence of parens and commas for function application.)
That's not ML syntax. Haskell got it from Miranda, I guess?
In SML you use the `fn` keyword to create an anonymous function; in Ocaml, it's `fun` instead.
9 replies →
They recently changed the syntax to add parens, commas and use `|arg|` for closures :)
https://github.com/roc-lang/roc/releases/tag/0.0.0-alpha2-ro...
Are the Roc people really doing everything they can to ruin everything they had going for them? Appealing to those who know nothing and won't be willing to touch anything FP at the cost of annoying those who actually want to try it is just so stupid
I say this as someone who enjoys reading Rust more than Haskell or Elm -- that looks like a really bad idea for aesthetic reasons anyway. I mean if you want the syntax to look like Zig or Rust, perhaps go all the way there instead of making a kind of a mutant hybrid like this. Syntax is superficial and the semantics actually matter, but that doesn't mean the syntax can be just anything.
Is there some deeper technical reason for making such changes?
Boooo
oh wow it went from being a very clear language to looking more like a hodgepodge of a few different languages.
Ah great improvements! I don't know why the sibling comments are so negative; this is clearly better.
16 replies →
Pretty much all of those changes look bad to me.
Jesus, why? This is a bummer.
1 reply →
I feel that he got a lot of pressure from the FP community and wrote a bunch of nonsense instead of being straightforward with them.
The only relevant reason he lists is point-free, but he doesn't go far enough. Point-free very often turns into write-only balls of unmaintainable nastiness. Wanting to discourage this behavior is a perfectly reasonable position. Unfortunately, this one true argument is given the most tepid treatment of all the reasons.
Everything else doesn't hold water.
As he knows way better than most, Elm has auto-curry and has been the inspiration for several other languages getting better error messages.
Any language with higher-order functions can give a function as a result and if you haven't read the docs or checked the type, you won't expect it. He left higher-order function in, so even he doesn't really believe this complaint.
The argument about currying and pipe isn't really true. The pipe is static syntax known to the compiler at compile time. You could just decide that the left argument is applied/curried to the function before the right argument.
I particularly hate the learning curve argument. Lots of great and necessary things are hard to learn. The only question is a value judgement about if the learning is worth the payoff. I'd guess that most of the Roc users already learned about currying with a more popular FP language before every looking at Roc, so I don't think this argument really applies here (though I wouldn't really care if he still believed it wasn't worth the learning payoff for the fraction of remaining users).
To reiterate, I agree with his conclusion to exclude currying, but I wish he were more straightforward with his one good answer that would tick off a lot of FP users rather than resorting to a ton of strawman arguments.
No trolling/nitpicking from me: You wrote <<The only relevant reason he lists is point-free>>. What do you mean by "point-free"... or did you write "point three" and it was auto-corrected on a mobile phone?
I also tried Googling for that term (never heard before), and I found these:
If you really meant "point-free", can you tell me where in his post he mentions it? I would like to learn more.
3 replies →
It's the closest you get to 'λ' on a US keyboard.
So this assumes knowledge of an obscure theoretical programming language, and a dubious symbol replacement. Yeah...
2 replies →
IIRC, Richard explain that in one of his videos about Roc. I have seen at least a handful of them.
I believe Haskell uses it as well.
my hot take: the language should accept \, but formatters should replace it with λ
1 reply →
^\
> `\` for anonymous functions
A one-character ASCII rendering of the Greek lowercase letter lambda: λ
λx → x + 5
\x -> x + 5
Opposite effect one, lost interest in Roc after reading that.
If anything I don't think Haskell goes far enough the automatic currying, points free stuff. If you're going to be declarative, don't half ass it.
We recently changed Roc's lambda syntax from the syntax that languages like Elm and Haskell use...
...to this:
The reason for this change was that we have a new and extremely well-received language feature (landed but not yet formally announced) which results in `->` and `=>` having different meanings in the type system. This made it confusing to have `->` in the syntax for anonymous functions, because it seemed to suggest a connection with the type-level `->` that wasn't actually there.
The most popular syntax that mainstream languages use today for anonymous functions is something like `(arg1, arg2) => body` but of course that has the same problem with having an arrow in it, so changing to that wouldn't have solved the problem.
Rust uses `|arg1, arg2| body` (and Ruby kinda uses it too for blocks), and we'd all had fine experiences using that syntax in Rust, so we chose it as the new lambda syntax. You can see the new syntax in the code example at the top of roc-lang.org.
R has that too.
really? thought it just function(x)
Yeah Rust compiles slowly, so we need two more half-baked languages - Zig and Roc, both of which I couldn't care less.
Rust's slow compilation comes from lots of features and an excellent generated machine code quality. Both Zig and Roc will be equally slow or slower if they match what Rust offers.
If all they want is fast compilation, they can just try Pascal.
> \ for anonymous functions
this didn't faze me in the least because it's just a more easily typed λ, the lambda character, which has for a long time now (many decades) been used to describe anonymous functions (i.e. "lambdas")
did you never take any formal CS education? if not, that might explain it
so before you jump to calling it "dumb", maybe next time lean on Chesterton's Fence for a bit.
https://sproutsschools.com/chesterton-fence-dont-destroy-wha...
That said, granted, the fact that it's also the escape character is problematic. Maybe /\ might have been better but that's even harder to type.
It's nice to see Zig continuing to gain support. I have no idea why I've ended up siding with Zig in the lower languages wars.
I used to root for the D programming language but it seems to have got stuck and never gained a good ecosystem. I've disliked Rust from the first time I saw it and have never warmed up to it's particular trade offs. C feels unergonomic these days and C++ is overfull with complexity. Zig feels like a nice pragmatic middle ground.
I actually think Rust is probably perfectly suited to a number of tasks, but I feel I would default to choosing Zig unless I was certain beyond doubt that I needed specific Rust safety features.
I wanted to like Rust (memory safety and performance - what's not to like?) but both of my experiments with it ended in frustration. It seemed a way too complex language for what I needed.
Recently, a coworker of mine made a great observation that made everything clear to me. I was looking for a replacement for C, but Rust is actually a replacement for C++. Totally different beast - powerful but complex. I need to see if Zig is any closer to C in spirit.
> I was looking for a replacement for C, but Rust is actually a replacement for C++. Totally different beast - powerful but complex.
I've seen this sentiment a lot, and I have to say it's always puzzled me. The difference between Rust and basically any other popular language is that the former has memory safety without GC†. The difference between C++ and C is that the former is a large multi-paradigm language, while the latter is a minimalist language. These are completely different axes.
There is no corresponding popular replacement for C that's more minimalist than Rust and memory safe.
† Reference counting is a form of garbage collection.
71 replies →
This is actually accurate. While Rust is a great replacement for C, so is C++. But Rust has tons of extra features that C doesn't have, just like C++. Moving from C++ to Rust is a sideways move. Moving from C to Rust is definitely an increase in complexity and abstraction.
Rust is a great replacement for C++ as it fits into the same place in the stack of tools.
Go is not a C or Python replacement.
Zig is a good replacement for C.
11 replies →
Somewhat related, even when I work with C++ I use it as "C with RAII". What I actually want is a scheme (R5RS) with manual memory management and a borrow checker. I don't know how well such a monstrosity would actually work in practice, but it's what I've convinced myself that I want.
2 replies →
>"but Rust is actually a replacement for C++. "
I would disagree. C++ provides way more features than Rust and to me Rust feels way more constrained comparatively.
3 replies →
> Rust is actually a replacement for C++
Last I checked, rust touted itself as a systems programming language, which C++ kinda is but mostly isn't (many C++ features just aren't appropriate or are poorly suited for systems programming).
I would never choose Rust over C++ for scientific programming, because Rust metaprogramming features are so poor.
However I'd probably choose Rust over C in many areas where C is best suited.
So to me the venn diagram of utility shows Rust to overlap with C far more than C++.
I read a blog that called Rust a language that was aimed to be high-level but without GC.
My experience with Zig is that it's also a plausible replacement for C++
8 replies →
I've played with Hare, Zig, and Odin. Odin is my favorite. It's a fair bit faster to compile (similar to Hare), and has the nicest syntax (subjectively speaking). I wish it would get more traction. Looking forward to trying Jai if it ever makes it to GA.
Odin is the best (followed by Zig)
Odin has the best approach for "standard library" by blessing/vendoring immensely useful libraries
Odin also has the best approach for Vector Math with native Vector and Matrix types
1 reply →
From what you describe, you might also like Odin.
Interestingly enough, it is one of the only "talked about" languages I have almost no experience with. Even Roc I've watched a few YouTube videos on. I've only really seen Odin mentioned on X, not even a HN post.
I suppose there is also Jai in a similar space as well, although I'm not a devotee to Jonathan Blow and I don't share much of the excitement his followers seem to have.
I do feel Zig has the current trend moving in its favor, with projects like Ghostty and Bun gaining prominence. I think Odin would need something like that to really capture attention.
3 replies →
It's questionable how attractive Zig really is, outside of people getting caught in the wave generated by its foundation, where there is a clear self financial benefit. A Zig language review indicates there are many questions as to its usefulness, and why it hasn't hit v1 yet, over just using C and the many available libraries of that ecosystem. Famous C programmers like Muratori, homemade hero, do not like Zig nor recommend it[1].
Rust is primarily aimed at more specific use cases, evolving around memory safety and low(er) level programming. So where people might dislike the confusing syntax and difficulty of learning either Zig or Rust (among other inconveniences), its harder to make arguments against Rust's usefulness for safety, maturity (v1 plus), or present job market popularity. Zig does not have the luxury of those bonus points.
When it comes to general-purpose programming, including for hobbyists or students, there are many other alternative languages that are arguably much more attractive, easier to use, and/or easier to learn. Golang, Vlang, Odin, Jai, C3, etc...
[1]: https://www.youtube.com/watch?v=uVVhwALd0o4 ("Language Perf and Picking A Lang Stream" from 29:50)
I don't say the "lower language wars" as being between zig and rust. I see it as being between old and new. And I'm on team new.
I'm happy for Zig to gain support for one major reason: Zig does not engage in "woke" culture the way Rust does.
Zig seems to take a neutral, technical-first stance, while Rust has a strong focus on inclusivity, strict moderation, and social policies like with the foundation drama (which is just nonsense for a programming language).
I really wish Nim would have won the language wars though.
I can't help but feel like the Roc team has an attitude of "imperative programming for me, but not for thee".
And now they are doubling down on that by moving from "OCaml meets C++" to "C, the good parts"!
If FP isn't good for writing a compiler, what is it good for?
Roc couldn't be optimized for writing the Roc compiler without sacrificing some of its own goals. For example, Roc is completely memory-safe, but the compiler needs to do memory-unsafe things. Introducing memory-unsafety into Roc would just make it worse. Roc has excellent performance, but it will never be as fast as a systems language that allows you to do manual memory management. This is by design and is what you want for the vast majority of applications.
There are a number of new imperative features that have been (or will be) added to the language that capture a lot of the convenience of imperative languages without losing functional guarantees. Richard gave a talk about it here: https://youtu.be/42TUAKhzlRI?feature=shared.
It still feels kinda weird. Parsers, compilers etc are traditionally considered one of the "natural" applications for functional programming languages.
There are plenty of compilers written in memory safe languages, including the list of self hosted ones linked from the gist describing the rewrite.
>but the compiler needs to do memory-unsafe things
sorry, but why?
3 replies →
> If FP isn't good for writing a compiler, what is it good for?
Summing the Fibonacci sequence I guess.
FP is bad for computing the Fibonacci series unless you have the compiler optimization to turn it into a loop (as seen in imperative languages).
To be fair, most practical FP languages have that, but I never saw the appeal for a strictly functional general purpose language. The situations where I wished one could not use imperative constructs are very domain specific.
other FP languages bootstrapped onto themselves IIRC
According to [1], they want to use a systems-level language for performance.
[1] https://www.roc-lang.org/faq#self-hosted-compiler
"The split of Rust for the compiler and Zig for the standard library has worked well so far, and there are no plans to change it."
I assume that statement will need updating.
2 replies →
"Unix system programming in OCaml"
https://ocaml.github.io/ocamlunix/ocamlunix.html
That's an interesting point, and something I thought of when reading the parser combinator vs. recursive descent point
Around 2014, I did some experiments with OCaml, and liked it very much
Then I went to do lexing and parsing in OCaml, and my experience was that Python/C++ are actually better for that.
Lexing and parsing are inherently stateful, it's natural to express those algorithms imperatively. I never found parser combinators compelling, and I don't think there are many big / "real" language implementations that uses them, if any. They are probably OK for small languages and DSLs
I use regular expressions as much as possible, so it's more declarative/functional. But you still need imperative logic around them IME [1], even in the lexer, and also in the parser.
---
So yeah I think that functional languages ARE good for writing or at least prototyping compilers -- there are a lots of examples I've seen, and sometimes I'm jealous of the expressiveness
But as far as writing lexers and parsers, they don't seem like an improvement, and are probably a little worse
[1] e.g. lexer modes - https://www.oilshell.org/blog/2017/12/17.html
OCaml allows mutation via reference cells.
1 reply →
Huh? The whole point of Roc is to let you easily put both imperative and functional code in the same project through Roc's platform system.
You get to decide what part of your code should be imperative, and which should be functional.
https://www.roc-lang.org/faq.html#self-hosted-compiler
So why is it a non-goal of Roc to be implemented with this approach?
5 replies →
As an FP fan, I agree. This is disappointing.
> Zig came up with a way around this: compile directly to LLVM bitcode (LLVM has strong backwards-compatibility on its bitcode but not on its public-facing API) and then upgrades become trivial because we can keep our existing code generation the same.
i'm glad that zig helps offset what i find a rather worrying trend -- replacing protocols with libraries (and as a consequence designing new underlying protocols in view of their only being used through the vendor library).
protocols are traditionally designed to facilitate an independent implementation; in fact, many standards ratification processes require several independent implementations as a prerequisite.
libraries (or worse, frameworks) intermingle the actual api with their own design, and necessitate a single implementation.
just the other day i wanted to display a notification a linux desktop where it wasn't convenient to depend on a library (it was a game, so not a traditional application expected to have many dependencies). the protocol (there is one, wrapped by the library) is very unpleasant, but i got it working out of spite.
and of course, when there is a perfectly nice protocol available (llvm ir, in either the bitcode or text representation) why not choose it? at least on unix, where starting processes and interprocess communication is cheap. (and as an added bonus, you won't crash when llvm does.)
LLVM IR is not stable, which is a prerequisite for a good protocol.
Go was built while waiting for C++ to compile- fast compilation was an implicit design goal.
Rust on the other hand didn’t prioritize compile times and ended up making design decisions that make faster compilation difficult to achieve. To me it’s the biggest pain point with Rust for a large code base and that seems to be the sentiment here as well.
Rust always cared about compile times. Much of the motivation to switch from rustboot to rustc was compile times.
(Source: I wrote much of the Rust compiler.)
The Rust ecosystem has long downplayed the importance of compile times.
Many foundational crates, serde for example, contribute much more to compile times than they need to.
I spent a long time reinventing many foundational rust crates for my game engine, and I proved its possible to attain similar features in a fraction of the compile time, but it’s a losing battle to forgo most of the ecosystem.
Thank you for your work! Rust is still a great language.
I think a significant portion of our pain with rust compile times is self inflicted due to the natural growth of our crate organization and stages.
I still think the rewrite in zig is the right choice for us for various reasons, but I think at least a chunk of our compile times issues are self inflicted (though this happens to any software project that grows organically and is 300k LOC)
But were any _language_ decisions discarded due to compile time concerns? I don't think anyone would claim the folks working on the rust compiler don't care.
On that note, thank you for your part! I sure enjoy your work! :)
2 replies →
Yet, the whole crate model giving up on ABI stability as a goal hurts a lot, both for performance and for sanity.
Why aren't people designing modern languages to make it easier to keep a stable ABI, rather than giving up entirely?
6 replies →
From listening to Feldman's podcast, this doesn't really come as a surprise to me. The rigor that Rust demands seems not to jibe with his 'worse is better' approach. That coupled with the fact they already switched the stdlib from Rust to Zig. The real question I have is why he chose Rust in the first place.
Zig was not ready or nearly as popular back in 2019 when the compiler was started.
Not to mention, Richard has a background mostly doing higher level programming. So jumping all the way to something like C or Zig would have been a very big step.
Sometimes you need a stepping stone to learn and figure out what you really want.
> The real question I have is why he chose Rust in the first place.
If you read the linked post carefully you will know.
> Compile times aside, the strengths and weaknesses of Rust and Zig today are much different than they were when I wrote the first line of code in Roc's Rust compiler in 2019. Back then, Rust was relatively mature and Zig was far from where it is today.
It’s covered in TFA, but the tldr is they started when Zig was immature.
> we also want to convert [the parser] to use recursive descent; it was originally written using parser combinators
Almost all parser combinators are recursive descent with backtracking, they just add higher-order plumbing.
I have a feeling that whatever issue they've encountered with the combinator approach could have been worked around by handwriting only a few pieces.
This is more about simplicity, maintainability, and possiblity for new contributors to easily jump in and fix things. Our current parser is not fun for new contributors to learn. Also, I think parser combinators obsficate a lot of the tracking required for robust error messages.
Personally, I find parser combinators nice for small things but painful for large and robust things.
When I used parser combinations in rust years ago the compile times were really long. In also think it’s a strange reason to move away from rust as a language.
What is Roc? Here it is: https://www.roc-lang.org/
This decisions looks well-reasoned. It acknowledges the strengths of their current implementation, and explains why another language may serve their goals even better. I can follow along as someone interested in general software decision-making, even though I'm not particularly invested in either language.
One specific thing I like is that the motivation for this rewrite is organic, i.e., not driven by an external pressure campaign. It's refreshing to see a drama-free rewrite.
I remember reading the HN thread when Fish rewrote to Rust. A big reason because “they wanted to stay current” so they rewrote from C++ to Rust.
Someone made a joke comment how in a few years there would be tons of HN threads about rewriting Rust to Zig.
The rationale given makes perfect sense, but isn't it risky to rewrite in a language that is yet to hit 1.0?
For sure, but we have a really good relationship with the zig folks and they are willing to help us out.
On top of that, zig has gotten a lot more robust and stable of the last few releases. Upgrading is getting smoother.
But yeah, it is a risk.
Much better before than after!
Why? There's the possibility that Zig will introduce breaking changes and the Roc compiler will have to be revised in light of those changes.
1 reply →
I use rust a lot but, Roc is right: compilation times are not proportional to the safety you gain. At least in my project.
I had an issue where the corporate spyware thought my cross compiler was potential malware and running scans on every file access. My compile times went from a few seconds to 2 minutes. And my codebase has a dozen targets. Where building, fixing, running tests, checking in would take half and hour it now took the whole afternoon.
I think I'd rather stick with C and run static analysis tools on my code when generating release candidates than have to deal with that.
That's hardly the language's fault
Strangely they don't mention that (allegedly? According to them at least) they already use Zig for the standard library[0]. Which was also started to be written in Rust, then it got rewritten in Zig:
[0] : https://www.roc-lang.org/faq#rust-and-zig
> (and to unblock updating to the latest Zig version, which we've use for our stdlib for years)
It's somewhere in the middle of the list of the "Why now?" section.
They do mention it but there's only 10k lines of Zig, so it's hardly a blip in the grand scheme of things.
The way compile times are stressed all over the place is a very good point.
Papercuts like this do make or break adoption.
Even C++ is improving on that front.
As is Rust. And the C++ compiler performance improvements in LLVM apply to Rust.
I was talking about C++ 20 modules and C++23 modularised standard library, and that has little to do with LLVM, rather clang.
LLVM improvements will do little to improve Rust, because the major issue is the lack of parallelism and massive amount of LLVM IR that the fronted gives to the backend to handle.
2 replies →
ccache made C++ usable for me.
It has been mostly usable to me with binary libraries, precompiled headers, incremental compiling and linking, and now modules.
Although ClearMake object sharing was quite neat experience back in the day.
https://github.com/mozilla/sccache
> While we're at it, we also want to convert it to use recursive descent; it was originally written using parser combinators because that was what I was comfortable with at the time
Aren't parser combinators just one way to do recursive descent?
Explicitly hand-writing the parsing logic allows you to get more specific in error states and messages that a combinator can't.
It will be interesting to compare compile times afterwards. I'm also interested if there's any Rust features that are 'taken for granted' which will make the Zig version less pleasant to read or write. Hopefully the old Rust repo is archived so I can reference it a year from now!
It will be impossible to ever truly do a one-to-one comparison. First, there may be a ton of code in the existing implementation that gets cut out by a mass rewrite due to better, more informed, architecture decisions. On the other hand, it may be possible that new features find their way into the rewrite that cause the fully Zig implementation to be larger than the original Rust-Zig implementation. I don't think any comparison will be fully "fair". A smaller implementation could be argued as only being possible in Rust or Zig because of its advantages over the other language. I expect results to be controversial.
I am also curios about at least some benchmark even if it is not one-to-one perfect comparison. There should be at least one case-study where they have shorter feedback loop with Zig than with Rust. It would be interesting to see that.
Yeah, the rewrite will clean up a ton of technical debt for us. So it will very much be a bad comparison.
Whenever I see <language A> rewrites its compiler in <language B>, and A != B, I lose a bit of interest in language A.
Why? Roc has good reasons for not self hosting the compiler.
Well their main reason is a compiler written in Roc won't be performant. (And their tag line is "Roc: A _fast_, friendly, functional language.")
So yeah, I am not interested. Plus I feel in general not dogfooding your own language is a bad sign.
1 reply →
Maybe I need to give it another shot, but Zig was a terribly unpleasant language last I tried to use it.
Their motivation seems to justify the decision, but I'm pretty sure this will scare away potential contributors. I've been following Roc personally, and probably will continue to do so, but this definitely has killed a lot of interest for me.
After watching YT talks on Roc using Rust and Zig, I always wondered they didn't just opt for Zig only. Interesting to see this.
Yeah, the compiler was started in 2019 when zig wasn't nearly as viable of an option. On top of that, you don't necessarily know what you want until you build something.
I'm part of the core team, and I'm honestly still a bit surprised we're switching to zig. I think it makes sense, it just kinda finally aligned and was decided now. I guess enough technical debt piled up and the core of the language was clearly discovered. So we really know what we want now.
A very good discussion and hard to disagree with conclusions. Also, educational.
Richard Feldman is an amazing educator. He got me into Elm back in the days.
I am a bit sad that Roc is following similar pitfalls as Elm in its quest to be simple by rejecting to have more advanced type system features. That just does not work. In dynamic languages you can opt for minimalism because everything goes by default but in fully statically typed languages things can painful quickly. Even golang had to add generics.
Still many amazing ideas in the language. The platform concept is pretty neat. Not doing implicit currying is a good move.
But Roc already has generics, Go has managed to become very successful despite having a simpler type system than Roc.
What particular type system featured do you miss? I think Elm proved that a restricted type system DOES WORK for large amounts of software. It's been a few years since I've written Elm, so I don't recall specific painful memories I've had with the type system.
One thing that makes Go's restrictive type system more bearable is a fantastic stlib package for analyzing and generating Go code. I wonder if Roc will get anything similar.
If you're talking about typeclasses, Roc calls them "abilities" https://www.roc-lang.org/abilities
What kind of advanced type system features?
I’ve been reading a lot about comptime in Zig and it’s really cool, it unifies and simplifies a lot of metaprogramming concepts that normally end up being the ugliest and clunkiest parts of most languages.
I’ve been immersed in Swift for a couple years after working as a Go programmer, and I find myself pining for a language that’s more expressive than the latter and more concise than the former.
If author is reading this, maybe worth a paragraph saying why you're not rewriting in Roc.
Like this one?
> Roc's compiler has always been written in Rust, and we do not plan to self-host. We have a FAQ entry explaining why, and none of our reasoning has changed on that subject.
https://www.roc-lang.org/faq.html#self-hosted-compiler
because that's hard. also it's one choice I respect them for not making: don't self host until you have actual users and momentum (unless you want to to prove a point). if Roc intends to have industry usage, keeping it in another language for now is a good move.
Roc never plans to self host. We want roc the compiler in the long term to be as nice as possible for end users. A big part of that is as fast as possible. While roc can be fast, it can't compete with all the techniques that can be done in rust/zig/c/c++. It fundamentally is a higher level language to increase developer joy and reduce bugs. So it isn't the right tool when you are trying to reach the limits of hardware like this.
Yay for Zig! I'm a big Zig fan. It's seemless interop with C makes it super fun. I really enjoy writing code in a C-style way. Its still got some rough edges, but its definitely super fun and enjoyable to write imo.
It is interesting to note an intentional choice to go from a "safe" language to an "unsafe" language for a program whose use is not without security implications (even if it's not network-facing).
What matters is that the compiled program is safe. Who cares if the compiler leaks memory? It's only going to run for a bit and then shut down, the OS will take care of it.
Zig seems to be a good choice here.
> Rust's compile times are slow. Zig's compile times are fast. This is not the only reason, but it's a big reason.
So why not something like OCaml that has fast compile times and a lot of nice features for writing compilers? (pattern matching, etc.)
I guess if you want to never limit yourself in terms of performance, you're kinda stuck with either C/C++/Zig/Rust/etc.
GCed languages can be fast, probably even as fast as any of the aforementioned languages with enough engineering effort, but in terms of high performance, it's actually easier to write high performance code in the aforementioned languages rather than in a GCed language.
1 reply →
The same reason why they're not self-hosting. They want compiler performance to be in the top C, C++, Rust, Zig tier. OCaml isn't slow, but at best it's in the second Java, C#, etc. tier.
Zig's union enums get you something kind of like pattern matching. https://zig.guide/language-basics/unions/
Because, like he said, compile times matter.
This includes the Roc compiler too. Zig is significantly faster than OCaml.
3 replies →
I'm not educated enough to give an opinion but I find it weird to rewrite in a language that is so new
Not only is it new, it has yet to reach version 1 as a language with any guaranteed level of stability. In every version release there's a disclaimer that seems to discourage its use in production software.
> Zig has known bugs and even some miscompilations.
> Zig is immature. Even with Zig 0.13.0, working on a non-trivial project using Zig will likely require participating in the development process.
But then again, it's a great thing for the language to be used in larger public projects (like tigerbeetle, bun, ghostty). This will drive its development and adoption.
Fast compile times in Zig? Last time I tried it was super slow. Has anything changed in recent years?
Last time I tried it was missing both speed and incremental compilation. That still looks to be the case due to no linker support.
https://github.com/ziglang/zig/issues/21165
Its honestly pretty dang good now. I can bootstrap Zig from C in about 3-5 minutes, and then use the self-hosted stage3 compiler to compile Zig very quickly (under 1 minute iirc) Once the cache kicks in, its even faster. I guess my overall gauge on this is the impression I get. Zig build times never annoy me and I never think about it... especially compared to clang, gcc and Rust.
Compared to Rust its blazingly fast. Compared to Go compilation its slower.
Non it’s not. I known its not a benchmark but just try a zig init and build their hello world. It takes significantly longer
3 replies →
Their x86 backend seems to be more complete, bypassing LLVM and allowing for fast debug builds.
Rust has a Cranelift-based backend that you can use for that.
1 reply →
The ROC webpage talks about ROC doing web servers via Rust libs...
Will these be replaced with Zig networking libs?
Not necessarily! Each Roc app runs on a particular platform which is built in a host language for a specific domain. That host language could be Rust, Zig, C, C++, Go, Swift, etc. It's possible the basic-webserver platform will be rewritten to Zig but it doesn't need to be.
I wonder if it would make sense for Ruby YJIT as well. Which is also written in Rust and started around the same time as Roc in 2019.
I think Go would have been a better choice.
absolutely not. both because it's awful for implementing parsers, but also it introduces lots of extra hoops for interop.
its build system is also terrible for targeting lots of varying platforms and feature sets.
it has a custom IR that isn't helpful interacting with other systems.
its runtime is really nice but it's a black box and not suitable for interop.