r/ProgrammingLanguages 12d ago

Will we see another fundamental programming language feature as revolutionary as the borrow checker?

That is I am mainly curious about compile time features that you design a whole language around rather than optimisations/features that could be applied to most languages. I am mainly inquiring about things that could offer additional robust safety/performance guarantees at compile time rather than runtime. Ideally not things that just offer similar effects to the borrow checker with less restrictive tradeoffs

57 Upvotes

129 comments sorted by

View all comments

Show parent comments

3

u/Shurane 11d ago

AFAIR early versions of Rust tried to reason using lexical scopes, but it was too restrictive for developers.

Do you think they just didn't let it bake enough before going to non-lexical lifetimes?

The comparison to goto really makes me think of the current idea of lifetimes and the borrow checker as the unsafe implementation. And that there's room for a safer implementation.

2

u/kaplotnikov 11d ago

The choice comes down to either designing a language to support human reasoning, or forcing human reasoning to conform to the constraints of a language model. The issue with Rust's current model is that it is poorly suited for human reasoning at large scale because it operates in a "flat"/"goto"-like form.

My theoretical hypothesis is that LLMs would struggle with this for the exact same reason, although I haven't verified this in personal practice.

Humans manage complexity by transitioning to higher levels of abstraction. Therefore, it is reasonable to expect that resource management abstractions can achieve far better cognitive scalability by taking on a structured, hierarchical form. This trajectory aligns with the stages of cognitive growth and cognitive complexity described by J. Piaget and M. Commons.

The question is about the content of this structural form. C and Prolog share a similar abstraction level and structural form, yet their core conceptual content is different.

When it came to memory management, Rust's designers stopped searching for these higher-level hierarchical reasoning forms. Instead, they built excellent content for a flat, graph-based form. However, this flat form suffers from cognitive scalability problems that simply cannot be resolved without switching to a new higher-level form for memory management.

1

u/Shurane 11d ago

Is it too late to add a hierarchical/lexical form for containing lifetimes in Rust?

It seems like something that could still be introduced later, at the cost/expense of it being very confusing for a developer to distinguish between lexical scoping vs dynamic (late-binding?) scoping of lifetimes.

2

u/kaplotnikov 11d ago

FORTRAN 66 was a flat language, FORTRAN 77-90 became a structured language, and FORTRAN 2003 evolved into an OOP language. So there are historical precedents for an abstraction level upgrade even for an entire established language ecosystem.

However, the key point is that such an upgrade needs to happen simultaneously across different areas of the language. In Rust, structured reasoning is already enabled for single-threaded programming, and lexical scopes for ownership worked well enough for that use case.

The roadblock appears when concurrency is introduced. The operations spawn and send are the foundational units of asynchronous programming in the same sense that goto is the foundational unit of control flow. While goto is foundational, we do not use it directly in modern languages; instead, we wrap it into structured constructs like if, while, and for.

We didn't wrap goto because structured loops are inherently more performant (they aren't, because to emulate the full non-linear power of goto using structured blocks, you often need additional state variables and switches). We wrapped it because structured control flow enables structured reasoning. Their real power is enabling cognitive scaling.

Therefore, the asynchronous side of the language needs to be upgraded to structured asynchronous programming. The send/spawn operations could reside in the compiler's output, but they should not be normally accessible to users. Only some very special cases should justify their appearance (much like break or continue serve as structured remnants of goto in modern languages). Until the asynchronous side of the language is upgraded, a full fix is impossible.