7 ms·
Nice balanced writeup! However, I am not convinced one can make something solving all the things Rust solves which is substantially simpler as language. Whethe
by socialdemocrat 4y ago
Nice balanced writeup! However, I am not convinced one can make something solving all the things Rust solves which is substantially simpler as language.
Whether we look at Zig, Go or Julia they stay relatively simple because they simply avoid solving certain problems. I think that is a fair tradeoff. I think good language design is really about figuring out what problems can we dump on the user which they will be able to handle relatively well.
A reason why there are so many languages I think is because different people have different preferences in terms of what problems the want to avoid dealing with and what problems they are fine dealing with.
Personally I think that if you "solve" a problem by making the language so complex that developers cannot easily read and reason about the code, then you haven't actually solved any problem. Priority must always be placed on the ability of developers to read and understand code.
Unreadable code will naturally be a source of bugs.
- vageli 4y ago> Whether we look at Zig, Go or Julia they stay relatively simple because they simply avoid solving certain problems. Could you expand on the specific problems that each language avoids?
- sk0g 4y agoTill recently, Go lacked generics. Aside from that, it has a GC that is pretty nice to work with. Manual memory management is semi-supported, using C interop instead of native language constructs (from when I last looked into it).
- kaba0 4y agoGo is a thoroughly uninteresting language bringing nothing new to the table. D, C#, even Haskell all has and had for decades now GC with manual memory at places. Other than virtual threads, which I have to give credit for, there is zero new thing from a PL perspective.
- flohofwoe 4y agoErm, Go channels were a pretty big deal in the beginning, as well as the idea of creating a 'better C' (instead of a 'better C++').
- kaba0 4y agoI absolutely don’t see anything in Go resembling C from a technical perspective, other than (before generics) being similarly unexpressive. It is much closer to JS than C in any way and shape.
- flohofwoe 4y agoI don't know, I currently write both C and JS on a daily basis, and I just can't see how Go is anything like JS. For me, Go is definitely in the C syntax family, while trying to solve roughly the same problems where Java has been used extensively (backend services).
- masklinn 4y ago> Erm, Go channels were a pretty big deal in the beginning In what world did people not know about MPMC queues?
- flohofwoe 4y agoWhen Go came around I wasn't aware of other popular languages which has channels as a core language feature (but maybe I was just ignorant).
- masklinn 4y ago> other popular languages which has channels as a core language feature That’s because other languages did not need channels as a core language features. Go does for the same reasons it needs e.g. hashmaps or append as “core language features”: no generics. As far as I could see, there was one cool thing Go brought to the table, and it’s select {}. That was (and still is, I think) pretty uncommon, though not unheard of (Erlang’s receive is similar, though it has different capabilities owing to different language semantics).
- adgjlsfhk1 4y agoJulia mostly ignores static compilation (although there now is work to add the ability to statically compile for a relatively large but limited subset of the language). The big thing that this allows is for you take the ideas of C++ templates to the extreme. In Julia pretty much every function behaves like a C++ function that is templated on the type of every input. This has the major advantage that it is really easy to write extremely fast generic code. This only works in general though because Julia only has to generate code for the types that actually appear rather than all the type combinations that static analysis can conclude might appear.
- agentultra 4y agoUnreadable as in, “this code is unfamiliar and I haven’t talent the time to understand it,” or “this code is syntactically designed to be difficult to parse by humans and I cannot read it?”
- abirch 4y agoWhat about, I can't read this code. Who's the idiot who wrote it? git blame Oh that was me.
- timeon 4y agoThis was often my issue. But I remember while learning more Rust I was like: 'now I can go back to my early Rust project to improve it!' and when I did check it: 'oh.. this is actually not bad! How was I able to do it?'
- verdagon 4y ago> Nice balanced writeup! However, I am not convinced one can make something solving all the things Rust solves which is substantially simpler as language. I used to think this too, but my opinions have evolved a bit. Recall the transition from goto-heavy assembly code to C; it solved all the things assembly solved, and was a substantially simpler language. One of the keys to designing a good language (or any good abstraction, really) is to identify the patterns that already informally exist. So if we want to see a simpler Rust, we need to look at the informal patterns that exist in Rust. * Zig did this with Rust's async/await, by making it colorblind. [0] * Vale did this with Rust's generational indices, by making generational references, a way to get memory-safe single ownership without a borrow checker. [1] It then adds a hardened FFI boundary to get more solid safety guarantees than Rust could. [2] * HVM did this for Haskell by looking at Rust's particular blend of borrowing and cloning, and making it happen automatically. [3] * Lobster did something similar for an imperative mutable language, to reduce RC costs lower than ever before. [4] I think Rust is an amazing first attempt at an interesting new problem space, and a huge step forward for the field. Now, language designers are starting to notice that the building blocks that Rust trailblazed might be combined in new interesting ways, to make new interesting languages. It's an exciting time for programming languages! [0] https://kristoff.it/blog/zig-colorblind-async-await/ https://kristoff.it/blog/zig-colorblind-async-await/ [1] https://verdagon.dev/blog/generational-references https://verdagon.dev/blog/generational-references [2] https://vale.dev/memory-safe https://vale.dev/memory-safe [3] https://github.com/Kindelia/HVM https://github.com/Kindelia/HVM [4] https://www.strlen.com/lobster/ https://www.strlen.com/lobster/
- nyanpasu64 4y agoSaying C solved all the problems assembly solved is a bit too strong; bootloaders and kernels, green thread or coroutine implementations (like libco), and cryptographic code are all implemented in part in assembly.
- flohofwoe 4y agoIMHO if C could solve all the same problems as assembly, it would need to be down on the assembly language level - which it isn't, C is clearly a high level language, it's much closer to any other programming language than any assembly dialect (maybe with the exception of Forth-like languages). In general, the higher level the language, the more flexibility you need to give up (but that's ok for many solutions).
- josephg 4y ago> However, I am not convinced one can make something solving all the things Rust solves which is substantially simpler as language. I wonder a lot about this too. As far as I know, rust is alone. It’s the first language to exist which does compile time memory management in a safe way. Every other language either is memory-unsafe (C, Zig) or garbage collected. (Go, JS, etc). Rust might the first language of its kind, but it probably won’t be the last. I’d be very surprised if we’re still using rust 100 years from now. Subsequent languages can learn from rust’s accidental complexity and clean up the approach. Personally I suspect it’s possible to have a borrow checked language that’s much simpler than rust. I don’t know what that would look like, but it would be surprising to me if we found the best programming language in this space on our (collective) first attempt at it. But I don’t have enough insight to figure out what rust’s successor would look like either. But luckily for both of us, there are a lot of smart people thinking about the problem now. Let’s give it a decade and see.
- pcwalton 4y agoThere have been memory-safe non-GC languages before, like Ada SPARK, but they generally disallow heap allocation altogether. If you heap allocate you get a GC. There were also some academic languages, most notably ML Kit, that offered a safe no-GC region-based memory management system that has a lot in common with Rust. Also, there are academic languages like Cyclone that have a very Rust-like memory management system, but they all use GCs in the end. Still, if you know Rust, Cyclone's ideas will be very familiar.
- throwaway17_17 4y agoSmall point of contention, but it’s actually Rust that has a Cyclone-like memory management system. Cyclone’s initial release was ‘02 and it finalized in ‘06.
- pcwalton 4y agoOh, I'm sorry, I didn't mean to imply that Rust invented any of those things. Cyclone was a big influence on Rust actually. We chatted with Dan Grossman early on, and his presentation "Existential Types and Imperative Languages" is the earliest description of the problem the borrow checker is trying to solve (check it out, it's a great presentation!) We stand on the shoulders of giants :)
- Nadya 4y agoI'm keeping an eye on Vale [0] but will continue to use Rust for hobby projects because the community/tooling that has built around it has made it much easier over a few years than when I first gave it a try in 2018 and especially as support for development on Windows grew. I went from absolutely struggling to get so much as a win32 GUI window to open [1] to writing a basic counting app [2] for a game I play to completely rewriting that app to not suck [3] (even though the code is still awful). Zig looks nice to read and understand. However at my skill level (more or less a skript kiddie) I don't trust myself to not cause major memory-related bugs. In fact, I can't help but wonder if it's hubris or what when extremely intelligent, college educated, programmers with more than my lifetime of experience who still manage to write programs riddled with memory safety issues and in the very next breathe say memory safety isn't an issue just write safe code. I wonder what skill/knowledge thresholds are where "I don't trust myself to not make those mistakes" turns into "I won't make those mistakes."? Honestly it reminds me of the low IQ/high IQ meme [4]. [0] https://vale.dev/ https://vale.dev/ [1] https://gitlab.com/NadyaNayme/rusty-eyes https://gitlab.com/NadyaNayme/rusty-eyes [2] https://github.com/NadyaNayme/Dorothy https://github.com/NadyaNayme/Dorothy [3] https://github.com/NadyaNayme/Dorothy-egui https://github.com/NadyaNayme/Dorothy-egui [4] https://kimiwo.aishitei.ru/i/sZaEWnY3NvfXecLm.png https://kimiwo.aishitei.ru/i/sZaEWnY3NvfXecLm.png
- kaba0 4y agoI have to agree. Adding safe, non-automatic memory management to a language will increase its complexity considerably, and I don’t see it changing even with the below mentioned new languages/techniques. Even just fundamentally, manual memory management begets functions that know about the memory architecture of your program, solidifying a given architecture. Changing this latter will result in a recursive refactor, which even if safe, is problematic/complex. Sure, it is a worthy tradeoff in certain, very niche cases (though other than writing other performant runtimes and realtime audio processing, I fail to see another area where a GC couldn’t fit - including even OSs), for which we need a language, but I really don’t get this recent hate for garbage collection, when it is truly one of the biggest productivity improvements for a PL, while simultaneously it being very performant (quite likely outperforming any naive manual take on more complex problems).
- gavinhoward 4y agoI think you are absolutely right about everything you mentioned, except for one: > I am not convinced one can make something solving all the things Rust solves which is substantially simpler as language. You could be right about this, but boy, I hope you are wrong. And I'm trying to prove that it's possible. I think the answer is structured concurrency [1], and here's why: 1. Structured concurrency can replace everything async/await can do. At least, I believe so, kind of like it's believed that structured programming can replace any unstructured use of goto. 2. If you extend structured concurrency a bit so that the thread tree is actually a DAG [2], you basically get a borrow checker for free because every item is rooted in a thread stack somewhere, and where it is rooted is always before everything that it could own and after everything that could own it. 3. Making the thread tree a DAG also means it's easy to implement RAII and have it work meaning no use-after-free, no double frees, etc. Put that on top of a language that has memory safety by default, and you will have everything that Rust gives you without the complexity. This isn't theoretical; I have implemented this in C. [3] (See [4] for a use of it.) I have even implemented bounds checking in C. [5] However, I'm also implementing a programming language with these ideas (see [6] for the example file and [7] within that file for how structured concurrency might be exposed in the language). As for readability, I do think that what I have is readable. However, I appreciate comments; readability is important to me too, but I've spent so long with this work that I am blind to what is actually readable to other people. [1]: https://gavinhoward.com/2019/12/structured-concurrency-definition/ https://gavinhoward.com/2019/12/structured-concurrency-defin... [2]: https://lobste.rs/s/8msejg/notes_on_structured_concurrency_go#c_wkxbvb https://lobste.rs/s/8msejg/notes_on_structured_concurrency_g... [3]: https://git.yzena.com/Yzena/Yc/src/branch/master/include/yc/threadset.h#L104-L115 https://git.yzena.com/Yzena/Yc/src/branch/master/include/yc/... [4]: https://git.yzena.com/Yzena/Yc/src/branch/master/src/rig/build.c#L972-L1020 https://git.yzena.com/Yzena/Yc/src/branch/master/src/rig/bui... [5]: https://git.yzena.com/Yzena/Yc/src/branch/master/include/yc/array.h#L208-L263 https://git.yzena.com/Yzena/Yc/src/branch/master/include/yc/... [6]: https://git.yzena.com/Yzena/Yc/src/branch/master/src/yao/example.y https://git.yzena.com/Yzena/Yc/src/branch/master/src/yao/exa... [7]: https://git.yzena.com/Yzena/Yc/src/branch/master/src/yao/example.y#L757-L765 https://git.yzena.com/Yzena/Yc/src/branch/master/src/yao/exa...
- Tozen 4y ago> ...Priority must always be placed on the ability of developers to read and understand code. The importance of this can't be overlooked, which is debatably why people and companies are constantly looking for a way to escape heavily complex languages and syntax. It is part of the thinking behind somewhat newer languages like Go, or newer offshoots like Vlang (https://vlang.io/ https://vlang.io/), which has a focus on high readability and general usability. If a language is being geared towards the majority or mass appeal, then it has to find that correct sweet spot. There is simply going to be a cap on how far complexity will be embraced or how much specialization will be found useful. “Genius is making complex ideas simple, not making simple ideas complex.” — Albert Einstein > ...I am not convinced one can make something solving all the things Rust solves which is substantially simpler as language. Debatably, such languages making the attempt, are less likely to succeed. Off rip, Rust already occupies that space. So, trying to make a "better Rust", which is already a specialist in that field (safety) and has already got "the push" from large corporate backers (Mozilla, Samsung, Quantum...) will usually mean failure, remaining in niche status, or overcoming incredible odds (like hitting a Lotto jackpot). The more the language specializes on that particular use case, then arguably the less general appeal and general use. And if there is already competition doing it better or being more known for that focus, the harder the hill to climb. For that matter, trying to make a "better C", is another tough hill. Not only because C is so entrenched and established, but that there is already a long list of quasi "better Cs" out there such as Object Pascal, D, Nim, etc... Add in more complex or confusing syntax, less features, less functionality, or less usefulness... Then the hill gets even more steep. Hitting the bullseye, just isn't easy.