Rue: Higher level than Rust, lower level than Go(rue-lang.dev)
169 points byingve16 hours ago |19 comments
killingtime7414 hours ago
I always thought of Go as low level and Rust as high level. Go has a lot of verbosity as a "better C" with GC. Rust has low level control but many functional inspired abstractions. Just try writing iteration or error handling in either one to see.
gpm13 hours ago
I wonder if it's useful to think of this as go is low type-system-complexity and rust is high type-system-complexity. Where type system complexity entails a tradeoff between the complexity of the language and how powerful the language is in allowing you to define abstractions.

As an independent axis from close to the underlying machine/far away from the underlying machine (whether virtual like wasm or real like a systemv x86_64 abi), which describes how closely the language lets you interact with the environment it runs in/how much it abstracts that environment away in order to provide abstractions.

Rust lives in high type system complexity and close to the underlying machine environment. Go is low type system complexity and (relative to rust) far from the underlying machine.

steveklabnik12 hours ago
I think this is insightful! I'm going to ponder it, thank you. I think it may gesture towards what I'm trying to get at.
josephg13 hours ago
Yep. This was the biggest thing that turned me off Go. I ported the same little program (some text based operational transform code) to a bunch of languages - JS (+ typescript), C, rust, Go, python, etc. Then compared the experience. How were they to use? How long did the programs end up being? How fast did they run?

I did C and typescript first. At the time, my C implementation ran about 20x faster than typescript. But the typescript code was only 2/3rds as many lines and much easier to code up. (JS & TS have gotten much faster since then thanks to improvements in V8).

Rust was the best of all worlds - the code was small, simple and easy to code up like typescript. And it ran just as fast as C. Go was the worst - it was annoying to program (due to a lack of enums). It was horribly verbose. And it still ran slower than rust and C at runtime.

I understand why Go exists. But I can't think of any reason I'd ever use it.

wswin12 hours ago
Rust gets harder with codebase size, because of borrow checker. Not to mention most of the communication libraries decided to be async only, which adds another layer of complexity.
gpm12 hours ago
I strongly disagree with this take. The borrow checker, and rust in general, keeps reasoning extremely local. It's one of the languages where I've found that difficulty grows the least with codebase size, not the most.

The borrow checker does make some tasks more complex, without a doubt, because it makes it difficult to express something that might be natural in other languages (things including self referential data structures, for instance). But the extra complexity is generally well scoped to one small component that runs into a constraint, not to the project at large. You work around the constraint locally, and you end up with a public (to the component) API which is as well defined and as clean (and often better defined and cleaner because rust forces you to do so).

jrjrjfhgggg10 hours ago
Disagree, having dealt with +40k LoC rust projects, bottow checker is not an issue.

Async is an irritation but not the end of the world ... You can write non asynchronous code I have done it ... Honestly I am coming around on async after years of not liking it... I wish we didn't have function colouring but yeah ... Here we are....

jesse__9 hours ago
We all know that lines of code is a poor measure of project size, but that said, 40k sloc is not a lot
raincole1 hour ago
Rust indeed gets harder with codebase size, just like other languages. But claiming it is because of borrow checker is laughable at best. Borrow checker is what keeps it reasonable because it limits the scope of how one memory allocation can affect the rest of your code.

If anything, borrow checker makes writing functions harder but combining them easier.

josephg11 hours ago
This hasn't been my experience at all.

I still regularly use typescript. One problem I run into from time to time is "spooky action at a distance". For example, its quite common to create some object and store references to it in multiple places. After all, the object won't be changed and its often more efficient this way. But later, a design change results in me casually mutating that object, forgetting that its being shared between multiple components. Oops! Now the other part of my code has become invalid in some way. Bugs like this are very annoying to track down.

Its more or less impossible to make this mistake in rust because of how mutability is enforced. The mutability rules are sometimes annoying in the small, but in the large they tend to make your code much easier to reason about.

C has multiple problems like this. I've worked in plenty of codebases which had obscure race conditions due to how we were using threading. Safe rust makes most of these bugs impossible to write in the first place. But the other thing I - and others - run into all the time in C is code that isn't clear about ownership and lifetimes. If your API gives me a reference to some object, how long is that pointer valid for? Even if I now own the object and I'm responsible for freeing it, its common in C for the object to contain pointers to some other data. So my pointer might be invalid if I hold onto it too long. How long is too long? Its almost never properly specified in the documentation. In C, hell is other people's code.

Rust usually avoids all of these problems. If I call a function which returns an object of type T, I can safely assume the object lasts forever. It cannot be mutated by any other code (since its mine). And I'm not going to break anything else if I mutate the object myself. These are really nice properties to have when programming at scale.

asa4008 hours ago
I wholeheartedly concur based on my experience with Rust (and other languages) over the last ~7 or so years.

