Threads for bhansconnect

  1. 1

    Elm is presumably not on the list since it compiles to JS and would therefore be more-or-less indistinguishable from the plain JS baseline?

    1. 3

      All of these options compile to JS.

      1. 1

        My bad. I thought everything on that list but Reason compiled to wasm. The Elm omission seems rather glaring then, doesn’t it?

        1. 2

          There’s no ClojureScript, Scala.js, F#, derw, js_of_ocaml, LunarML, Gleam, and many other FP options either.

      2. 2

        It might do some tricks to get better optimization and be a bit faster, but that can be hit or miss depending on the specific code. I’m not sure why it isn’t included. Presumably the author didn’t know about it?

      1. 4

        I remember running into this a long while ago. I wrote 2 versions in Roc compiled to wasm. It was significantly faster than any of the current implementations. The Roc version took approximately 0.1 ms to run. So about 10x faster than the JavaScript version. That said, performance.now() which I used for timing only measure to about 0.1 ms on my browser. So sometimes the roc function measures as taking zero time. Though on average, it was just under 0.1 ms.

        This is the actual core of the Roc code (though it probably needs to be updated to compile): https://github.com/bhansconnect/functional-mos6502-web-performance/blob/master/implementations/roc-effectful/emulator.roc

        Might have to revive this to put exact stats on this thread.

        1. 8

          Modern CPUs and GPUs have hardware support for sin, cos and tan calculations. I wonder if trig functions that operate on turns rather than radians can really be faster if they aren’t using this hardware support. I guess it depends on the specific processor, and maybe also on which register you are operating on (since intel has different instruction sets for different size vector registers).

          If you are programming a GPU, you are generally using a high level API like Vulkan. Then you have the choice of using the SIN instruction that’s built into the SPIR-V instruction set, vs writing a turn-based sin library function using SPIR-V opcodes. I wouldn’t expect the latter to be faster. Maybe you could get some speed using a lookup table, but then you are creating memory traffic to access the table, which could slow down a shader that is already memory bound.

          1. 4

            A lot of code avoids using hardware sin and cos because they are notoriously slow and inaccurate. As such, it ends up using software emulated sin, cos, etc. So turns definitely shouldn’t be worse.

            Maybe this is changing, but historically on CPUs, using the sin instruction is not a great idea.

            1. 1

              I wonder if trig functions that operate on turns rather than radians can really be faster if they aren’t using this hardware support.

              CUDA has sincospif and sincosf, only the latter has its precision affected by –use_fast_math, so maybe all “builtin” functions still do the conversion to turns in code before accessing hardware?

              https://docs.nvidia.com/cuda/cuda-math-api/group__CUDA__MATH__SINGLE.html#group__CUDA__MATH__SINGLE_1g9456ff9df91a3874180d89a94b36fd46

            1. 5

              Interesting read, I definitely agree with the general sentiment.

              TLDR: In Roc, which has approximately 5 to 10 big contributors, most of the issues mentioned in this article don’t arise too often/have ok solutions. Definitely not as flexible as being solo, but surprisingly decent. With correctly set expectations and some minor cost, I think going from solo to a small team is not a big deal. With more growth though, these issues definitely become worse.


              As one of the large contributors to Roc, I have a few comments on the potential issues you mentioned and how they played out for me while contributing to Roc.

              Working alone means you don’t have to share the context of your changes with others… But if there’s a change I wish to make, I can do so without consulting others.

              100% agree. All of my hobby projects that are open source are this way. Sure someone can depend on it if they want, but I can and will change it whenever I am working on it. It is up to others to keep up if they want to use the project. The project is primarily for me. It is open to other just in case it happens to be useful for them.

              That being said, a lot of main Roc contributors essentially own a section of Roc (I mostly deal with the x86/arm dev backend and the surgical linker). As such, they are the go to person on that section of code. Due to how the compiler is structured, a contributor can do a lot without consulting anyone (except for code reviews because we require them). In many parts of the code, there is almost no chance of a PR collision. Still not as good as a solo project on these ergonomics, but surprisingly not bad.

              You’re able to keep to your own schedule, and adjust it as necessary.

              This is hit or miss. I think that Roc has done a good job at setting expectation. All of the main developers know that others are working in their spare time. It may not be possible for them to get to a feature that you want. You might have to just wait for them or figure it out yourself. People also have work they find interesting. If the feature you want is drab work, it might be a bit before someone wants to deal with it. I think overall the expectations are low here and the core contributors are all friendly so it doesn’t really lead to issues in practices, but as Roc gets closer to 0.1 with some real users, this is likely to get a lot more complex.

              Shared goals have to be established through long conversations and RFCs.

              We have live text chat with discussions that works pretty well. We also do a lot of video chats which work around these communication issues quite well for us. On top of that, working on Roc is ultimately accepting that Richard is currently the BDFN (benevolent dictator for now), and you are buying into his vision. You mostly have to convince him. That may sound bad to a number of people, but it does deal with this issue quiet well. Still more restricted, but mostly works fine so far. Again, growth and more users will likely eventually strain this.

              a creator with a strong opinion and direction leads to a purer, more cohesive creation

              Totally agree, which is why it is a BDFN. Does have some overhead compared to a solo project, but not bad.

              I can take this collective knowledge on a specific feature and implement it myself, and that’s likely the best way for a new language.

              I think this depends a lot on the language. Many languages have so many pieces that a single person can not implement them all in a reasonable amount of time. Roc would probably never get finished without the community of contributors. It has a lot of different things to handle (some using novel PhD algorithms). Just so much work for one person to cover.

              1. 4

                a lot of main Roc contributors essentially own a section of Roc

                This was how a lot of Elm community projects were run - under my banner, for some time, fell json-to-elm, elm-webpack-loader, elm-test, and the elm-community org on Github. It definitely makes sense when a project is scaling up - like you say, there’s a lot of different topics that might require someone to do a deep dive that wouldn’t be possible if there was one person doing it alone. But I’ve also seen the benefits with Derw of doing much of that myself - for example, having written the testing framework and the CLI interface to Derw means that I know how to make those pieces fit together nicely, without the need for convincing others.

                On top of that, working on Roc is ultimately accepting that Richard is currently the BDFN (benevolent dictator for now), and you are buying into his vision

                This is how I think languages, and a lot of projects, have to work. There’s a reason why we don’t just build everything based on groupthink, and in my experience groups tend to establish weaker visions than if they had someone who could guide the conversations and debates. In the case of Roc, I know from experience that Richard is excellent at both listening to people and enabling them to do great things.

                1. 4

                  Roc has a very simple story: like Elm, but compiles to native executables. It’s easier to organize a group of people if the mission is crystal clear. Again, Linux was: “like Unix on a PC, except GPL”. Those projects had lots of collaborators very early on.

                  If you are doing an exploratory research project, creating a new category of software without a lot of hard constraints, then it makes sense to do cathedral style development with a single person or a very few close collaborators, until the design is solid and the requirements are crystal clear. Then it’s time to open up to bazaar style collaboration.

                  Smalltalk was an extremely ambitious & original project. It started as a series of exploratory prototypes designed and implemented by Alan Kay from 1967-1971 (from Flex, through some intermediates, to Smalltalk-71). Then it became a cathedral-style project at Xerox PARC with a small, close group of collaborators from 1972-1980, undergoing massive design changes. It was released in 1980, at which point the design became more or less frozen. Now Smalltalk is a bazaar.

                  I understand that Rust was a personal hobby project by Graydon Hoare from 2006-2009, then in late 2009 Mozilla got interested and assigned a team of developers to the project, switching to cathedral mode until there was an MVP solid enough to release. Now Rust has a huge community of contributors.

                  1. 1

                    If you are doing an exploratory research project, creating a new category of software without a lot of hard constraints, then it makes sense to do cathedral style development with a single person or a very few close collaborators, until the design is solid and the requirements are crystal clear. Then it’s time to open up to bazaar style collaboration.

                    Thanks for this framing. It more clearly names and tags something my intuition has suspected for a while, but I’ve been leery that it was just finding excuses for how I already enjoy working.

                1. 1

                  I have switch between a few backup tools over the years. Probably have used restic the most. Need to set that up again. Haven’t since reinstalling my machine. No critical data, but definitely some data worth backing up.

                  1. 1

                    So many questions.

                    Can this only be used in the browser? Like, can I run the fuzzer on a React app via CLI? Is this React-only? Can I add properties to check invariants of the UI?

                    This looks super cool. I’m just trying to evaluate how I’d use it on a project.

                    1. 1

                      It is someone else project, but to my understanding: it is mostly a proof of concept. It is react only. It can be run locally, but will still launch a browser window to execute in. No idea on invariants.

                    1. 2

                      I don’t see the problem. If the change I make turns out to be big, I split the “narrative” into steps and put each one into its own commit, so the branch ends up with two or three of them. The reviewer can follow the story by starting at the first one and reviewing them in order.

                      Sometimes I do use “stacked branches” though. When I need the code of the first for the next ticket/feature/task. This is only for me, so that I can continue with my work. By the time the second PR gets to the reviewer, the first should have been merged already and the reviewer would never know it started out as a stacked on the first, because I would rebase before submitting it.

                      1. 1

                        The issue is the code review interface and unit. Thinking about a tool like github PRs, it is extremely in convent to follow the flow of commits. It is really just made to show you the final diff. Generally speaking the finally diff is too large and not focused enough. As such, you are stuck reviewing something with the problems mentioned in this article unless you dis. Lot more manual work to look at each individual commit. That said, even if you look at each individual commit, you can’t leave review comments in them you have to go back and leave comments in the full PR, where context is lost.

                        This is definitely why I prefer stacked diffs/commits as the review unit instead.

                      1. 5

                        I produce stacked PRs constantly and I think they’re valuable. I love that we’re seeing more and more tooling to support them.

                        But I think articles like this are pretty one-sided and don’t acknowledge the risks and costs of stacked PRs. They aren’t 100% positive with zero downsides. So here are some reasons you shouldn’t go all-in on stacked PRs.

                        1. They often replace the “500 lines = looks good” problem with the “10 lines = looks good” problem. I can split a large change up into a sequence of small pieces, each of which is a correct, self-contained piece of code that quickly passes review. They can even be reviewed in parallel by different reviewers to really maximize my velocity. And when I’m done, I will have solved the problem in an awful way that makes no sense when you look at it as a whole. No reviewer could evaluate the whole change because I doled it out one tidbit at a time.
                        2. They take more work to produce. The article makes the point that reviewer effort goes up more than linearly with increases in PR size, which may be true. But author effort goes up more than linearly with decreases in PR size. Breaking your 500-line change into fifty 10-line changes will probably get you lightning-fast reviews, but is it worth an extra two days of your time to save two hours of your reviewers’ time? (You might not even notice, because splitting up the change feels like two days of useful, productive work, whereas twiddling your thumbs for a couple extra hours feels frustrating and useless.)
                        3. They can cause a feedback loop where the cultural norm shifts toward ever-smaller changes without any regard to the impact on high-level review quality or author effort.
                        4. They can mask underlying problems with performance evaluation and prioritization. This one is a little fuzzy but I’ve seen it in real life. If a team treats code review as a first-class responsibility on a cultural level, and the company rewards timely, thoughtful code reviews with promotions and raises, the “my 500-line change waited for review for over a week and then got rubber-stamped” problem basically never comes up. And as an added benefit, code quality and knowledge sharing goes up because people take code review very seriously. But on most teams, stacked PRs are a technical hack to cope with the organizational problem that code review isn’t truly valued and is a distraction from the work that is truly valued.
                        1. 1

                          Exactly this.

                          Anyone who hates stacked PRs is the because they aren’t using tools like git-machete. After I discovered this, it literally opened the workflow right up. It’s all I use now.

                          1. 1

                            I think 10 lines of code is really the issue here. It is generally speaking not large enough to tell a cohesive part of a narrative. I think the goal is generally around 100 to 200 lines, but that is heavily heavily context and complexity dependent.

                          1. 1

                            I love stacked PR like everyone else. It shows you step-by-step how a problem can be solved. But as time goes by, I increasingly find there are extra work for stacked PRs, and wondering if there are better ways. Let me give you an example of how I work:

                            1. I started by doing it end-to-end to verify for a sub-problem, or happy-path, or toy-example, whatever you call it, worked. This will be my “WIP” or “RFC” PR;
                            2. After people looked over, I start to refine that “WIP” PR, handling more edge cases, run fuzzers, make sure the dependencies I introduced is sensible, write more unit tests to cover my ass;
                            3. Break down what I have in 2 into several “stacked PRs”, maybe data models first, then executors / services, and then hook it up to the rest. These PRs will reference back to the previous “WIP” PR to give people an overview.

                            Reviewers obviously are extremely happy about this. However, moving from 2 to 3 is a lot of work (probably in itself would take a day to write good commit message, split files etc, without considering the back-and-forth incorporating review feedbacks). On the other hand, my mental model cannot go directly from 1 to 3 (skipping 2) somehow (it just doesn’t work for me, I cannot have a good idea everything is in the right place without seeing it in the right place first). Git probably could be one of the culprits why there are more work than needed though.

                            1. 2

                              Note: this depends a lot on what you consider a PR. When I say PR here, I am thinking about something like a GitHub PR with many commits in it.

                              I definitely prefer stacked diffs/stacked commits. They are a lot easier to manage with the right tools and each diff maps directly to a code review. With stacked PRs, you have multiple commits that make up 1 PR. This leads to more things to mess with/go wrong. It also makes reordering and splitting messier in my experience.

                              That all being said, stacked diffs simply are not supported that well on sites like GitHub. To get that, each PR would be a single commit that you always amend and then force push. That would be much worse than stacked PRs.

                              1. 1

                                It’s more work to produce stacked PRs, no question about it.

                                One thing I’ve found helps is to start off with a stack of empty changes from the get-go. For a given kind of coding task, I can usually take a pretty good guess at how I’ll ultimately want the stack to be organized. Then as I work on the problem, I am constantly switching branches so that I introduce a given piece of code in the right step in the sequence. I don’t always guess the stack structure exactly right, but it’s much easier to split a particular stack entry in two as soon as I see it’s needed than it is to wait until the end and split up the entire change all at once.

                                This gets much more feasible with a tool like Graphite that automates the “reparent all the child changes onto the latest version of the ancestor” process; otherwise you’re constantly having to run git rebase and it gets error-prone and hard to keep track of.

                                Even with good tools, you still have the added overhead of having to think about where in the stack to put a given piece of code. But doing it this way ends up being less total effort than splitting a change up after the fact, in my experience.

                              1. 2

                                Sounds interesting, but i definitely don’t grok it. I get the problems it points out, but definitely don’t get how it concretely fixes them. Probably just need to mess with it to understand better.

                                Also, being able to swap any commit order easily sounds like an anti-feature to me. I think history should not be edited generally speaking. Making that easy sounds concerning.

                                1. 4

                                  Also, being able to swap any commit order easily sounds like an anti-feature to me.

                                  This is not what Pijul does. If you want a strict linear ordering in Pijul, you can have it.

                                  1. 1

                                    Again, not claiming to grok Pijul at all, but isn’t that specifically the feature emphasized here: https://pijul.org/manual/why_pijul.html#change-commutation

                                    I get that you don’t have to use a feature even if it exists, but having a feature means someone might use it even if that is a bad idea.

                                    I probably just don’t understand the feature.

                                    1. 2

                                      The commutation feature means that rebase and merge are the same operation, or rather, that it doesn’t matter which one you do. Patches are ordered locally in each Pijul repository, but they are only partially ordered globally, by their dependencies.

                                      What Pijul gives you is a datastructure that is aware of conflicts, and where the order of incomparable patches doesn’t matter: you’ll get the exact same snapshot with different orders.

                                      You can still bisect locally, and you can still get tags/snapshots/version identitifiers.

                                      What I meant in my previous reply is that if your project requires a strict ordering (some projects are like that), you can model it in Pijul: you won’t be able to push a new patch without first pushing all the previous ones.

                                      But not all projects are like that, some projects use feature branches and want the ability to merge (and unmerge) them. Or in some cases, your coauthors are working on several things at the same time and haven’t yet found the time to clean their branches, but you want to cherrypick one of their “bugfix” patches now, without (1) waiting for the bugfix to land on main and (2) without dealing with an artificial conflict between your cherry-picking and the landed bugfix in the future.

                                      1. 1

                                        That makes a lot of sense. Thanks for the details!

                                1. 2

                                  This is a really good read of how easy it is to fall short especially when expanding in terms of breath and complexity.

                                  1. 3

                                    I really like this line:

                                    Look at that, you can see that image if you want by clicking.

                                    In my mobile browser, the link is just text. I can’t click it. So, no, links don’t just work when in plain text. Sure I can copy it, but that is less convenient.

                                    1. 7

                                      Will come back to fully read. Was longer than I expected. So far, it seems to be making arbitrary claims and distinctions though. Feels like a pretty unrooted opinion piece. Hoping this gets better when I get a chance to fully read it.

                                      1. 5

                                        I read it to the end. Unfortunately it doesn’t get better.

                                      1. 2

                                        I think it’s interesting to have a pretty simple, strict, pure language in the ML family. I found that talk enjoyable. It’s nice to see row polymorphism both for records and for sum types, and it might even work if their error messages are good enough.

                                        However, they seem to be making the catastrophic mistake of trying to write their own editor (with structural editing?). Focusing on a good LSP experience would be much better. This indicates to me that their priorities are wrong if they look to ship something useful that people might try (and maybe adopt). I hope they realize their mistake and make that a side project or something like that.

                                        1. 1

                                          Why is that a catastrophic error? Isn’t it, in the worst case, some sunk cost for something that gets tossed?

                                          1. 2

                                            Sure, it’s a grave mistake for them only if they care about adoption! Opportunity cost and all that :-). The first thing many people will ask is “is there a LSP server?”, and if their answer is “no, just use our editor” people will just shrug and lost interest.

                                            1. 1

                                              I would assume if the editor fails that badly, an LSP would be made. Also, I bet long term even if they don’t make the LSP, someone will.

                                              Anyway, adoption isn’t everything. If the editor works for them and is nice, why does it matter what others think?

                                        1. 3

                                          Interesting, looking forward to having a bit more documentation to read about this as it progresses.

                                          As an aside, is NoRedInk still invested in the Elm ecosystem?

                                          1. 6

                                            Heavily. Elm is for frontend. Roc technically can be used for frontend, but will probably never give as nice of an experience as elm. Elm is hyper focused on frontend web which makes it amazing for that use case. Roc is more targeted towards backend, system apps, cli, etc.

                                          1. 1

                                            Ooh it’s public!

                                            Edit: looks like it’s in Rust now aww

                                            1. 8

                                              looks like it’s in Rust now aww

                                              Look at the last entry in the FAQ. The compiler has always been written in Rust, the runtime is written in Zig. To see some of the Zig source code, look in crates/compiler/builtins/bitcode.

                                              1. 2

                                                would be interesting to read the story behind the rust rewrite

                                                1. 8

                                                  As far as I know, the compiler has always been written in Rust! You can write platforms in whatever language you like, though.

                                                  1. 1

                                                    oh, interesting; when they first announced the language i assumed it started life as a straight-up fork of the haskell-based elm compiler

                                                    1. 5

                                                      Always rust, though parts of it were translated very directly from the elm compiler. So some sections the code may be quite similar.

                                              1. 4

                                                I feel the core issue with the article is that it is asking if we can “replace” C. When you look across many languages in programming history, they don’t tend to die, they don’t tend to be replaced. Slowly, they will be used less as other languages are picked for x or y reason, but that is very different than replacing the language as a whole. Replacing C as a whole will probably never happen. Picking up a project her and a project there, that is more possible, but C will likely live on for an extremely long time.

                                                There are counter examples, like objective c to swift, but that is in a very specific scope with a lot of company control.

                                                1. 33

                                                  The problem is that C have practically no checks, so any safety checks put into the competing language will have a runtime cost, which often is unacceptable. This leads to a strategy of only having checks in “safe” mode. Where the “fast” mode is just as “unsafe” as C.”

                                                  So, apparently the author hasn’t used Rust. Or at least hasn’t noticed the various benchmarks showing it to be capable of getting close to C performance, or in some cases outpacing. Also that because of Rust’s safety, it’s much easier to write working parallelised code v.s. C and so you can get a lot of improvements that way.

                                                  I’ve written a lot of C over the years (4 years of embedded software PhD), and now never want to go back now I’ve seen what can be done with Rust.

                                                  1. 12

                                                    The author notes that he does not consider Rust a C alternative, but rather a C++ alternative:

                                                    Like several others I am writing an alternative to the C language (if you read this blog before then this shouldn’t be news!). My language (C3) is fairly recent, there are others: Zig, Odin, Jai and older languages like eC. Looking at C++ alternatives there are languages like D, Rust, Nim, Crystal, Beef, Carbon and others.

                                                    Now, you could argue that it’s possible to create a C-like language with a borrow checker. I wonder what that would look like.

                                                    1. 20

                                                      C++ is a C alternative. The author dismisses rust without any explanation or justification (I suspect it’s for aesthetic reasons, like “the language is big”). For a lot of targets (non embedded), rust is in fact a valid C alternative, and so is C++.

                                                      1. 8

                                                        For a lot of targets, especially embedded, Rust is an amazing C alternative. Granted, currently it’s primarily ARM Cortex M that has 1st class support but I find your remark funny how from my perspective embedded is probably the best application of Rust and its features. Representing HW peripherals as type-safe state machines that won’t compile if you missuse them? Checked. Concurrency framework providing sane interrupt handling with priorities that is data race and deadlock free without any runtime overhead that won’t compile if you violate its invariants?. Checked. Embedded C is a joke in comparison.

                                                      2. 13

                                                        Adding a borrow checker to C requires adding generics to C, at which point it would be more C++-like than C-like. The borrow checker operates on types and functions parameterized by lifetimes, so generics is not optional, even if you do not add type generics.

                                                        1. 6

                                                          Also, not adding type generics is going to make the safety gained from the borrow checker a lot less useful, because now instead of writing the unsafe code for a Vec/HashMap/Lock/… once (in a library) and using it a gazillion times, you write it once per type.

                                                          1. 3

                                                            Isn’t this more or less what Cyclone is?

                                                            1. 4

                                                              Yes it is. It is why Cyclone added polymorphic functions, polymorphic data structures, and pointer subtyping to C, check Cyclone user manual. Not because they are cool features, but because they are required for memory management.

                                                            2. 1

                                                              Even though types and functions are parameterized by lifetimes, they do not affect codegen. So it should be possible to create a “C with borrowchecker”.

                                                              1. 1

                                                                I don’t understand how codegen matters here. Clang and rustc share codegen… If C-like codegen (whatever that is) gives C-with-borrow-checker, C-with-borrow-checker is rustc.

                                                                1. 1

                                                                  ops, I guess I should’ve said “lifetimes do not affect monorphisation”

                                                                  1. 1

                                                                    This is still a mysterious position. You seem to think C++-ness of templates comes from monomorphisation code generation strategy, but most would say it comes from its frontend processing. Monomorphisation is backend implementation detail and does not affect user complexity, and as for implementation complexity it is among the simpler one to implement.

                                                                    1. 1

                                                                      the whole point was that generics are not needed for a language to have a borrowchecker.

                                                                      if you call a generic function twice with different types, two function code is generated. if you call a generic function twice with different lifetimes, only one function code is generated.

                                                                      borrowchecker is anmotation + static analysis. the generated code is the same. the same is not true for generics or templates.

                                                                      1. 1

                                                                        If you think this, you should write a paper. Yes, borrow checker is a static analysis. As it currently exists, it is a static analysis formulated to work on generics. As far as I know, no one knows how to do the same without generics.

                                                            3. 5

                                                              There was some recent discussion on Garnet which is an attempt to make a smaller version of Rust.

                                                              1. 5

                                                                This is the correct position on Rust.

                                                                1. 32

                                                                  I disagree. To me Rust is a great C replacement, and Rust is incompatible with C++ both technically and philosophically. I’ve used C for two decades. I’ve never liked C++, but really enjoy Rust. I’ve written a C to Rust transpiler and converted projects from C to Rust.

                                                                  C programs can be converted to Rust. C and Rust idioms are different, but language features match and you can refactor a C program into a decent Rust program. OTOH C++ programs can’t be adapted to Rust easily, and for large programs it’s daunting. It’s mostly because Rust isn’t really an OOP language (it only has some OOP-like syntax sugar).

                                                                  I think people superficially see that both Rust and C++ are “big” and have angle brackets, and conclude they must be the same. But Rust is very different from C++. Rust doesn’t have inheritance, doesn’t have constructors, doesn’t have move constructors, doesn’t use exceptions for error handling. Rust’s iterator is a completely different beast than C++ iterators. Rust’s macros are closer to C++ templates than Rust’s generics. Lots of Rust’s language design decisions are at odds with C++’s.

                                                                  Rust is more like an ML language with a C subset, than C++.

                                                                  1. 9

                                                                    To me Rust is a great C replacement

                                                                    When you say “replacement”, what do you mean, exactly? For example, could C++ or Ada be great C replacements?

                                                                    I think some of the disagreements about Rust - and the whole trope about Rust being a “big” language - come from different people wanting different things from their “C replacement”. A lot of people - or maybe just a particularly vocal group of people on Internet forums - seem to like C not just because it can be used to write small, fast, native code, but because they enjoy the aesthetic experience of programming in C. For that sort of person, I think Rust is much more like C++ than C.

                                                                    Rust is very different from C++. Rust doesn’t have inheritance, doesn’t have constructors, doesn’t have move constructors, doesn’t use exceptions for error handling.

                                                                    Modern C++ (for some value of “modern”) doesn’t typically have inheritance or exceptions either. I’ve had the misfortune to program in C++ for a couple of decades now. When I started, it was all OO design - the kind of thing that people make fun of in Enterprise Java - but these days it’s mostly just functions in namespaces. When I first tried Rust, I thought it was just like C++ only I’d find out when I screwed up at compile-time rather than with some weird bug at run-time. I had no trouble with the borrow checker, as it just enforced the same rules that my team already followed in C++.

                                                                    I’ve never liked C++ because it’s too complicated. Nobody can remember the whole language and no two teams use the same subset of the language (and that applies to the same team at two different times too, as people leave and join). People who program alone, or only ever work in academia in small teams, might love the technical power it offers, but people who’ve actually worked with it in large teams, or long-lived teams, in industry, tend to have a dimmer view of it. I can see why people who have been scarred by C++ might be put off by Rust’s aesthetic similarity to it.

                                                                    1. 2

                                                                      I’ve never liked C++ because it’s too complicated. Nobody can remember the whole language and no two teams use the same subset of the language (and that applies to the same team at two different times too, as people leave and join).

                                                                      I think that’s a correct observation, but I think that’s because C++ standard library and the language itself has over 3 decades of heavy, wide industry use across much of the depth and breadth of the software development, generating demands and constraints from every corner of the industry.

                                                                      I do not think we had ‘solved’ the problem of theoretically plausible definition of the minimal, but sufficient set of language features + standard library features, that will be enough for 30+ years of use across everything.

                                                                      So all we have right now is C++ as a ‘reference stick’. If a newbie language compares well to that yardstick, we hale it. But is that the right yard stick?

                                                                      1. 1

                                                                        I definitely don’t use C for an “aesthetic experience” (I do not enjoy aesthetics of the preprocessor, spiral types, tediousness of malloc or header files). I would consider C++ also a C replacement in the same technical sense as Rust (native code with minimal runtime, C ABI, ±same performance), but to me Rust addresses C’s problems better than C++ does.

                                                                        Even though C++ is approximately a C superset, and Rust is sort-of a C superset too, Rust and C++ moved away from C in different directions (multi-paradigm mainly-OOP with sugar vs ML with more explicitness and hindsight). Graphical representation:

                                                                        Rust <------ C ----> C++
                                                                        

                                                                        which is why I consider Rust closer to C than C++.

                                                                        1. 2

                                                                          Sorry for the obvious bait, but if you don’t like C, why do you use it? :-). If you’re looking for a non OOP language that can replace C, well, there’s a subset of C++ for that, and it’s mostly better: replace malloc with smart pointers, enjoy the RAII, enjoy auto, foreach loops, having data structures available to you, etc.

                                                                          1. 5

                                                                            In my C days I’ve been jealous of monomorphic std::sort and destructors. C++ has its benefits, but they never felt big enough for me to outweigh all the baggage that C++ brings. C++ still has many of C’s annoyances like headers, preprocessor, wonky build systems, dangerous threading, and pervasive UB. RAII and smart pointers fix some unsafety, but temporaries and implicit magic add new avenues for UAF. So it’s a mixed bag, not a clear improvement.

                                                                            I write libraries, and everyone takes C without asking. But with C++ people have opinions. Some don’t like when C++ is used like “C with classes”. There are conundrums like handling constructor failures given that half of C++ users bans exceptions and the other half dislikes DIY init patterns. I don’t want to keep track of what number the Rule of $x is at now, or what’s the proper way to init a smart pointer in C++$year, and is that still too new or deprecated already.

                                                                  2. 1

                                                                    Now, you could argue that it’s possible to create a C-like language with a borrow checker. I wonder what that would look like.

                                                                    Zig fits in that space, no?

                                                                    1. 8

                                                                      Zig is definitely more C-like, but it doesn’t have borrow checking. I think Vale is closer. There’s MS Checked-C too.

                                                                      But I’m afraid that “C-like” and safe are at odds with each other. I don’t mean it as a cheap shot against C, but if you want the compiler to guarantee safety at compilation time, you need to make the language easy to robustly analyze. This in turn requires a more advanced static type system that can express more things, like ownership, slices, and generic collections. This quickly makes the language look “big” and not C-like.

                                                                      1. 7

                                                                        I don’t think Zig has borrow checking?

                                                                        1. 7

                                                                          It doesn’t. As I understand, its current plan for temporal memory safety is quarantine. Quarantine is a good idea, but if it was enough, C would be memory safe too.

                                                                          Android shipped malloc with quarantine, and here is what they say about it:

                                                                          (Quarantine) is fairly costly in terms of performance and memory footprint, is mostly controlled by runtime options and is disabled by default.

                                                                    2. 2

                                                                      Ada has made the same claims since 1983, and hasn’t taken over the world. Maybe Rust will do better.

                                                                      1. 1

                                                                        I think the big point here is that the author is talking in hypotheticals that don’t always pan out in practice. Theoretically, a perfectly written c program will execute faster than a rust program because it does not have safety checks.

                                                                        That being said, in many cases those limited safety checks actually turn out to be a miniscule cost. Also, as you mentioned rust may unlock better threading and other performance gains.

                                                                        Lastly, i think it is important to note that often software architecture, algorithms, and cache friendliness will matter much much more than rust vs c vs other low level languages.

                                                                      1. 1

                                                                        What’s the performance cost though? Seems like something that should be easily fixable with typescript and ensuring the data you stringify matches a valid JSON type.

                                                                        1. 1

                                                                          You’re correct that TypeScript could catch some of the same problems, specifically invalid JSON types (bigints, functions, symbols, classes, etc.). Except for a few ones (NaN and Infinity cannot be literal types in TypeScript, i.e. could not be detected with TypeScript only).

                                                                          However, it would not be able to detect (nor fix) most reasons why a value might throw with JSON.serialize(): circular values, infinite toJSON(), very large fields, exceptions inside toJSON(), getters or proxies, etc.

                                                                          Also, it would only detect those issues at build-time, which would not work with dynamic values.

                                                                          That being said, if the values are not dynamic and you’re only concerned with invalid JSON types (not exceptions thrown by JSON.serialize()) then I agree that TypeScript would be a better fit there. Thanks for pointing that out!

                                                                          1. 2

                                                                            Thanks for the explanation. I have not done much web dev in a long time, this extra context makes a ton of sense.

                                                                            1. 1

                                                                              Bigints are not invalid JSON; JSON declares no limitations on the length of numeric values.

                                                                              1. 1

                                                                                In JSON itself, you’re right. Integers in a JSON string do not have a length limit.

                                                                                However, I was referring to BigInt as a JavaScript type. BigInts make JSON.serialize() throw.

                                                                                It is possible to circumvent this by serializing them to strings first via bigint.toString(), then parsing them back from a string too. Or do the same with floats. However, as its own type (without conversion to another type), they are not valid JSON otherwise.

                                                                                1. 1

                                                                                  That is a bug in JS’s JSON encoder, not in JSON.

                                                                          1. 3

                                                                            What is the big cost that flat buffer is paying that Karmen is not? Just trying to understand where the performance gains are coming from, at what cost, and if they scale.

                                                                            1. 1

                                                                              Flatbuffers is known to have one more indirection to enable smooth schema evolution (i.e. you can mark fields as deprecated, these won’t show up in both generated code, and as encoded payload, doesn’t pay penalty for these fields). This seems just dumps fields into a memory buffer without that extra layer of indirection. For a well-tuned language / generator, it can perform well, but you lose:

                                                                              1. Cannot remove fields, only append new fields;
                                                                              2. Having a lot of empty optional fields doesn’t automatically translates to size saving (flatbuffers indirection allows default / optional fields).

                                                                              I am also not sure how they handle schema evolution for table contains table cases (table A contains table B, when table B appending more fields resulting in table A’ and table B’, how can you downgrade from A’ to A?).