That sounds pretty great. My impression is that relatively little code actually runs that often.
but with none of the footguns of manual memory management, no garbage collection pauses, but yet also no evil stepparent style borrow checker to be beaten by.
That part sounds implausible, though. What kind of memory management are they doing?
They pay a lot of attention to preventing cache misses and branch prediction failures, which is how they get away with reference counting and still being fast.
I wish more languages used ref counting. Yes, it has problems with memory cycles, but it’s also predictable and fast. Works really well with immutable data.
Roc uses immutable data by default. It performs opportunistic in-place mutation when the reference count will stay 1 (eg this code would satisfy the borrow checker without cloning or copying if it were rust - static code analysis).
Thanks, this looks really interesting. I’ve thought for a while that Rust’s borrow checker wouldn’t be such a pain in the ass if the APIs were developed with immutable data in mind. It’s not something you can easily slap on, because the whole ecosystem fights against it. Looks like Roc is taking that idea and running with it.
Oh, you just mean it’s a kind of garbage collection that’s lighter on pauses. Sorry, I’ve had the “my pre-Rust pet language already does what Rust does” conversation on here too many times.
Garbage collection is analyzing the heap and figuring out what can be collected. Reference counting requires the code to increment or decrement a counter and frees memory when the counter hits zero. They’re fundamentally different approaches. Also reference counting isn’t necessarily automatic, Objective-C had manual reference counting since day one.
To be fair, the drop/dealloc “pause” is very different from what people usually mean when they say “garbage collection pause”, i.e. stop-the-world (…or at least a slice of the world).
By your definition any automatic memory management is garbage collection, including rust!
Did you think rust doesn’t free up memory for you? That would be the biggest memory leak in history! No! Rust does reference counting, it just makes sure that that number is always one! What did you think the borrow checker was for?
In roc, because the platform is in charge of memory management, it can optimise, so that a web server can allocate an arena for each client, a game loop can calculate what it needs in advance etc etc.
But like I say, they do a lot of work on avoiding cache misses and branch mispredictions, which are their own source of “stop the world while I page in from main memory” or “stop the pipeline while I build a new one”. If it was doing traditional garbage collection, that would be an utterly pointless microoptimisation.
Rust isn’t a religion. Don’t treat it like one.
When it was very new a bunch of C programmers shit on its ideas and said C was the only real systems programming language, but rust, which was pretty much Linear ML dressed up in C style syntax came from hyper weird functional programming language to trusted systems programming language. Why? Because it does memory management sooooo much better than C and is just about as fast. Guess what roc is doing? Memory management soooooo much better than C, and sooooo much less niggly and hard to get right than the borrow checker and is just about as fast.
Plenty of beginners program in rust by just throwing clone at every error the borrow checker sends them, or even unsafe! Bye bye advantages of rust, because it was hard to please. Roc calculates from your code whether it needs to clone (eg once for a reference to an unmodified value, each time for an initial value for the points in a new data structure), and like rust, frees memory when it’s not being used.
Rust does manual cloning. Roc does calculated cloning. Rust wins over C for memory safety by calculating when to free rather than using manual free, totally eliminating a whole class of bugs. Roc could win over rust by calculating when to clone, eliminating a whole class of unnecessary allocation and deallocation. Don’t be so sure that no one could do better than rust. And the devXP in rust is really poor.
Did you think rust doesn’t free up memory for you? That would be the biggest memory leak in history! No! Rust does reference counting, it just makes sure that that number is always one! What did you think the borrow checker was for?
That is not how Rust lifetimes work. There is no runtime garbage collector, like you’re talking about. Given a legal program, it can detect where free-type instructions are needed at compile time, and adds them. From there on it works like C, but with no memory leaks or errors because machines are good at being exactly correct.
Roc has runtime overhead to do garbage collection, it says so right on their own page. It might be a post-Rust language but this is the same conversation I’ve had about D and… I can’t even remember now. Maybe Roc is a cool, innovative language. It’s new to me. But I kind of feel like you’re being a zealot about it.
Roc does static reference counting too, otherwise it wouldn’t be able to do opportunistic in place mutation. It can do static reference counting up to a known compile time bound, whereas rust can only count to one. Both of them can do runtime reference counting, but it’s implicit in roc and explicit with Rc and Arc in rust.
For example, consider the pseudocode { h = "Hello, " hw = h + "world." hm = h + "Mum!" }
In real life, this could be something less swervable.
Roc counts, at compile time, 1,2,3,0, drop. No problem.
Depending on how you declare these variables (with what additional keywords, symbols, string types and concepts), rust counts, at compile time, 1,release,1,2!Nononostopbroken!Badprogrammer! This was in this case an unnecessary premature optimisation. That’s what I mean by rust counts references, but only counts up to 1.
The borrow checker is a static reference counter with an arbitrary number of immutable references that you must declare explicitly and a maximum of one mutable reference that you declare explicitly with mut or let under different circumstances. Arc and Rc are runtime reference counters that you declare explicitly. This is essentially all tracked in the type system.
Roc does the static reference counting and if the total doesn’t rise above rust’s maximum of 1, uses in place mutation (as opposed to the default immutability). If it is bounded it can use static (compile time) reference counting so that when, for example, all four local references fall out of scope, the memory is dropped. If the number is unbounded (eg parameter passing recursion that can’t be tail-cpseudocode ilarly removed), runtime reference counting is used. This is all essentially tracked in the runtime system, but calls to clone are automated in roc. A beginner absolutely can write a memory hog in roc, but the same beginner is likely to overuse clone in rust and write a similar memory hog.
I don’t know whatever that language is doing is called, but it’s not reference counting. It’s doing some kind of static code analysis, and then it falls back to reference counting.
If you call that reference counting, what stops you from calling garbage collectors reference counting too? They certainly count references! Is the stack a reference count too? It keeps track of all the data in a stack frame, some of it might be references!
runtime check. Which in turn results in a runtime performance.
If you’re calling drop on a mutable string that’s been extended repeatedly, you’re recursively dropping all kinds of mess all over the heap. Checking for zero beforehand has an insignificant impact. Those cache misses you had because rust pays less attention to “where” than it does to “whether”, they cost you a lot more than the reference count check. In the real world, in practice, under profiling of real code, the cache misses and the branch misses are more expensive than the reference counting.
You sound a little bit like a C programmer who claims his code is fast because his arrays don’t do bounds checking. That’s not why C is fast. Similarly rust isn’t fast because it never does runtime reference counting. It does sometimes, but that code isn’t pathologically slow.
Also, rust isn’t just fast because of the borrow checker, primarily it’s memory safe because of the borrow checker.
If it’s any consolation, afaik, most of the roc platforms are written in rust. Also afaik only application specific code is written in roc. There are no memory management primitives in roc code unless a platform author exposes them in their api/interface, and I don’t think anyone is working on implementing C on top of roc.
I don’t know what you read on my reply. But your reply makes no sense.
Let me rephrase it if you prefer:
Claiming that Rusty’s borrow checker is reference counting is hugely misleading. Since the borrow checker was made specifically to prevent the runtime cost of garbage collection and reference counting while still being safe.
To anyone unaware, it may read as “rust uses reference counting to avoid reference counting, but they just call it borrow checking”. Which is objectively false, since rust’s solution doesn’t require counting references at runtime.
I don’t know what mutable string or any of the other rant has to do with reference counting. Looks like you’re just looking to catch a “rust evangelist” in some kind of trap. Without even reading what I said.
That sounds pretty great. My impression is that relatively little code actually runs that often.
That part sounds implausible, though. What kind of memory management are they doing?
Reference counting.
I wish more languages used ref counting. Yes, it has problems with memory cycles, but it’s also predictable and fast. Works really well with immutable data.
Roc uses immutable data by default. It performs opportunistic in-place mutation when the reference count will stay 1 (eg this code would satisfy the borrow checker without cloning or copying if it were rust - static code analysis).
Thanks, this looks really interesting. I’ve thought for a while that Rust’s borrow checker wouldn’t be such a pain in the ass if the APIs were developed with immutable data in mind. It’s not something you can easily slap on, because the whole ecosystem fights against it. Looks like Roc is taking that idea and running with it.
Oh, you just mean it’s a kind of garbage collection that’s lighter on pauses. Sorry, I’ve had the “my pre-Rust pet language already does what Rust does” conversation on here too many times.
Garbage collection is analyzing the heap and figuring out what can be collected. Reference counting requires the code to increment or decrement a counter and frees memory when the counter hits zero. They’re fundamentally different approaches. Also reference counting isn’t necessarily automatic, Objective-C had manual reference counting since day one.
It’s still mentioned as one of the main approaches to garbage collection in the garbage collection Wikipedia article.
To be fair, the drop/dealloc “pause” is very different from what people usually mean when they say “garbage collection pause”, i.e. stop-the-world (…or at least a slice of the world).
It’s a post rust language.
By your definition any automatic memory management is garbage collection, including rust!
Did you think rust doesn’t free up memory for you? That would be the biggest memory leak in history! No! Rust does reference counting, it just makes sure that that number is always one! What did you think the borrow checker was for?
In roc, because the platform is in charge of memory management, it can optimise, so that a web server can allocate an arena for each client, a game loop can calculate what it needs in advance etc etc.
But like I say, they do a lot of work on avoiding cache misses and branch mispredictions, which are their own source of “stop the world while I page in from main memory” or “stop the pipeline while I build a new one”. If it was doing traditional garbage collection, that would be an utterly pointless microoptimisation.
Rust isn’t a religion. Don’t treat it like one.
When it was very new a bunch of C programmers shit on its ideas and said C was the only real systems programming language, but rust, which was pretty much Linear ML dressed up in C style syntax came from hyper weird functional programming language to trusted systems programming language. Why? Because it does memory management sooooo much better than C and is just about as fast. Guess what roc is doing? Memory management soooooo much better than C, and sooooo much less niggly and hard to get right than the borrow checker and is just about as fast.
Plenty of beginners program in rust by just throwing clone at every error the borrow checker sends them, or even unsafe! Bye bye advantages of rust, because it was hard to please. Roc calculates from your code whether it needs to clone (eg once for a reference to an unmodified value, each time for an initial value for the points in a new data structure), and like rust, frees memory when it’s not being used.
Rust does manual cloning. Roc does calculated cloning. Rust wins over C for memory safety by calculating when to free rather than using manual free, totally eliminating a whole class of bugs. Roc could win over rust by calculating when to clone, eliminating a whole class of unnecessary allocation and deallocation. Don’t be so sure that no one could do better than rust. And the devXP in rust is really poor.
That is not how Rust lifetimes work. There is no runtime garbage collector, like you’re talking about. Given a legal program, it can detect where free-type instructions are needed at compile time, and adds them. From there on it works like C, but with no memory leaks or errors because machines are good at being exactly correct.
Roc has runtime overhead to do garbage collection, it says so right on their own page. It might be a post-Rust language but this is the same conversation I’ve had about D and… I can’t even remember now. Maybe Roc is a cool, innovative language. It’s new to me. But I kind of feel like you’re being a zealot about it.
There is no reference counting if the count is always one.
The defining feature of reference counting is that its a runtime check. Which in turn results in a runtime performance.
If there is no in memory counter at runtime, nobody calls that reference counting.
It’s not as simple as that.
Roc does static reference counting too, otherwise it wouldn’t be able to do opportunistic in place mutation. It can do static reference counting up to a known compile time bound, whereas rust can only count to one. Both of them can do runtime reference counting, but it’s implicit in roc and explicit with Rc and Arc in rust.
For example, consider the pseudocode
{
h = "Hello, "
hw = h + "world."
hm = h + "Mum!"
}
In real life, this could be something less swervable.
Roc counts, at compile time,
1,2,3,0, drop
. No problem.Depending on how you declare these variables (with what additional keywords, symbols, string types and concepts), rust counts, at compile time,
1,release,1,2! No no no stop broken! Bad programmer!
This was in this case an unnecessary premature optimisation. That’s what I mean by rust counts references, but only counts up to 1.The borrow checker is a static reference counter with an arbitrary number of immutable references that you must declare explicitly and a maximum of one mutable reference that you declare explicitly with mut or let under different circumstances. Arc and Rc are runtime reference counters that you declare explicitly. This is essentially all tracked in the type system.
Roc does the static reference counting and if the total doesn’t rise above rust’s maximum of 1, uses in place mutation (as opposed to the default immutability). If it is bounded it can use static (compile time) reference counting so that when, for example, all four local references fall out of scope, the memory is dropped. If the number is unbounded (eg parameter passing recursion that can’t be tail-cpseudocode ilarly removed), runtime reference counting is used. This is all essentially tracked in the runtime system, but calls to clone are automated in roc. A beginner absolutely can write a memory hog in roc, but the same beginner is likely to overuse clone in rust and write a similar memory hog.
I don’t know whatever that language is doing is called, but it’s not reference counting. It’s doing some kind of static code analysis, and then it falls back to reference counting.
If you call that reference counting, what stops you from calling garbage collectors reference counting too? They certainly count references! Is the stack a reference count too? It keeps track of all the data in a stack frame, some of it might be references!
If you’re calling drop on a mutable string that’s been extended repeatedly, you’re recursively dropping all kinds of mess all over the heap. Checking for zero beforehand has an insignificant impact. Those cache misses you had because rust pays less attention to “where” than it does to “whether”, they cost you a lot more than the reference count check. In the real world, in practice, under profiling of real code, the cache misses and the branch misses are more expensive than the reference counting.
You sound a little bit like a C programmer who claims his code is fast because his arrays don’t do bounds checking. That’s not why C is fast. Similarly rust isn’t fast because it never does runtime reference counting. It does sometimes, but that code isn’t pathologically slow.
Also, rust isn’t just fast because of the borrow checker, primarily it’s memory safe because of the borrow checker.
If it’s any consolation, afaik, most of the roc platforms are written in rust. Also afaik only application specific code is written in roc. There are no memory management primitives in roc code unless a platform author exposes them in their api/interface, and I don’t think anyone is working on implementing C on top of roc.
I don’t know what you read on my reply. But your reply makes no sense.
Let me rephrase it if you prefer:
Claiming that Rusty’s borrow checker is reference counting is hugely misleading. Since the borrow checker was made specifically to prevent the runtime cost of garbage collection and reference counting while still being safe.
To anyone unaware, it may read as “rust uses reference counting to avoid reference counting, but they just call it borrow checking”. Which is objectively false, since rust’s solution doesn’t require counting references at runtime.
I don’t know what mutable string or any of the other rant has to do with reference counting. Looks like you’re just looking to catch a “rust evangelist” in some kind of trap. Without even reading what I said.