> If I call a function which returns an object of type T, I can safely assume the object lasts forever. It cannot be mutated by any other code (since its mine). And I'm not going to break anything else if I mutate the object myself. These are really nice properties to have when programming at scale.

I rarely see this mentioned in the way that you did, and I'll try to paraphrase it in my own way: Rust restricts what you can do as a programmer. One can say it is "less powerful" than C. In exchange for giving up some power, it gives you more information: who owns an object, what other callers can do with that object, the lifetime of that object in relation to other objects. And critically, in safe Rust, these are _guarantees_, which is the essence of real abstraction.

In large and/or complicated codebases, this kind of information is critical in languages without garbage garbage collection, but even when I program in languages with garbage collection, I find myself wanting this information. Who is seeing this object? What do they know about this object, and when? What can they do with it? How is this ownership flowing through the system?

Most languages have little/no language-level notion of these concepts. Most languages only enforce that types line up nominally (or implement some name-identified interface), or the visibility of identifiers (public/private, i.e. "information hiding" in OO parlance). I feel like Rust is one of the first languages on this path of providing real program dataflow information. I'm confident there will be future languages that will further explore providing the programmer with this kind of information, or at least making it possible to answer these kinds of questions easier.

aw16211078 hours ago
> I rarely see this mentioned in the way that you did, and I'll try to paraphrase it in my own way: Rust restricts what you can do as a programmer. One can say it is "less powerful" than C. In exchange for giving up some power, it gives you more information

Your paraphrasing reminds me a bit of structured vs. unstructured programming (i.e., unrestricted goto). Like to what you said, structured programming is "less powerful" than unrestricted goto, but in return, it's much easier to follow and reason about a program's control flow.

At the risk of simplifying things too much, I think some other things you said make for an interesting way to sum this up - Rust does for "ownership flow"/"dataflow" what structured programming did for control flow.

UltraSane10 hours ago
async seems sensible for anything subject to internet latency.
steveklabnik14 hours ago
Rue author here, yeah I'm not the hugest fan of "low level vs high level" framing myself, because there are multiple valid ways of interpreting it. As you yourself demonstrate!

As some of the larger design decisions come into place, I'll find a better way of describing it. Mostly, I am not really trying to compete with C/C++/Rust on speed, but I'm not going to add a GC either. So I'm somewhere in there.

written-beyond14 hours ago
How very so humble of you to not mention being one of the primary authors behind TRPL book. Steve you're a gem to the world of computing. Always considered you the J. Kenji of the Rust world. Seems like a great project let's see where it goes!
steveklabnik14 hours ago
That is a very kind thing to say, I admire him quite a bit. Thank you!
AdieuToLogic10 hours ago
> Mostly, I am not really trying to compete with C/C++/Rust on speed, but I'm not going to add a GC either. So I'm somewhere in there.

Out of curiosity, how would you compare the goals of Rue with something like D[0] or one of the ML-based languages such as OCaml[1]?

EDIT:

This is a genuine language design question regarding an imperative/OOP or declarative/FP focus and is relevant to understanding the memory management philosophy expressed[2]:

  No garbage collector, no manual memory management. A work 
  in progress, though.

0 - https://dlang.org/

1 - https://ocaml.org/

2 - https://rue-lang.dev/

steveklabnik8 hours ago
Closer to an OCaml than a D, in terms of what I see as an influence. But it's likely to be more imperative/FP than OOP/declarative, even though I know those axes are usually considered to be the way you put them than the way I put them.
killingtime7413 hours ago
Wow didn't realise it was you who was the author. I learnt a lot about Rust from your writings.
steveklabnik12 hours ago
I'm glad to have helped you :)
manaskarekar13 hours ago
Since it's framed as 'in between' Rust and Go, is it trying to target an intersection of both languages' use-cases?
steveklabnik13 hours ago
I don't think you'd want to write an operating system in Rue. I may not include an "unsafe" concept, and will probably require a runtime. So that's some areas where Rust will make more sense.

As for Go... I dunno. Go has a strong vision around concurrency, and I just don't have one yet. We'll see.

ChrisSD2 hours ago
Do you have plans for handling C FFI without "unsafe"? Will it require some sort of extension module written in C/C++/Rust?
steveklabnik1 hour ago
No direct plans. For the immediate future, only the runtime is allowed to call into C.

If this ever becomes a production thing, then I can worry about FFI, and I'll probably just follow what managed languages do here.

chrysoprace11 hours ago
Do you think you'll explore some of the same problem spaces as Rust? Lifetimes and async are both big pain points of Rust for me, so it'd be interesting to see a fresh approach to these problems.

I couldn't see how long-running memory is handled, is it handled similar to Rust?

steveklabnik11 hours ago
I'm going to try and avoid lifetimes entirely. They're great in Rust! But I'm going to a higher level spot.

I'm totally unsure about async.

Right now there's no heap memory at all. I'll get there :) Sorta similar to Rust/Swift/Hylo... we'll see!

