Performing this statically is hard, and GHC tries to do it (Demand analysis).
But without a GC, these sorts of languages would be a no-go, as would logic-based languages like Prolog which need to GC their internal data structures.
While we now have the expertise for eliminating the GC from single threaded strict programs (multi threading in Rust is still quite complex, and you do see Rc<Box<..>> more often than not in these settings, which is essentially refcounting/GC), this _does not scale_ to all models of languages.
GHC even has a strictness analyzer.
Not possible generally. It would be easy to create a situation where some kind of refcount is a necessary final fallback.
> The truth is with most of the Rust I write, I don't have to worry about allocation and deallocation of objects, and it happens.
If Rust is the answer, why are you pushing for new langs when Rust is sufficient? Something doesn't add up here.
> Long live the rocket powered horse!
Your response to someone giving their years of experience is that? Well, we're all convinced now.
Maybe because he said, twice, that Rust is not all the way there?
However if he says "Rust gets us 80% of the way there" as you point out then he is obliged, IMO, to point out explicitly the 20% where it fails or we can't even begin to discuss it.
> Not possible generally. It would be easy to create a situation where some kind of refcount is a necessary final fallback.
Either refcount or a GC. But those situations might be rare in practice with a good system.
If you think about it, generational garbage collection implements lots of those heuristics. (But it's closer to a JIT optimizer, not a static optimizer.)
Adding annotations requires more programmer effort and reduces the level of abstraction of your programming language. Rust takes this approach, but tries to keep the level of annotation somewhat reasonable, and provides the "unsafe" escape hatch for when you cannot convince the compiler that what you're doing makes sense. (Although I do believe that it would be possible for the Rust compiler to infer most/all lifetimes).
For instance, in a functional programming language, you are typically creating closures all over the place, like one of the other posters mentioned. It seems quite unlikely that there is any kind of reasonable lifetime annotation system that would capture all of the complex lifetime patterns that you would see in such a language.
One can say "Oh, but surely someone smarter than me must be able to come up with something that works for those situations", but I don't think that is a very constructive line of thought, since you could say that about pretty much everything. Rust is an example of a language where lifetimes work reasonably well, but Rust already makes some tradeoffs that reduce its level of abstraction to some degree. It doesn't provide the same level of abstraction that, say, Haskell does.
Program analysis would allow you to keep your level of abstraction and ideally not require only little annotations. However, since any non-trivial program property is undecidable, program analysis is fundamentally incomplete and it is again unlikely that it would be able to deal with all lifetime patterns that you see in practice.
Therefore, it seems to me that there is no free lunch here. Either you sacrifice abstraction by requiring annotations or you don't require any annotations but then you have an incomplete program analysis.
(Also, you seem to think 30 years of good research has been wasted on the "obviously bad idea" of garbage collection, but somehow none of these researchers were good enough to realize this and come up with the silver bullet that makes GC unnecessary? After all, people were already thinking about GC-less languages long before Rust was a thing)
possible for the Rust compiler
to infer most/all lifetimes)
I'd be interested to learn why you think this is possible. As you point out yourself, by Rice's theorem, precise lifetimes are not statically decidable. Rust's lifetimes are essentially based on nesting (think well-balanced brackets) and tracked by enriching the standard Damas-Hindley-Milner approach with a notion of affin-ness which is a weakening of Girard's linearity. Circular data structures immediately violate affine-ness. What mechanism do you propose to get around this conundrum?So there's definitely room for improvement, but also good progress being made.
I think you're qualifying Rc/Arc as kludges to get around the type system. They may end up being used like that sometimes but when well used they are actually encoding extra semantics, just like &Type and &mut Type.
A simple example is a cache get() method:
pub fn get(&self, key: &K) -> Option<Arc<V>>
https://docs.rs/multicache/0.5.0/multicache/struct.MultiCach...
This allows you to get and use a cache value across your program and depending on outside input the value can disappear first in the cache by eviction or in the consumer. So Arc<V> is already expressing to the compiler the exact semantics. It just so happens that the actual lifetime is runtime defined from outside input so it must be managed dynamically.
I don't see a general way around that without copying but maybe a lot more can be inferred automatically so you get similar ergonomics to a GC language by introducing Rc/Arc/etc automatically at compile time. I don't think that's a good fit for a system programming language like Rust though where you don't pay extra costs without asking for them explicitly. But maybe there's space for a higher level language that does all that magic and doesn't use a GC. Someone trademark Rustscript.
I think I saw a Rust implementation of an arena. Is it working?
You'd probably want to do this whenever you implement a graph in a performant language, it's faster and safer (depending on the problem). Implementing edges as vectors of pointers is an exercise in memory leaks and data races, imo.
You can see an implementation with adjacency lists here:
https://github.com/bluss/petgraph/blob/master/src/graph_impl...
>I think I saw a Rust implementation of an arena. Is it working?
Of course, with traditional languages, that's the trade-off we're being asked to make. That's my point! We need to develop languages that accurately encapsulate lifetimes statically so that we can express that to the compiler. If we do, the compiler can just make instances disappear statically when we're done with them -- not dynamically! Like Rust but without the need for Rc and Arc boxes. Rust gets us 80% of the way there.
The truth is with most of the Rust I write, I don't have to worry about allocation and deallocation of objects, and it happens. It's almost ubiquitous. We need to extend that to 100%.
> Long live garbage collection!
Long live the rocket powered horse!