I don't really use this site any more, but thought I'd just pop in to remind people that these are my personal thoughts from last year... I think there are some things I would add or change now and as others have noted, it's ok to disagree with me!
Many of the things I listed are not new, and there's been plenty of difficult discussions about many of them over the years, and are being worked on or postponed, or rejected for various good reasons (I could have done a better job at citing stuff in this gist). Managing a living language is difficult and challenging task, and many compromises need to be made. I think the Rust community is doing a great job considering all the challenges.
That said, I'd love to see more language designers consider the possible space of memory-safe by default systems languages, learning from what Rust can teach us, and bringing on board ideas from other places, like the newer systems languages and developments in dependent types, sub-structural type systems, etc. There's still so much more to explore, and still lots that can be done to improve in Rust itself.
I think there's actually a serious point here. As is so often the case, we should be unifying our efforts to solve common, pressing problems, not fragmenting our efforts over things that matter far less. Specifically, I think those of us that care about reliability and efficiency should unite behind Rust to provide a compelling replacement for C and C++ on the one hand, and (for some applications) higher-level but less efficient languages on the other. Rust is certainly not perfect, but if we succeed at making it a popular language with great libraries covering many application domains, it'll be better than what we have now.
The two on here that stand out as things I agree with but don't see called out much are:
> allowing semicolons to be omitted after some expressions, like match, if/else, and if
This drives me up the wall. I know a lot of rust's aesthetic is there to make C++ programmers comfortable (I'm a recovering C++ programmer myself), but it would have been so much better if semicolons had been left out.
It seems like we put a semicolon on every line (except the ones mentioned above! But only if they're not subexpressions!) just to make the whole "you don't need to say return in the last statement" look clever.
> Confusion about whether to use kebab-case or snake_case for crate names I now lean to the former, but it's impossible to import snake case crates using kebab case, leading to an ugly mix in my Cargo.toml file.
I'm so tired of guessing which one I need every time I import a crate in Cargo.toml. Never mind that this is a blatant opportunity to make supply-chain attacks. I don't know why these are treated as distinct names in crate names, and if there are any conflicting crates out there in the wild right now they should probably all be forced to rename.
My personal pet peeve that doesn't come up much is the way magic impls around From/TryFrom/Into/TryInto conspire to make incredibly obtuse error messages about implementing TryFrom<T, Error=Infallible> or the like. I'm not entirely convinced having these 'helpful' impls is really worth it, at least not the way they were implemented.
> just to make the whole "you don't need to say return in the last statement" look clever.
I sure prefer `let x = if foo { 0 } else { 1 };` over `let x = if foo { return 0; } else { return 1; };`. Which raises another potential dilemma: do you break consistency and allow returning values without `return` in if-blocks, or do you make it impossible to return from a function early within them, instead making the returns exit the if-expression early for ultimate confusion?
No, please don't take away my semicolons unless you provide an actual alternative, even if it's just a Lisp-like flood of parentheses.
It's not clear to me why it even needs to be treated as a special case. In particular, the biggest other example of a language that has this "return or hanging last expr" I'm aware of is ruby, where the problem with it is that accidentally returning a value from a block might affect escape analysis for GC, but rust doesn't have that problem.
In your example, the if is in expression position, so it's fine if it treats its bodies as expressions. I'm definitely not saying I want to pepper my code with returns, I'm just unconvinced semicolons help with avoiding that.
I think the alternative style of semicolons are as separators which makes more sense from the grammar and is what many languages that are C-like do. Rust tries to be both and unsurprisingly ends up with complicated and IMO fragile rules.
Why not do it like Ruby, where blocks always return the result of the last statement (and the return keyword returns from the nearest enclosing function)?
Someone correct me if I'm wrong, but I think the semi-colons main purpose is to make error messages better. It makes it easy to say "well, the previous malformed statement definitely finishes by here".
And to avoid all the horrible edge-cases that can arise when the parser has to work out whether the next line is a continuation of the same statement or not.
For example in JS:
var x = y.z
[a, b, c].forEach(doIt)
parses to:
var x = y.z[a, b, c].forEach(doIt)
rather than the two statements that were clearly intended.
Because not all statements in a block have to end in a semicolon, and most things at module scope don't, rust fails at this anyways imo. Without an editor's help I'm pretty frequently lost finding the missing balancer for an open brace or parenthesis.
> Never mind that this is a blatant opportunity to make supply-chain attacks. I don't know why these are treated as distinct names in crate names, and if there are any conflicting crates out there in the wild right now they should probably all be forced to rename.
crates.io already disallows crates differing only in underscore/hyphen choice. I can't easily find documentation to this effect, but users have reported it returning an error (e.g., [0]).
I suppose it must be a thing that while crates.io does disallow this, there's a possibility that there's a private registry out there that does allow it. That's the only reason I can think of for why they haven't just made it treat them as interchangeable in Cargo.toml files.
I feel like cargo should have gone further and made it borderline impossible to write out the package name from a guess and require you to look it up and copy/paste the name so its always right. They should have required all packages to be scoped under an org name like NPM is moving towards.
That would have stopped typo squatting much better than making names insensitive to all kinds of weird conventions.
I think it would've been sufficient to namespace package names, something like `dtolnay::syn = "1"` (this is terrible syntax, just an example). Perhaps also allow Cargo to be configured to fail if any of the packages were outside of an allowlist of trusted namespaces. I think this is a good compromise between allowing people to experiment quickly and allowing established projects to lock themselves down.
Is it Stockholm syndrome if I prefer semicolons over no semicolons?
It gives me warm fuzzies in the belly knowing that as long as I've placed the semicolon it doesn't matter if I indented correctly or used the new line correctly.
I'm even one those weirdos that put the first squiggly bracket right after the statement(
There are languages like Lua that do not require semicolons and are also whitespace (and newline) insensitive.
That comes with other tradeoffs, the main one being that Lua is forced to maintain a pretty restrictive grammar to remain unambiguous. There are only very limited expression statements, for example. But anyway, it exists and it addresses the points you specifically made in your comment.
> It seems like we put a semicolon on every line just to make the whole "you don't need to say return in the last statement" look clever.
This has been my own finding when designing a language. Freestanding expressions can in most cases be turned into statements. Even for things like Rust's if...else expressions, a 'be' keyword could work.
This is a pretty reasonable list, one I resonate with (as an otherwise large fan of Rust).
I'll point out one that I think is a little less solid:
> Cargo.toml is capitalised, unlike most other development files.
This is consistent with Makefile, Gemfile, Pipfile, etc., meaning plenty of precedent. My understanding is that these files are capitalized to put them first in a directory listing.
This seems like a reasonable list of things a reasonable person might have grievances about.
With these types posts in general, keep in mind that they are "personal lists" -- meaning that other people might feel differently, and that's OK.
The context of the tweet that motivated this was "how can things be improved", and lists like this help (if not for rust, then for whatever comes after rust).
To pick one thing (mostly at random) to comment on:
> It might have been better to call `unsafe` blocks `trusted` blocks.
This is a thought I've often had myself. The name `unsafe` is not wrong, per-se, but it can sometimes have the wrong connotation
> This is a thought I've often had myself. The name `unsafe` is not wrong, per-se, but it can sometimes have the wrong connotation
Yeah, this is one of the many things on this list that isn't a new idea, see this RFC from June 2014: https://github.com/rust-lang/rfcs/pull/117. I believe D has a `@trusted` attribute at the function level that serves a similar purpose.
> Traits are hard to extend after the fact, and you can't break them into smaller parts in a backwards-compatible way.
> No way of defining abstract types that conform to a given interface. You can use traits, but these do not support abstract associated types, and require a Self type.
This is definitely one of the main grievances I have with Rust. Overall, Rust can be a productive language but it's frustratingly limited when it comes to advanced types or really anything compile time. I read something a while back that made sense to me, to paraphrase "Rust solves problems by brute force".
The syntax for annotations on structs are also really distracting to me as well.
(edited spelling)
Annotations are very useful, but they're pretty much ugly regardless of the language.
Though I like the sibling comment's Scheme version where it's postfixed. I've also come to appreciate Nim's sorta post-name annotation syntax since I can quickly read the name and type at the ends:
type MyComponent = object
position {.editable, animatable.}: Vector3
alpha {.editRange: [0.0..1.0], animatable.}: float32
I was pondering if languages could provide CSS like annotation. Like
>> Many sub-languages to learn, many with different syntaxes and semantics. For example:
>>
>> the expression language
>> unsafe runtime language
>> safe runtime language
>> compile time language
>> the type language
>> the trait language
>> the macro language
>> the attribute language
I've been wanting to ask Rust experts about this..... to what extent is this really necessary?
Is it possible to create a language with all the memory and thread safety of Rust, based on a single language with no sub languages?
The micro-syntax in attributes could have been avoided. There isn't any technical reason why:
#[cfg(all(feature = "a", target = "b"))]
couldn't look more like the rest of Rust:
#{ has_feature("a") && is_target("b") }
I'm not sure if declarative macros would have been better without a custom syntax. Procedural macros are just regular Rust, and they're verbose and hard to follow. In this case languages that aren't LISP just have to accept some trade-offs.
In case of safe Rust vs unsafe Rust vs compile-time Rust, I think they're fine. They're still using the same language with the same syntax. The specifics of what is allowed in safe/unsafe/const are different, but that's the point of these features.
Maybe it is. But each of these perform a role that can’t be easily removed.
- unsafe. You could make a language that didn’t allow third parties to write unsafe code. Except there are a few cases where it’s necessary. Me personally, I’ve never written any unsafe but I have relied on well written, vetted implementations written by others (like the standard library Vec).
- compile time language. A language either needs run time reflection or it needs compile time meta programming. Rust chooses the bare minimum for a runtime so there are no reflection APIs. That said, there probably is space for more ergonomic meta programming APIs. It still wouldn’t look like regular Rust, but it could be easier to read.
I don’t know what they mean by expression language. And I thought the type and trait language is the same? Calling these “languages” seems excessive to me, but sure.
I think it’s almost certainly possible to improve on Rust, either in place or by creating a new language that makes different trade-offs. Maybe that one would provide run time reflection, for instance.
I am not a expert, but I wrote things I am happy with and I think it is a little over the top. What is called the type language, the trait language and the attribute language here are just one thing for example. Those are not necessarily sub languages, they are different parts of the same thing. The hard thing is not the syntax or the way you write it, it is the concepts behind it.
The hard things about Rust are hard, because they are novel concepts that solve existing, hard problems in often very intelligent ways, while still upholding the guarantuees the language gives you. Granted, in C there would be less language to learn (although getting the details about value conversion right can be hard) — but using the language at a level where you'd be able to utilize it safely at a level like that would take a few decades as well. Because now you have to uphold the guarantees. C has no concept of ownership and lifetimes for example, but if you use your pointers without having a mental concept of ownership and lifetimes you are going to introduce the next exploitable zero day.
That being said: You can also happily program without ever touching half of that list and you would still end up with programs that are fast, maintainable and compile fine. In fact I'd argue unless you are building some hardcore library where you have to do things in optimal ways while still getting the ergonomics you'd be totally fine with e.g. avoiding unsafe, macros etc. The language is flexible in that way, you can go extremely deep or stay on the surface (or even do both in different parts of your program at the same time).
The type system is something that you need to get used to if you come from dynamic languages, but once you grasp it, the guarantuees it provides really simplify a lot of things in your head. E.g. typically when a compile fails I would know right away where to look and how to fix it, even in big bespoke programs. It prevents a whole class of bugs that every programmer makes once in a while. But type systems are old programming concepts. Sure there is a certain way it is written and used in Rust, but the hard thing again is the concepts underneath (e.g. how a type system makes your variables map to memory and what cool tricks you can do with that).
Of all those the only one that bugs me is macros. They are legitimately alien to the Rust language and remind me of Perl (a.k.a. Herl). I do understand why macros are a sub-language because they're doing something totally unlike what the main language does, but could the syntax have been less fugly?
I think it's more a reflection of how Rust evolved, and the techniques and approaches known and understood at the time and the strangeness budget they were (understandably) willing to take on at the time as opposed to something inherent. And also sometimes having separate, complicated features for similar things (as opposed to simple, generalised features that compose powerfully) can be useful pedagogically as well.
At any rate, this is something I'm personally interested in based on my experience working with Rust over the last decade, and so that's why it appears so high up on my list. Often you really do want sub-languages for different purposes, but managing how they interact and work together, what is the same and what is different, and how that impacts usability is interesting (and difficult) part. I feel like it should be possible to do this, but it's going to take some work and there's still lots of unknowns.
In technical terms, I'm interested in dependently typed module systems, multistage programming[1], graded modal type theory[2], elaborator reflection, and two level type theory[3]. These all sound pretty intimidating, but you can actually see glimmers of some of this stuff in how Zig handles type parameters and modules, for example, something that most programmers really like the first time they see it!
I do feel like there is the core of a simple, flexible, powerful systems language out there... but finding it, and making it approachable while maintaining a solid footing in the theory and being sensitive to the practical demands of systems programming is a nontrivial task, and many people will be understandably skeptical that this is even a good direction to pursue. Thankfully the barrier to entry for programming language designers to implementing languages in this style has reduced significantly in just the last number of years[4], so I have hope that we might see some interesting stuff in the coming decade or so. In the meantime we have Rust as well, which is still an excellent language. I'm just one of those people who's never content with the status quo, always wishing we can push the state of the art further. This is why I got excited by Rust in the first place! :)
I think you share a lot of my interests on the theory side of things. I also share your belief that those areas probably can allow for the development of the core of systems level programming language. Of course, I tend to find that graded, or the more general contextual modal type theory subsumes multi-staged, two level and grade modal theories. I also think a broad spectrum dependently typed language (with modal enforcement of computational irrelevance) can take care of dependent modules.
I think you are also spot on about Rust having a strangeness budget and that could be responsible for the syntactic state of the language as it exists today. I have a much higher tolerance for non-conventional syntax so almost all of my type theory implementation and PL work has been outside the normal syntactic bounds for the last 2 years. I doubt I ever produce a language that is public, let alone a language that is used by any portion of the software engineering field. But my belief is that this arena is fertile ground for a more fundamental core, like you mentioned.
As an aside, I happened to stumble upon Andras’ video presentations on YouTube on Saturday and flagged them to watch and bookmarked the repo for them earlier today. So bravo on linking what look to be really nice resources for this area of work.
> No support for using something like separation logic within Rust itself to verify that unsafe code upholds the invariants that the safe language expects.
I think this is something we might see in the future. There are a lot of formal methods people who are interested in rust. Creusot in particular is pretty close to doing this - at least for simpler invariants
Disclaimer: I have been (re-)learning Rust recently through some toy projects.
> Type aliases are transparent (as opposed to abstract) by default, exposing their definitions publicly.
IMHO this hurts a lot because the main alternative, which is the "newtype" pattern (`struct NewType(OldType);`), introduces a lot of friction. Yes, you can `derive`, but it usually takes a lot more to reach "feature parity" with the original type, with only the undesirable parts swapped out. There are libraries to help: `derive_more`, `derivative`, etc. but the qualities vary, you need to find the correct library in the first place, and even then there's no guarantee they play nice with each other...
This makes the 1st point worse TBH. With a myriad of newtypes and typedefs, it soon becomes confusing what can convert to what, through which trait or direct methods.
Because Rust has restrictions on what can be impl'd to containing at least one local type/trait.
impl Foo<X> for Y {}
^^^--- ^ one of these two has to belong to your current crate
|
this part doesn't matter
If you tried
impl ForeignTrait for ForeignType {}
You get a compile error, hence why you can sometimes hear people suggesting using a new-type
struct LocalType(ForeignType);
impl ForeignTrait for LocalType {}
^^^^^^^^^ this is a local type, so it compiles
This restriction is to avoid allowing semver footguns, where changes in a dependency of a dependency can cause your current crate to stop compiling after a seemingly innocuous upstream change.
With that context
impl Into<Result<U, E>> for &T
^^^^ ^ could be foreign?
|
foreign (from std::)
If you want to provide your users with a way to call .into() on a type you provide, that can be converted to a type they wrote, you need to be able to express the opposite construct, hence `From`
impl From<Foreign> for Local
^^^^ ------- ^^^^^ local type, so the impl is allowed
| |
| foreign, but it doesn't matter
foreign (from std::)
I don't really use this site any more, but thought I'd just pop in to remind people that these are my personal thoughts from last year... I think there are some things I would add or change now and as others have noted, it's ok to disagree with me!
Many of the things I listed are not new, and there's been plenty of difficult discussions about many of them over the years, and are being worked on or postponed, or rejected for various good reasons (I could have done a better job at citing stuff in this gist). Managing a living language is difficult and challenging task, and many compromises need to be made. I think the Rust community is doing a great job considering all the challenges.
That said, I'd love to see more language designers consider the possible space of memory-safe by default systems languages, learning from what Rust can teach us, and bringing on board ideas from other places, like the newer systems languages and developments in dependent types, sub-structural type systems, etc. There's still so much more to explore, and still lots that can be done to improve in Rust itself.
I happen to agree with a significant portion of that list and some are, as you point out, being worked on.
> allowing semicolons to be omitted after some expressions, like match, if/else, and if
This drives me up the wall. I know a lot of rust's aesthetic is there to make C++ programmers comfortable (I'm a recovering C++ programmer myself), but it would have been so much better if semicolons had been left out.
It seems like we put a semicolon on every line (except the ones mentioned above! But only if they're not subexpressions!) just to make the whole "you don't need to say return in the last statement" look clever.
> Confusion about whether to use kebab-case or snake_case for crate names I now lean to the former, but it's impossible to import snake case crates using kebab case, leading to an ugly mix in my Cargo.toml file.
I'm so tired of guessing which one I need every time I import a crate in Cargo.toml. Never mind that this is a blatant opportunity to make supply-chain attacks. I don't know why these are treated as distinct names in crate names, and if there are any conflicting crates out there in the wild right now they should probably all be forced to rename.
My personal pet peeve that doesn't come up much is the way magic impls around From/TryFrom/Into/TryInto conspire to make incredibly obtuse error messages about implementing TryFrom<T, Error=Infallible> or the like. I'm not entirely convinced having these 'helpful' impls is really worth it, at least not the way they were implemented.
I sure prefer `let x = if foo { 0 } else { 1 };` over `let x = if foo { return 0; } else { return 1; };`. Which raises another potential dilemma: do you break consistency and allow returning values without `return` in if-blocks, or do you make it impossible to return from a function early within them, instead making the returns exit the if-expression early for ultimate confusion?
No, please don't take away my semicolons unless you provide an actual alternative, even if it's just a Lisp-like flood of parentheses.
In your example, the if is in expression position, so it's fine if it treats its bodies as expressions. I'm definitely not saying I want to pepper my code with returns, I'm just unconvinced semicolons help with avoiding that.
For example in JS:
parses to: rather than the two statements that were clearly intended.crates.io already disallows crates differing only in underscore/hyphen choice. I can't easily find documentation to this effect, but users have reported it returning an error (e.g., [0]).
[0] https://github.com/rust-lang/crates.io/issues/166
That would have stopped typo squatting much better than making names insensitive to all kinds of weird conventions.
It gives me warm fuzzies in the belly knowing that as long as I've placed the semicolon it doesn't matter if I indented correctly or used the new line correctly.
I'm even one those weirdos that put the first squiggly bracket right after the statement(
if ( ... ) { . . . }
That comes with other tradeoffs, the main one being that Lua is forced to maintain a pretty restrictive grammar to remain unambiguous. There are only very limited expression statements, for example. But anyway, it exists and it addresses the points you specifically made in your comment.
It gives me warm fuzzies in the belly knowing that as long as I’ve indented correctly it doesn’t matter If I placed the semicolon.
This has been my own finding when designing a language. Freestanding expressions can in most cases be turned into statements. Even for things like Rust's if...else expressions, a 'be' keyword could work.
FWIW, I created a DSL where comma and semicolon are treated as whitespace.
> ...kebab-case or snake_case...
Crate names are case insensitive, right?
Maybe treat underscore and hyphen as different cases of the same delimiter?
I'll point out one that I think is a little less solid:
> Cargo.toml is capitalised, unlike most other development files.
This is consistent with Makefile, Gemfile, Pipfile, etc., meaning plenty of precedent. My understanding is that these files are capitalized to put them first in a directory listing.
With these types posts in general, keep in mind that they are "personal lists" -- meaning that other people might feel differently, and that's OK.
The context of the tweet that motivated this was "how can things be improved", and lists like this help (if not for rust, then for whatever comes after rust).
To pick one thing (mostly at random) to comment on:
> It might have been better to call `unsafe` blocks `trusted` blocks.
This is a thought I've often had myself. The name `unsafe` is not wrong, per-se, but it can sometimes have the wrong connotation
> This is a thought I've often had myself. The name `unsafe` is not wrong, per-se, but it can sometimes have the wrong connotation
Yeah, this is one of the many things on this list that isn't a new idea, see this RFC from June 2014: https://github.com/rust-lang/rfcs/pull/117. I believe D has a `@trusted` attribute at the function level that serves a similar purpose.
This is definitely one of the main grievances I have with Rust. Overall, Rust can be a productive language but it's frustratingly limited when it comes to advanced types or really anything compile time. I read something a while back that made sense to me, to paraphrase "Rust solves problems by brute force".
The syntax for annotations on structs are also really distracting to me as well. (edited spelling)
I really like annotations on structs/struct fields, but I've never seen a syntax for it that felt quite right.
The ones that I've seen and come to mind are rust's:
go's: and kotlin/scala/...'s I'm curious if people have other examples, and/or a preference for one.Though I like the sibling comment's Scheme version where it's postfixed. I've also come to appreciate Nim's sorta post-name annotation syntax since I can quickly read the name and type at the ends:
I was pondering if languages could provide CSS like annotation. LikeI've been wanting to ask Rust experts about this..... to what extent is this really necessary?
Is it possible to create a language with all the memory and thread safety of Rust, based on a single language with no sub languages?
In case of safe Rust vs unsafe Rust vs compile-time Rust, I think they're fine. They're still using the same language with the same syntax. The specifics of what is allowed in safe/unsafe/const are different, but that's the point of these features.
- unsafe. You could make a language that didn’t allow third parties to write unsafe code. Except there are a few cases where it’s necessary. Me personally, I’ve never written any unsafe but I have relied on well written, vetted implementations written by others (like the standard library Vec).
- compile time language. A language either needs run time reflection or it needs compile time meta programming. Rust chooses the bare minimum for a runtime so there are no reflection APIs. That said, there probably is space for more ergonomic meta programming APIs. It still wouldn’t look like regular Rust, but it could be easier to read.
I don’t know what they mean by expression language. And I thought the type and trait language is the same? Calling these “languages” seems excessive to me, but sure.
I think it’s almost certainly possible to improve on Rust, either in place or by creating a new language that makes different trade-offs. Maybe that one would provide run time reflection, for instance.
The hard things about Rust are hard, because they are novel concepts that solve existing, hard problems in often very intelligent ways, while still upholding the guarantuees the language gives you. Granted, in C there would be less language to learn (although getting the details about value conversion right can be hard) — but using the language at a level where you'd be able to utilize it safely at a level like that would take a few decades as well. Because now you have to uphold the guarantees. C has no concept of ownership and lifetimes for example, but if you use your pointers without having a mental concept of ownership and lifetimes you are going to introduce the next exploitable zero day.
That being said: You can also happily program without ever touching half of that list and you would still end up with programs that are fast, maintainable and compile fine. In fact I'd argue unless you are building some hardcore library where you have to do things in optimal ways while still getting the ergonomics you'd be totally fine with e.g. avoiding unsafe, macros etc. The language is flexible in that way, you can go extremely deep or stay on the surface (or even do both in different parts of your program at the same time).
The type system is something that you need to get used to if you come from dynamic languages, but once you grasp it, the guarantuees it provides really simplify a lot of things in your head. E.g. typically when a compile fails I would know right away where to look and how to fix it, even in big bespoke programs. It prevents a whole class of bugs that every programmer makes once in a while. But type systems are old programming concepts. Sure there is a certain way it is written and used in Rust, but the hard thing again is the concepts underneath (e.g. how a type system makes your variables map to memory and what cool tricks you can do with that).
At any rate, this is something I'm personally interested in based on my experience working with Rust over the last decade, and so that's why it appears so high up on my list. Often you really do want sub-languages for different purposes, but managing how they interact and work together, what is the same and what is different, and how that impacts usability is interesting (and difficult) part. I feel like it should be possible to do this, but it's going to take some work and there's still lots of unknowns.
In technical terms, I'm interested in dependently typed module systems, multistage programming[1], graded modal type theory[2], elaborator reflection, and two level type theory[3]. These all sound pretty intimidating, but you can actually see glimmers of some of this stuff in how Zig handles type parameters and modules, for example, something that most programmers really like the first time they see it!
I do feel like there is the core of a simple, flexible, powerful systems language out there... but finding it, and making it approachable while maintaining a solid footing in the theory and being sensitive to the practical demands of systems programming is a nontrivial task, and many people will be understandably skeptical that this is even a good direction to pursue. Thankfully the barrier to entry for programming language designers to implementing languages in this style has reduced significantly in just the last number of years[4], so I have hope that we might see some interesting stuff in the coming decade or so. In the meantime we have Rust as well, which is still an excellent language. I'm just one of those people who's never content with the status quo, always wishing we can push the state of the art further. This is why I got excited by Rust in the first place! :)
[1]: https://github.com/metaocaml/metaocaml-bibliography
[2]: https://granule-project.github.io/
[3]: https://github.com/AndrasKovacs/staged
[4]: https://github.com/AndrasKovacs/elaboration-zoo/
I think you are also spot on about Rust having a strangeness budget and that could be responsible for the syntactic state of the language as it exists today. I have a much higher tolerance for non-conventional syntax so almost all of my type theory implementation and PL work has been outside the normal syntactic bounds for the last 2 years. I doubt I ever produce a language that is public, let alone a language that is used by any portion of the software engineering field. But my belief is that this arena is fertile ground for a more fundamental core, like you mentioned.
As an aside, I happened to stumble upon Andras’ video presentations on YouTube on Saturday and flagged them to watch and bookmarked the repo for them earlier today. So bravo on linking what look to be really nice resources for this area of work.
I think this is something we might see in the future. There are a lot of formal methods people who are interested in rust. Creusot in particular is pretty close to doing this - at least for simpler invariants
https://github.com/xldenis/creusot
> Type aliases are transparent (as opposed to abstract) by default, exposing their definitions publicly.
IMHO this hurts a lot because the main alternative, which is the "newtype" pattern (`struct NewType(OldType);`), introduces a lot of friction. Yes, you can `derive`, but it usually takes a lot more to reach "feature parity" with the original type, with only the undesirable parts swapped out. There are libraries to help: `derive_more`, `derivative`, etc. but the qualities vary, you need to find the correct library in the first place, and even then there's no guarantee they play nice with each other...
> conversions: From, TryFrom, Into, TryInto, As, AsRef, AsMut
This makes the 1st point worse TBH. With a myriad of newtypes and typedefs, it soon becomes confusing what can convert to what, through which trait or direct methods.
With that context
If you want to provide your users with a way to call .into() on a type you provide, that can be converted to a type they wrote, you need to be able to express the opposite construct, hence `From`