lamontcg7 hours ago
So if you don't have a garbage collector, and you don't have manual memory management, and you don't have lifetimes... What do you have?
steveklabnik7 hours ago
The plan is something like mutable value semantics and linear types. I'm figuring it out :)
ksec10 hours ago
Is this a simplified / distilled version of Rust ? Or Subset of Rust with some changes ?
rob742 hours ago
Simplified as in easier to use, or simplified as in less language features? I'm all for the former, while the latter is also worth considering (but hard to get right, as all the people who consider Go a "primitive" language show)...
steveklabnik8 hours ago
Some of it is like that, but some of it is going to be from other stuff too. I'm figuring it out :)
a963 hours ago
Since that seems to be the (frankly bs) slogan that almost entirely makes up the languages lading page, I expect it's really going to hurt the language and/or make it all about useless posturing.

That said, I'm an embedded dev, so the "level" idea is very tangible. And Rust is also very exciting for that reason and Rue might be as well. I should have a look, though it might not be on the way to be targeting bare metal soon. :)

steveklabnik1 hour ago
I don't mind if a sentence I threw up for a side project "hurts the language" at this stage, this is a project primarily for me.

You should use Rust for embedded, I doubt Rue will ever be good for it.

pjmlp2 hours ago
All are high level as long as they don't expose CPU capabilities, even ISO C is high level, unless we count in language extensions that are compiler specific, and any language can have compiler extensions.
asim4 hours ago
Agree with Go being basically C with string support and garbage collection. Which makes it a good language. I think rust feels more like a c++ replacement. Especially syntactically. But each person will say something different. If people can create new languages and there's a need then they will. Not to say it's a good or bad thing but eventually it would be good to level up properly. Maybe AI does that.
batisteo14 hours ago
C was designed as a high level language and stayed so for decades
AdieuToLogic9 hours ago
> C was designed as a high level language and stayed so for decades

C was designed as a "high level language" relative to the assembly languages available at the time and effectively became a portable version of same in short order. This is quite different to other "high level languages" at the time, such as FORTRAN, COBOL, LISP, etc.

andsoitis16 hours ago
> Memory Safe

> No garbage collector, no manual memory management. A work in progress, though.

I couldn't find an explanation in the docs or elsewhere how Rue approaches this.

If not GC, is it via:

a) ARC

b) Ownership (ala Rust)

c) some other way?

steveklabnik14 hours ago
I am playing around with this! I'm mostly interested in something in the space of linear types + mutable value semantics.
onlyrealcuzzo1 hour ago
Also working on a language / runtime in this space.

It transpiles to Zig, so you have native access to the entire C library.

It uses affine types (simple ownership -> transfers via GIVE/TAKES), MVCC & transactions to safely and scalably handle mutations (like databases, but it scales linearly after 32 cores, Arc and RwLock fall apart due to Cache Line Bouncing).

It limits concurrent complexity only to the spot in your code WHERE you want to mutate shared memory concurrently, not your entire codebase.

It's memory and liveness safe (Rust is only memory safe) without a garbage collector.

It's simpler than Go, too, IMO - and more predictable, no GC.

But it's nearly impossible to beat Go at its own game, and it's not zero overhead like Rust - so I'm pessimistic it's in a "sweet spot" that no one will be interested in.

Time will tell.

Imustaskforhelp1 minute ago
can you share the link, sounds fascinating language to follow its development as well and good luck on this project!
steveklabnik1 hour ago
Neat! Good luck, that sounds very cool. I have no idea what if anything I'm going to do about liveliness.
torginus3 hours ago
Could you please explain what this implies in layman's terms? I've read the definition of 'linear type' as a type that must be used exactly once, and by 'mutable value semantics', I assume, that unlike Rust, multiple mutable borrows are allowed?

What's the practical implication of this - how does a Rue program differ from a Rust program? Does your method accept more valid programs than the borrow checker does?

sureglymop1 hour ago
Have you explored the ideas explored for the Vale language: https://vale.dev/

May be an interesting approach. That language seems very academic and slow moving at the moment though.

steveklabnik1 hour ago
I think Vale is interesting, but yeah, they have had some setbacks, in my understanding more to do with the personal lives of the author rather than the ideas. I need to spend more time with it.
jasonwatkinspdx12 hours ago
You might find one of my late brother's research interests relevant: https://www.cs.princeton.edu/~dpw/papers/space.pdf
steveklabnik12 hours ago
Thank you for the link! I'll check it out for sure.

(And sorry to hear about your brother's passing.)

jasonwatkinspdx8 hours ago
Yeah, that's just one of the essays he was on as a phd student, but he was really interested in the interaction of linear types and region inferencing as a general resource management framework. That grew into an interest in linear types as part of logical frameworks for modeling concurrency. But then like a lot of people he became disillusioned with academia, went to make some money on wall street, then focused on his family after that.

Anyhow, I just thought it might be a good jumping off point for what you're exploring.

echelon14 hours ago
Nice! I see you're one of (if not the primary) contributor!

Do you see this as a prototype language, or as something that might evolve into something production grade? What space do you see it fitting into, if so?

You've been such a huge presence in the Rust space. What lessons do you think Rue will take, and where will it depart?

I see compile times as a feature - that's certainly nice to see.

steveklabnik14 hours ago
This is a project between me and Claude, so yeah :)

It's a fun project for me right now. I want to just explore compiler writing. I'm not 100% sure where it will lead, and if anyone will care or not where it ends up. But it's primarily for me.

I've described it as "higher than Rust, lower than Go" because I don't want this to be a GC'd language, but I want to focus on ergonomics and compile times. A lot of Rust's design is about being competitive with C and C++, I think by giving up that ultra-performance oriented space, I can make a language that's significantly simpler, but still plenty fast and nice to use.

We'll see.

echelon12 hours ago
Love it! I think that's a nice target.

Have fun! :)

oulipo214 hours ago
So linear type + mutable value would be quite close to Rust, right?
steveklabnik14 hours ago
Rust has affine types, not linear. It also doesn't have mutable value semantics, it uses references, lifetimes, and borrowing.
EnPissant13 hours ago
I've never seen any significant difference in linear vs affine types.

To me it just seems like Rust has Linear types, and the compiler just inserts some code to destroy your values for you if you don't do it yourself.

I guess the only difference is that linear types can _force_ you to manually consume a value (not necessarily via drop)? Is that what you are going for?

steveklabnik13 hours ago
Affine types are "may use" and linear types are "must use," yeah. That is, linear types are stronger.

See https://faultlore.com/blah/linear-rust/ for a (now pretty old but still pretty relevant, I think) exploration into what linear types would mean for Rust.

freakynit7 hours ago
Check out V-lang ... it has the details. It's a beautiful language... but, mostly unknown.
aw16211076 hours ago
> Check out V-lang ... it has the details.

Does it? From its docs [0]:

> There are 4 ways to manage memory in V.

> The default is a minimal and a well performing tracing GC.

> The second way is autofree, it can be enabled with -autofree. It takes care of most objects (~90-100%): the compiler inserts necessary free calls automatically during compilation. Remaining small percentage of objects is freed via GC. The developer doesn't need to change anything in their code. "It just works", like in Python, Go, or Java, except there's no heavy GC tracing everything or expensive RC for each object.

> For developers willing to have more low-level control, memory can be managed manually with -gc none.

> Arena allocation is available via a -prealloc flag. Note: currently this mode is only suitable to speed up short lived, single-threaded, batch-like programs (like compilers).

So you have 1) a GC, 2) a GC with escape analysis (WIP), 3) manual memory management, or 4) ...Not sure? Wasn't able to easily find examples of how to use it. There's what appears to be its implementation [1], but since I'm not particularly familiar with V I don't feel particularly comfortable drawing conclusions from a brief glance through it.

In any case, none of those stand out as "memory safety without GC" to me.

[0]: https://docs.vlang.io/memory-management.html

[1]: https://github.com/vlang/v/blob/master/vlib/builtin/prealloc...

freakynit1 hour ago
"none of those stand out as "memory safety without GC" to me" ... can you explain why you believe they are not memory safe without GC? Im more interested to know the points in relation to autofree.

Regarding the details, here is a pretty informative github discussion thread on same topic: https://github.com/vlang/v/discussions/17419

It is also accompanied with a demo video (pretty convincing in case you would like to watch).

V-lang is not shiny as other languages are, but, it does have a lot to learn from.

maleldil7 hours ago
Oh, it's known. It just has an incredibly negative reputation on this site.
freakynit2 hours ago
I kinda expected.. just hesitated to point it out.
est319 hours ago
I have mostly been writing Rust in the last 10 years, but recently (1 year) I have been writing Go as well as Rust.

The typical Go story is to use a bunch of auto generation, so a small change quickly blows up as all of the auto generate code is checked into git. Like easily a 20x blowup.

Rust on the other hand probably does much more such code generation (build.rs for stuff like bindgen, macros for stuff like serde, and monomorphized generics for basically everything). But all of this code is never checked into git (with the exception of some build.rs tools which can be configured to run as commands as well), or at least 99% of the time it's not.

This difference has impact on the developer story. In go land, you need to manually invoke the auto generator and it's easy to forget until CI reminds you. The auto generator is usually quite slow, and probably has much less caching smartness than the Rust people have figured out.

In Rust land, the auto generation can, worst case, run at every build, best case the many cache systems take care of it (cargo level, rustc level). But still, everyone who does a git pull has to re-run this, while with the auto generation one can theoretically only have the folks run it who actually made changes that changed the auto generated code, everyone else gets it via git pull.

So in Go, your IDE is ready to go immediately after git pull and doesn't have to compile a tree of hundreds of dependencies. Go IDEs and compilers are so fast, it's almost like cheating from Rust POV. Rust IDEs are not as fast at all even if everything is cached, and in the worst case you have to wait a long long time.

On the other hand, these auto generation tools in Go are only somewhat standardized, you don't have a central tool that takes care of things (or at least I'm not aware of it). In Rust land, cargo creates some level of standardization.

You can always look at the auto generated Go code and understand it, while Rust's auto generated code usually is not IDE inspectable and needs special tools for access (except for the build.rs generated stuff which is usually put inside the target directory).

I wonder how a language that is designed from scratch would approach auto generation.

mxey3 hours ago
> On the other hand, these auto generation tools in Go are only somewhat standardized, you don't have a central tool that takes care of things (or at least I'm not aware of it).

https://pkg.go.dev/cmd/go#hdr-Generate_Go_files_by_processin...

beautron4 hours ago
> The typical Go story is to use a bunch of auto generation, so a small change quickly blows up as all of the auto generate code is checked into git. Like easily a 20x blowup.

Why do you think the typical Go story is to use a bunch of auto generation? This does not match my experience with the language at all. Most Go projects I've worked on, or looked at, have used little or no code generation.

I'm sure there are projects out there with a "bunch" of it, but I don't think they are "typical".

tgv2 hours ago
Me neither. My go code doesn't have any auto-generation. IMO it should be used sparingly, in cases where you need a practically different language for expressivity and correctness, such as a parser-generator.
steveklabnik8 hours ago
Yeah, this is a hard problem, and you're right that both have upsides and downsides. Metaprogramming isn't easy!

I know I don't want to have macros if I can avoid them, but I also don't forsee making code generation a-la-Go a first class thing. I'll figure it out.

Kinrany9 hours ago
FYI rust-analyzer can show expanded macros. It's not perfect because you only get syntax highlighting, but it works.
ryanobjc8 hours ago
The "just generate go code automatically then check it in" is a massive miswart from the language, and makes perfect sense because that pathological pattern is central to how google3 works.

A ton of google3 is generated, like output from javascript compilers, protobuf serialization/deserialization code, python/C++ wrappers, etc.

So its an established Google standard, which has tons of help from their CI/CD systems.

For everyone else, keeping checked-in auto-generated code is a continuous toil and maintenance burden. The Google go developers don't see it that way of course, because they are biased due to their google3 experience. Ditto monorepos. Ditto centralized package authorities for even private modules (my least fave feature of Go).

mxey3 hours ago
> For everyone else, keeping checked-in auto-generated code is a continuous toil and maintenance burden. The Google go developers don't see it that way of course, because they are biased due to their google3 experience.

The golang/go repo itself has various checked-in generated repo

chrysoprace11 hours ago
It may have been more useful to link to the blog post [0] which gives more of an introduction than the front page at this point.

[0] https://rue-lang.dev/blog/hello-world/

steveklabnik11 hours ago
I posted that, and also https://steveklabnik.com/writing/thirteen-years-of-rust-and-...

Just to link them all together. This is the one that the algorithm picked up :)

pjmlp2 hours ago
If this language is supposed to be used for systems programming, doing a factorial isn't really a selling example of why Rue.
steveklabnik1 hour ago
For sure. It's just such early days I don't have a lot of stuff that's useful yet. I'll get there.
jameskilton14 hours ago
Probably best to link to the repo itself, this is not meant to be used yet. https://github.com/rue-language/rue
lifis13 hours ago
All the Rue code in the manual seems to also be valid Rust code, except for the @-prefixed intrinsics
steveklabnik13 hours ago
Yes, I started off with the idea that Rue's syntax would be a strict subset of Rust's.

I may eventually diverge from this, but I like Rust's syntax overall, and I don't want to bikeshed syntax right now, I want to work on semantics + compiler internals. The core syntax of Rust is good enough right now.

scuff3d10 hours ago
Out of interest, what's the motivation? What are you hoping to do with Rue that Rust doesn't currently provide?
steveklabnik8 hours ago
Primary motivation is to have a fun project. If nobody ever uses this, I'll still be happy.

I'd like fast compile times, and giving up some of Rust's lowest level and highest performance goals in exchange for it. As well as maybe ease of use.

scuff3d6 hours ago
Nice, seems like a super cool project.

I've thought a Rust like language but at Go's performance level would be interesting. Garbage collected, but compiled to a binary (no VM), but with Rust's mix of procedural and functional programming. Maybe some more capable type inference.

If you don't mind me asking, how did you get started with programming language design? I've been reading Crafting Interpreters, but there is clearly a lot of theory that is being left out there.

emerent12 hours ago
How is it a subset then if it has the @-prefix? Wait, does Rust's grammar still have the @ and ~ sigils from the pre 1.0 times for pointers?
steveklabnik12 hours ago
It started off that way, but didn't (and won't) remain that way.

I'm using @ for intrinsics because that's how Zig does it and I like it for similar reasons to how Rust uses ! for macros.

Panzerschrek6 hours ago
I am surprised that a language with nothing than a couple of promises gets so much attention. Why exactly?
steveklabnik1 hour ago
I've been a member of this community for a long time.

People also like hearing about new languages.

I agree that it's not really ready for this much attention just yet, but that's the way of the world. We'll see how it goes.

squirrellous1 hour ago
IIRC the author has a good track record with programming languages.
homarp2 hours ago
In the intro text, the Ramsus is who ? a typo about php creator or a more obscure language creator?
steveklabnik1 hour ago
It's a typo, I'm referring to https://en.wikipedia.org/wiki/Rasmus_Lerdorf. I'll fix it, thank you :)
waldrews10 hours ago
What the world needs is a more expressive language than Go, that interops with Go's compilation model and libraries.
rubenvanwyk7 hours ago
Something like Borgo https://borgo-lang.github.io/
jandy5 hours ago
Sadly, seems to be abandoned. Last commit a year ago.
coffeeaddict113 hours ago
How does this differ from Hylo [0]?

[0] https://hylo-lang.org

steveklabnik13 hours ago
I am very interested in Hylo! I think they're playing in similar spaces. I'd like to explore mutable value semantics for Rue.

One huge difference is that Hylo is using LLVM, whereas I'm implementing my own backends. Another is that Hylo seems to know what they want to do with concurrency, whereas I really do not at all right now.

I think Hylo takes a lot of inspiration from Swift, whereas I take more inspiration from Rust. Swift and Rust are already very similar. So maybe Hylo and Rue will end up like this: sister languages. Or maybe they'll end up differently. I'm not sure! I'm just playing around right now.

Panzerschrek6 hours ago
How does it achieve memory safety?
steveklabnik1 hour ago
Right now? By not even having heap allocation (though I'll be sending in the first PR for that soon.)

Eventually: through not having references, thanks to mutable value semantics. Also linear types.

But that's just ideas right now. It'll get there.

misir9 hours ago
Any plans for adding algebraic data types (aka rust enums)?
steveklabnik8 hours ago
I landed non-generic enums this evening. I'm not 100% sure what abstraction paths I want to go down. But I see sum types as just as important as product types, for sure.
norir14 hours ago
I wince every time I see naive recursive fibonacci as a code example. It is a major turnoff because it hints at a lack of experience with tail call optimization, which I consider a must have for a serious language.
stouset14 hours ago
Would someone please explain to me why TCO—seemingly alone amongst the gajillions of optimization passes performed by modern compilers—is so singularly important to some people?
oersted14 hours ago
For people that like functional style and using recursion for everything, TCO is a must. Otherwise there’s no way around imperative loops if you want decent performance and not having to worry about the stack limit.

Perhaps calling it an “optimization” is misleading. Certainly it makes code faster, but more importantly it’s syntax sugar to translate recursion into loops.

int_19h3 hours ago
You don't need full fledged TCO for that; see Clojure's recur for an example. Zig recently added something similar but strongly typed with match/continue. These all map exactly to a closed set of mutually recursive functions with a single entry point, which is quite sufficient (and then some) to fully replace iterative loops while still desugaring to the same exact code.
oersted1 hour ago
Indeed there are more explicit versions of such mechanisms, which I prefer, otherwise there’s always a bit of paranoia about recursion without assurance that the compiler will handle it properly.
Rusky14 hours ago
TCO is less of an optimization (which are typically best-effort on the part of the compiler) and more of an actual semantic change that expands the set of valid programs. It's like a new control flow construct that lives alongside `while` loops.
zephen9 hours ago
It virtue-signals that they're part of the hip functional crowd.

(To be fair, if you are programming functionally, it is essential. But to flat-out state that a language that doesn't support isn't "serious" is a bit rude, at best.)

mrkeen2 hours ago
Supporting recursion only to a depth of 1000 (or whatever) is equivalent to supporting loops of up to 1000 iterations.

If I put out a language that crashed after 1000 iterations of a loop, I'd welcome the rudeness.

Ar-Curunir1 hour ago
Plenty of languages, including very serious ones like C and Rust, have bounded recursion depth.
mrkeen6 minutes ago
Then let me rephrase:

If every iteration of a while-loop cost you a whole stack frame, then I'd be very rude about that language.

This works, btw:

  #include <stdio.h>

  long calc_sum(int n, long acc) {
    return n == 0
      ? acc
      : calc_sum(n-1, acc+n);
  }

  int main(void) {
    int iters = 2000000;
    printf("Sum 1...%d = %ld\n", iters, calc_sum(iters, 0));
    return 0;
  }
aaronblohowiak14 hours ago
functional programming background / SICP ?
steveklabnik14 hours ago
I only have basic constant folding yet in terms of optimizations, but I'm very aware of TCO. I haven't decided if I want to require an annotation to guarantee it like Rust is going to.
int_19h3 hours ago
Please require some form of annotation like an explicit `tailcall` operator or something similar. TCO wrecks havoc on actionable backtraces, so it should be opt-in rather than opt-out.
steveklabnik1 hour ago
I am very sympathetic to this, for sure.
dullcrisp12 hours ago
Plus we all know that fibs = 1 : 1 : zipWith (+) fibs (tail fibs) is the only serious Fibonacci implementation.
xpe9 hours ago
Who thunk of that one?
airstrike13 hours ago
"Well you can judge the whole world on the sparkle that you think it lacks.

Yes, you can stare into the abyss, but it's staring right back"

mrkeen2 hours ago
Please, it supports a hole at best. Maybe a pit. No way will this let you construct an abyss.
reactordev11 hours ago
I write a lot of go. I tried to write a lot of rust but fell into lifetime traps. I really want to leave C++ but I just can’t without something that’s also object oriented.

Not a dig at functional, it’s just my big codebases are logically defined as objects and systems that don’t lend itself to just being a struct or an interface.

Inheritance is why I’m stuck in C++ land.

I would love to have something like rust but that supports classes, virtual methods, etc. but I guess I’ll keep waiting.

scuff3d10 hours ago
In Rust you can have structs with any number of methods defined on them, which is functionally not that different from a class. You get interface like behavior with traitsz and you get encapsulation with private/public data and methods.

Does inheritance really matter that much?

reactordev9 hours ago
Yes it does. Unless I can attach a trait to a struct without having to define all the methods of that trait for that struct. This is my issue with interfaces and go. I can totally separate out objects as interfaces but then I have to implement each implementation’s interface methods and it’s a serious chore when they’re always the same.

For example: Playable could be a trait that plays a sound when you interact with it. I would need to implement func interact for each object. Piano, jukebox, doorbell, etc. With inheritance, I write it once, add it to my class, and now all instances of that object have interact. Can I add instance variables to a trait?

This saves me time and keeps Claude out of my code. Otherwise I ask Claude to implement them all, modify them all, to try to keep them all logically the same.

I also don’t want to go type soup in order to abstract this into something workable.

scuff3d8 hours ago
You can provide default method implementations for traits. Any type with that trait gets the default behavior, unless you override it.
steveklabnik11 hours ago
I respect your preferences, but I am unlikely to add this sort of OOP. Ideally there'll be no subtyping at all in Rue. So you'll have to keep waiting, I'm afraid. Thanks for checking it out regardless!
amluto11 hours ago
As a long time C++ user, I’m curious why you like inheritance and virtual methods so much.

I maintain a medium sized, old-ish C++ code base. It uses classes and inheritance and virtual methods and even some multiple inheritance. I despise this stuff. Single Inheritance is great until you discover that you have a thing that doesn’t slot nicely into the hierarchy or when you realize that you want to decompose an interface (cough, base class) into a couple of non-hierarchically related things. Multiple inheritance is an absolute mess unless you strictly use base classes with pure virtual methods and no member variables. And forcing everything into an “is a” relationship instead of a “has a” relationship can be messy sometimes.

I often wish C++ had traits / or Haskell style type classes.

xpe9 hours ago
Ah, yes, multiple inheritance in C++: where order matters but sanity does not
oulipo214 hours ago
Interesting, for me the "between Rust and Go" would be a nice fit for Swift or Zig. I've always quite liked the language design of Swift, it's bad that it didn't really take off that much
isodev0 minutes ago
I think with Swift 6 Apple really took it in a wrong direction. Even coding agents can’t wrap their mind around some of the “safety” features (not to mention the now bloated syntax). If anything, Swift would go down as a “good example why language design shouldn’t happen by committee in yearly iterations”.
steveklabnik14 hours ago
One thing working on this project has already done is give me more appreciation for a lot of Zig's design.

Zig really aims to be great at things I don't imagine Rue being useful for, though. But there's lots of good stuff there.

And lots of respect to Swift as well, it and Hylo are also major inspiration for me here.

vips7L12 hours ago
Checkout Borgo: https://github.com/borgo-lang/borgo

I also find that D is good between language. You can do high level or low level whenever you need it.

You can also do some inbetween systems programming in C# if you don’t care about a VM or msft.

behindsight11 hours ago
> You can also do some inbetween systems programming in C# if you don’t care about a VM or msft.

C# Native AOT gets rid of the JIT and gives you a pretty good perf+memory profile compared to the past.

It's mostly the stigma of .NET Framework legacy systems that put people off, but modern C# projects are a breeze.

vips7L11 hours ago
AFAIK there’s still holes like reflection and you have some work, but if that’s changed that’s really good. I suspect it’ll be hard for C# to escape the stench of “enterprise” though.

I’m looking forward to seeing how it shapes out over the next few years. Especially once they release union types.

neonsunset10 hours ago
FWIW JIT is rarely an issue, and enables strong optimizations not available in AOT (it has its own, but JIT is overall much better for throughput). RyuJIT can do the same speculative optimizations OpenJDK Hotspot does except the language has fewer abstractions which are cheaper and access to low-level programming which allows it to have much different performance profile.

NativeAOT's primary goal is reducing memory footprint, binary size, making "run many methods once or rarely" much faster (CLI and GUI applications, serverless functions) and also shipping to targets where JIT is not allowed or undesirable. It can also be used to ship native dynamically or statically (the latter is tricky) linked libraries.

gethly4 hours ago
This is a bit silly but when i look at new languages coming up I always look at the syntax, which is usually horrible(Zig and Rust are good examples), and how much garbage there is. As someone that writes in Go, I can't stand semicolons and other crap that just pollutes the code and wastes time and space to write for absolutely no good reason whatsoever. And as this compares itself with Go, I just cannot but laugh when I see ";", "->" or ":" in the example. At least the semicolon seems optional. But still, it's an instant nope for me.
mrkeen1 hour ago
Weird, that's exactly how I feel reading Go:

  func (lst *List[T]) Push(v T) {
    if lst.tail == nil {
        lst.head = &element[T]{val: v}
        lst.tail = lst.head
    } else {
        lst.tail.next = &element[T]{val: v}
        lst.tail = lst.tail.next
    }
}

And this one doesn't even have the infamous error-checking.

steveklabnik1 hour ago
I respect your preferences! I like punctuation.

Even though I have a Perl tattoo, it'll never get like that, though.

(Semicolon rules, for now at least, will be the same as Rust)

imbnwa4 hours ago
What would be better?
gethly1 hour ago
Remove all of that noise.

Take this:

  fn fib(n: i32) -> i32 {}
The (n: i32) can be just (n i32), because there is no benefit to adding the colon there.

The -> i32 can also be just i32 because, again, the -> serves no purpose in function/method definition syntax.

So you end up with simple and clean fn fib(n i32) i32 {}

And semicolons are an ancient relic that has been passed on to new languages for 80 fucking years without any good reason. We have modern lexers/tokenizers and compilers that can handle if you don't put a stupid ; at the end of every single effing line.

Just go and count how many of these useless characters are in your codebase and imagine how many keystrokes, compilation errors and wasted time it cost you, whilst providing zero value in return.

exceptione56 minutes ago
In a Hindley-Milner (or deriative) type system, types doesn't have to be explicit, making the number of arguments ambiguous here:

  fn fib(n i32) i32 {}
But even if they need to be written explicitly, type applications like `List a` would require syntax to disambiguate them.

Personally, I would like a language that pushes the programmer to write the types as part of a doc comment.

Also think about returning lambda's. Should it look like this?

  fn foo(n i32) (i32 i32) {}
Of course the IDE could help by showing the typographic arrows and other delineations, but as plaintext this is completely unreadable.

  > And semicolons are an ancient relic that has been passed on to new languages for 80 fucking years without any good reason. 
You still have to think about stuff like currying. You either delimit the line, or you use significant white space.
christophilus3 hours ago
I wish more languages would adopt Clojure’s approach to optional delimiters in collections.

[2 45 78]

It’s just a nicer thing to view and type in my experience.

Regarding syntax soup, I think Odin is probably syntactically the cleanest of the lower level languages I’ve played with.

gethly1 hour ago
oh, yeah. that looks good. i always hated using ", " delimiter for lists and the amount of typos it always takes to make clean(well, not with Go fmt).

Odin seems interesting but for me it has two deal-breakers: first one use the use of ^ for pointer de/reference. Not that it does not make sense, it's just that it is not an easy key to get to on my keyboard layout and i will not be changing that. The & and * are well known characters for this purpose and, at least for me, easily accessible on the keyboard. Second issue is the need to download many gigabytes of visual studio nonsense just so i am able to compile a program. Coming from Go, this is just a non-starter. Thirdly, and this is more about the type of work i do than the language, there are/were no db drivers, no http/s stack and other things i would need for my daily work. Other than that, Odin is interesting. Though I am not sure how I would fare without OOP after so many years with inheritance OOP and encapsulated OOP.

int_19h3 hours ago
It's a Lisp thing, obviously, but also there's a benefit to explicit delimiters - it makes it possible to have an expression as an element without wrapping that in its own brackets, as S-exprs require.
frizlab13 hours ago
How does this compare to Swift?
steveklabnik13 hours ago
I don't plan on implementing ARC, I don't think. I do think Swift/Hylo mutable value semantics is a neat idea that I do want to play around with.