Cooperative C++ Evolution – Toward a TypeScript for C++
herbsutter.com
herbsutter.com
The alternate approach (which is extremely popular unfortunately), is to throw it all out and rebuild everything from scratch. I guess it's fun and exciting, which attracts developers, but it takes a long time to achieve any level of maturity and are hard to sustain (the people who are in it for the fun and excitement will move on before too long).
This happened in TypeScript when they added support for an early version of decorators and now the TC39 version (which is still only Stage 3) is just different enough to cause issues.
I think the Typescript creators themselves learned a lesson with decorators and enums which is why we haven’t seen other JS language proposals get added until they’re actually in the process of being adopted (e.g. matchers).
So... like the pipeline operator? Pretty much given up on that being included now. They can't make up their minds over two competing syntaxes (and, FWIU, it has taken them years to decide).
1. Enums are one of the few TypeScript features that isn't a type annotation that can simply be erased. Enums emit code and don't have an equivalent JS feature.
2. Const enums are unsupported by some bundlers/build tools, and so people try to use them and then got burned at build time.
3. The use cases covered by enums are often better served by union types.
None of the above is necessarily fatal for the feature. Certainly people used to enums in other languages still like them. But all 3 combined and the general recommendation these days ends up being just don't use them.
Supporting const enums will, by necessity, greatly reduce the maximum performance that toolchain can achieve, because you have to evaluate the entire project to tell whether `Foo.BAR` should be left alone or replaced with a constant defined elsewhere. And in the worst case of an ambient declaration, "elsewhere" could be any file in the project.
This was only really added to appease Google and Angular 2.0.
You already have a compiler. Just make it emit binary-compatible code for the new dialect. You have modules now so you don't have the problem of supporting mixed-dialect headers.
Herb Sutter proposes a bunch of stuff to WG21 (the C++ committee) and most of it goes nowhere. We're not talking about two proposals here, maybe a dozen is closer. Most of that stuff from many years is in Cpp2 because it's Herb's language so they can't stop him.
The one thing Herb proposed in that time which got into C++ was the spaceship operator <=> which is basically like Rust's PartialOrd trait, you write one operator for your type, and for any pair of values (this & that) you decide whether this was Greater, Equal or Less than that -- or none of the above. As a result the compiler can implement all the obvious comparison operators like <= or == or > using that one piece of code you wrote, and it's easily able to be consistent. This is very nice, and it's in C++ today, but it's also in Cpp2 of course.
Why waste time on forking clang when you can both hash out your language design and have a highly functional prototype right away?
If it's optional it will be omitted in the hard parts, for instance if your favorite library doesn't support it.
Are you aware of any systematic review that shows evidence that C++ is safer than C?
The rate of safety defects between major C and C++ projects appears similar at first glance, and both way worse than managed languages or rust.
C requires many times more lines of code than C++ to do the same thing. AFAIK there is considerable academic evidence that bug counts roughly scale with lines of code, so languages that are precise and concise naturally reduce total defect rates. Minimizing defects requires maximizing expressiveness. The ratio of LoC between languages to express the same thing is not constant, it depends on the application.
The kinds of bugs I see in C++20, given the type of software I work on, are almost entirely the same kinds of logic and behavioral bugs that occur in every language. This is why Rust isn't as popular as one might expect for systems software: memory safety bugs are not a thing for many code bases, and Rust requires many more lines of code compared to C++20. I am sure Rust will become more economical over time but for now it is pretty verbose and has pretty limited metaprogramming functionality.
C++20 is remarkably safe and concise if you take full advantage of the type system.
Against what sort of laughably low bar are you measuring to make C++ "remarkably" safe ?
This is a language which delights in deliberately adding more footguns, on the rationale that well, it's less safe so surely it'll be faster right? No need to measure, no need to investigate what actual performance optimisations might somehow be available if we allowed the dangerous behaviour, no, just mandate it and YOLO.
C++ 20 introduces std::span. Now, std::span is basically a slice type, Rust's [T], and to some extent it's remarkable that C++ didn't have a slice type, but that's C++ for you. What's fascinating is that in 2020 I remind you they standardized a type which deliberately has no safe way to use it. It was proposed as a safe type, and WG21 stripped out the safety on the rationale that now it's faster (see above) then rejected all attempts to add the usual half-arsed C++ safety features to the type now that it wasn't safe by default.
Let me quote a C++ proposal paper (this isn't some hit piece from Rust fanatics, it's a serious proposal to the ISO working group) P2821 on std::span:
"Ultimately, this becomes a stereotypical example of how C++ traditionally handles safety. this example gets to be pointed at for years/decades to come. All of this could have been avoided"
The metaprogramming facilities of C++ are strong enough now that there is little that can't be customized without macros in a way that is nearly transparent.
Use templates instead of macros, RAAI instead of gotos, namespaces instead of prefixes, bounded checked strings and arrays instead of raw pointers, new instead of error prone sizeof with malloc(),...
How come? Surely there would be fewer memory leaks in a code base with proper RAII than in C code with malloc and free all over the place.
Linux kernel (C): https://www.cvedetails.com/product/47/Linux-Linux-Kernel.htm...
Chrome (C++): https://www.cvedetails.com/product/15031/Google-Chrome.html?...
There’s some variability year over year, but if anything C appears to have a slight advantage over C++ in terms of memory corruption (840 vs 1004), with essentially the same number of overflow errors (322 vs 328). There is no comparable rust project, but initial evidence from the asahi gpu drivers hints that memory corruption errors are fundamentally eliminated.
This is obviously not accounting for confounding factors, hence my request for any peer reviewed evidence for the security claim. Until then, the facts don’t seem to be supporting it.
Feel free to provide evidence for other cross-comparable c & c++ codebases if you have.
Because that’s the only thing holding back some languages right? If only the programmers using them would get good, use static analysis tools then bugs would be eliminated.
Linux compiles and runs on many more architectures and hardware configurations than chrome, and it supports a frankly ridiculous number of peripherals up to and including the most complicated gpu accelerators ever made.
Chrome is indeed complex, but on what do you base your “vastly more” assertion?
Bounds checking instantly eliminates buffer overflow related unsafety, but not having it as the default is not good.
You also want to hoist bounds checks and do them early. Often, one check at loop entry can eliminate the bounds checks for each iteration. But the language has to allow an early fail.
C++ was itself the TypeScript model applied to C.
And, in fact, many of the things people hate about C++ are the result of exactly the kind of compromises you have to make to apply a TypeScript-like model to language evolution. C backwards compatibility is what made C++ successful originally but is now one of the main things that makes C++ crappy today.
> "Dart plan" - Competitive/successor 10x improvement
> New creation
> Limited interop, relies on wrapping/marshaling/thunking...
> Competes with standard (e.g., different modules, different generic constraints)
> Evolves independently of standards committee - far fewer design constraints
> Needs to bootstrap a new ecosystem
> "Typescript plan" - Cooperative/compatible 10x improvement
> Every .js file is a valid .ts file, add 1 class and see benefit
> Lowers to standard .js, 100% seamless compat with all JS libraries
> Cooperates with the standards committee (ECMAScript)
> Brings evolution proposals to standards committee
> Leverages entire existing ecosystem - works with all JS implementations & tools
Carbon is an example of the "Dart plan". Some quotes from Carbon's "Interoperability philosophy and goals" page (my emphasis):
> The C++ interoperability layer of Carbon allows a subset of C++ APIs to be accessed from Carbon code, and similarly a subset of Carbon APIs to be accessed from C++ code.
> The result is that it will often be reasonable to directly expose a C++ data structure to Carbon without converting it to a "native" or "idiomatic" Carbon data structure. Although interfaces may differ, a trivial adapter wrapper should be sufficient.
> There should be support for most idiomatic usage of advanced C++ features. A few examples are templates, overload sets, attributes and ADL.
> Non-goals
> Never require bridge code
> Support for C++ exceptions without bridge code
https://github.com/carbon-language/carbon-lang/blob/trunk/do...
Carbon is following a plan much more analogous to Kotlin -- we even say that on our site very explicitly.
The "subset" of C++ APIs you emphasize is only about there existing some long-tail esoteric parts of C++ that may be used rarely enough to not worry about. Everything that people use we'll need to support here. We think about interop constantly and are designing it into every aspect of the language.
The whole point of Carbon is to integrate into and re-use an existing ecosystem of software written in C++. It's as far from the Dart approach as it can get without literally being a TypeScript style approach.
Ultimately, this dichotomy doesn't help discuss Carbon. I think it is useful for looking at Rust (until/unless Crubit or something similar radically changes its interop story), Go, and many other languages. But not Carbon IMO. It loses all of the important nuance. And there are important and meaningful differences from TypeScript's approach that we've talked about since announcing Carbon, but they don't make it anything like Dart's strategy.
The construct can be useful without being the end-all answer to what should be done and I would definitely advice you to watch the full talk if you haven't yet. And the consider how each point may apply or not apply to Carbon. Don't just dismiss it on the notion that Carbon is different from Dart.
Actually, I'd encourage you to reach out to Herb Sutter and ideally meet up in person to discuss the matter. Your goals are aligned in many ways and while you have different approaches a lot of good can come out of sharing ideas.
Edit: of course this is about cppfront :)
Android, ChromeOS and Fuschia already use Rust, and Chrome is in the process of getting the first Rust libraries integrated.
deeply incorrect.
Like, if Google "never approves" why are they designing a course to teach this, and paying outsider trainers to teach their staff ?
Just like Typescript to JavaScript, the syntax is an evolution of what already exists, not a completely different syntax.
I have no doubts about the qualifications (or even intentions) of the author, but one feels that a language meant for serious things should have an open implementation and/or standard. Of course, I realize this may not align with Baxter's goals, but it is going mean ~0 adoption.
> [new_decl_syntax]
There's too much pining for Rust syntax and non-conservative changes or additions that aren't consistent with the rest of C++ syntax.
It repeats C++'s mistake of having too many non-convergent features and adds as much new syntax on top as C++ already has. This is a problem because to be able to read code, you have to know all language features at least superficially.
Instead of doing what plain C++ has been trying to do and stabilize on a smaller feature set and (at least verbally) deprecate legacy cruft, this is going the exact opposite route.
Sorry, but this is DOA.
From my point of view, Cpp2 gets sold this way, due to the conflict of interest that it is being proposed by the ISO C++ chair, and naturally the story can't be that it is yet another wannabe C++ replacement like all the other ones.
I think C and C++ are two of the most interesting languages out there. I think it's rarely worth bothering rewriting existing C/C++ software in something else for the sake of safety. I think there's an appreciable amount of applications where C/C++ are still justified. I don't think this will end up being much different than the last two decades worth of attempts to put some guardrails on them.
TS is relevant because JS has severe functionality deficiencies and you've historically had no other choices when writing for web browsers. C++'s deficiencies are less with the functionality of the language itself and more with properly and safely using it, and you're very rarely forced to use it.
However, the problems abound when you have less experienced devs in a less structured environment and working with legacy C or C-style code, the latter often being the reason for why C++ was used in the first place. For this, something like cpp2 can be revolutionising as it makes it easier to write correct code and provides mechanisms to disable many of the unsafe patterns.
Herb's Cpp2 announcement on the other hand was clear from the outset that Cpp2 is not safe. It's aimed to be fifty times safer but that seems untestable, maybe even meaningless.
The immediate trigger for Carbon was P2137. Basically P2137 says "C++ should prioritise performance over safety, safety over compatibility" and WG21 is like "No, absolutely not". That's a set piece, nobody was astonished this happened, but getting it down on paper avoids executive argument. Google could have spent six years convincing non-expert people that C++ really isn't going to deliver, or it could secure a piece of paper which says they don't even want to and eliminate that whole discussion.
Apple have pretty clearly settled on Swift. I'm not convinced they can write all their bare metal stuff in a Swift dialect, or that they'll be able to in time to not need anything else long term, but clearly the vast bulk of new work at Apple will trend to Swift. Apple are quite good at single minded and "Write all new code in Swift" is a single minded idea. I have no idea why anybody would buy from a company like that, but they're very popular so what do I know.
Microsoft are much less single-minded. I doubt they could settle on Swift (or Rust, or even say sticking with C++) as a company wide policy even if they wanted to. But equally they're not interested in finding themselves as "last man standing" for C++.
To be fair, Rust does have an unsafe superset and the interaction between "safe" Rust and unsafe code is quite non-trivial. It's not safe to call any part of safe Rust from an unsafe context unless the extra preconditions that safe Rust expects (as documented in the 'Rustonomicon') are proven to hold. This means that, e.g. much of the Rust stdlib and 'core' code might not be practically usable from unsafe code written in either Unsafe Rust or C/C++, whereas the idiomatic C++ counterpart might be. There is some effort underway to fix this where it matters, but these are not easy questions to address.
First off, unsafe Rust is not meant for writing application logic. It should be isolated within data structures, algorithms, or other abstractions exposing safe APIs.
Secondly, what you say about calling into safe Rust from unsafe contexts just doesn't sound correct. It seems like by "extra preconditions" you're talking about the requirements placed on references: that they must be initialized, non-null, and for &mut, unaliased. But these aren't requirements for calling into safe code, these are requirements for dereferencing raw pointers.
You might also be talking about the issues around moveability and Pin. But these are also not about calling into safe code, but about representing your type correctly (making certain actions only possible when pinned or whatever).
And then you talk about std and view not being practically usable from unsafe Rust, and this just doesn't align with my experience at all.
It's really not that hard to get unsafe code right (Miri is an awesome tool), and it's also not difficult to avoid unsafe code entirely if you're not comfortable with the requirements.
You can use the unsafe read() and write() (and similar) functions to do things with raw pointers that would clearly involve dereferencing in C/C++ (including working with aliased pointers or 'pinned' data or writing to uninitialized memory), so I don't think this is correct from a C/C++ point of view. What Rust calls dereferencing is explicitly driven by the requirements placed on safe code; the two are effectively one and the same.
It's extraordinarily difficult to build a significant C++ codebase without segfaults and UB. You end up wasting a huge amount of time debugging that stuff.
Any time I've lost fighting Rust's borrow checker has easily been paid off by not having to debug segfaults.
> cppfront is on the TypeScript plan:
> full seamless interop compatibility with ISO Standard C++ code and libraries without any wrapping/thunking/marshaling,
> full ecosystem compatibility with all of today’s C++ compilers, IDEs, build systems, and tooling, and
> full standards evolution support with ISO C++, including not creating incompatible features (e.g., a different concepts feature than C++20’s, a different modules system than C++20’s) and bringing all major new pieces to today’s ISO C++ evolution as also incremental proposals for today’s C++.
I should have just called the variable oldC.
Edit: found the Github repository.[1] "Where's the documentation? I'm not posting much documentation because that would imply this project is intended for others to use — if it someday becomes ready for that, I'll post more docs."
The examples don't have comments.
[1] https://github.com/hsutter/cppfront#wheres-the-documentation
The title strikes me as ironic that it’s talking about a typescript on top of a typescript.
Others in this thread already went into more details about this, but it doesn't help me much as long as I don't have an idea what the basic difference between both approaches is.
Can someone ELI5 the gist of both approaches?
TypeScript, on the other hand, has always set its goal to be a superset of JS and to transpile to JS only. This means a more familiar syntax, as well as (practical, IMO) design choices that ensure higher compatibility with the existing JS ecosystem. There is no TypeScript without JavaScript. Its development coincided with that of VSCode, and arguably is one the main facilitators for the latter's feasibility.
I have to say Microsoft's grand plan of VSCode and TypeScript has to be one of the most astonishing software revolution in this decade. The foresight and acuity of Erich Gamma and Anders Hejlsberg is just amazing.
One of his projects at Microsoft was J++ which is basically own-brand Java. Doubtless that's great material for learning to design C# but nobody is going to claim J++ was a success.
VM integration with host OS, J/Direct (P/Invoke), events, COM integration, WFC (Windows Forms).
TypeScript deliberately takes a "good enough" approach to improving JavaScript, instead of designing an ideal but incompatible approach. For example, its handling of function parameter bivariance (https://www.typescriptlang.org/docs/handbook/type-compatibil...) is unsound but works much better with the existing JavaScript ecosystem. By contrast, a more academic functional programming language would guarantee a sound type system but would be a huge shift from JavaScript.
By analogy, Herb Sutter is arguing that something like the C++ Core Guidelines (https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines), with tooling help in this new Cpp2 syntax, can bring real improvements to safety. Something like Rust's borrow checker would bring much stricter guarantees, backed by academic research and careful design, but would be incompatible and a huge adjustment.
"The goal is to address existing problems in C++ by embracing the solutions and guidance we've already de facto adopted, and to have to explain less rather than more." [1]
I found a repo with some examples of the syntax [2]. For example:
myclass : type = {
data: int = 42;
more: std::string = std::to_string(42);
// method
print: (this) = {
std::println("data: (data)$, more: (more)$");
}
// non-const method
inc: (inout this) = data++;
}
main: () = {
x: myclass = ();
x.print();
x.inc();
x.print();
}
--1: https://github.com/hsutter/cppfront/wiki/Design-note:-Cpp2-n...
On the other hand, carbon has been very actively developed since its announcement, which also claims to be a typescript-for-c++. Now it also wants to be a memory safe: https://www.youtube.com/watch?v=1ZTJ9omXOQ0
I'm forced to learn rust but I hope carbon will take off ASAP so I can be more productive and my (carbon) code can interface with c/c++ code easier than rust.
Is that true? Seems like he is actively working on it:
next year 0.1 will be usable, 1.0 is about 3 years away, sigh, back to my rust fight
Now, how willing WG21 and the compiler vendors are open to the idea, is anyone's guess.
https://github.com/search?q=repo%3Achromium%2Fchromium+memor...
A few leaks fixed per month is nothing. Further there is no major language promising no leaks once you include things like retained references to garbage and reference cycles. Leaks are one thing modern C++ solves pretty well.
It's not indicative of anything average in the real world.
class A {};
std::unique_ptr<A> array(new A[256]);
It's very easy to produce innocuous-looking code that contains leaks. (And yes, I know that make_unique solves the problem here.)Compilation via another language is an implementation detail.
Also, I think that promoting a new programming language as "TypeScript for C++" is a marketing move to try to force their way into a market share in spite of the language's merits.
Visceral disgust reaction.
The only way typescript is going away is if JS essentially incorporates it.
The only things people don't like about it is that you need some tooling and a build step.
However, with its popularity, ts is built in to a lot of things, so the tooling usually isn't a big burden (particularly to get started).
And it's viable in a lot of cases these days to forgo the build step and instead use JS with typescript type annotations. (That might be what you heard about. That's really still using typescript, though.)
Wrong. Many people dislike many things about Typescript.
https://levelup.gitconnected.com/the-inevitable-decline-of-t...
https://medium.com/codex/why-are-javascript-pros-saying-good...
https://gomakethings.com/ditching-typescript-for-javascript/
https://www.reddit.com/r/sveltejs/comments/12cyady/are_you_l...
There are plenty more examples of why people tried typescript and went back to javascript. It's definitely not only because of a build step, but also "type gymnastics", and other code bloat.
The big problem is that it still requires transpilation, which can set up all sorts of shitty traps. Not to mention that the Javascript ecosystem in general is a horrendous mess, which Typescript on its own can’t fix.
Yet, most of the code people write has statically defined types.
Anyway, the whole point of JavaScript is that it runs on the browser. Outside of that, it has no strong points. Even though most of them are not weak enough to immediately abandon the language, its type system is one of the weakest.
If you're just glueing things together between APIs, then JavaScript is fine, and static typing would just get in the way.
Don't believe in everything you hear.
It's weird to refer to these arguably bad web technologies to market an unrelated initiative.
Dart aimed to replace JavaScript completely and isn’t very compatible with it, leading to issues like not being able to leverage the existing library ecosystem. While the Typescript approach enhances the base language instead of replacing it and is still compatible with existing libraries.
When looking at language adoption, the Typescript approach seems to have worked a lot better than the Dart approach. If it wasn’t for Flutter, Dart would probably be irrelevant by now and Typescript is now pretty much everywhere where JavaScript is.
Another successful-ish example of the Typescript plan is Kotlin, which was originally designed as an improved Java, fully compatible with the existing ecosystem.
So I can see where the author comes from when trying to do the same thing for C++.
I guess that's smart positioning. But a big problem Herb has is that the real alternative isn't any of those 2022 C++ Successor Languages. Ultimately your project will decide whether to stick with C++ or go to a language like Rust or maybe Swift. "Alternatives to C++" that might be finished some time in the next five years are irrelevant to that decision. It's like arguing that you're the best Chicago-style pizza joint in Naples. Who cares?
And when comparing against Rust or Swift, we're back to Herb's ten year head start problem also mentioned in this talk. Rust is what, 18 months away from its tenth anniversary of Rust 1.0? Swift is even closer.
In that sense, cpp2 might get to the point of being usable while having perfect interop with C++ (as in, you can use any C++ library trivially, and you can mix it with C++ in a codebase on a per file, maybe even within files, easily) before Rust or swift or Carbon.
It is also a reason why if you want modern C++ support on Apple devices, now you need to install clang yourself.
As Apple keeps focusing on Swift, with Objective-C for its companion.
It's not really a "web technology" at all. It's a language which transpiles to standard JavaScript. The only real web-geared parts of TypeScript are the included (optional) DOM typings.
JavaScript's only reason for existence is web browsers. If you're not doing web stuff do yourself a favour and use something else.
Yet, as with the parent comment, you haven't divulged your reasoning behind this statement. If you're going to make such a broad statement, at least place your rationale beside it.
> JavaScript's only reason for existence is web browsers.
JavaScript has seen broad adoption throughout the industry, for servers (Node.js, Deno, Bun), IoT (DeviceScript), browsers (duh) and mobile apps (NativeScript, React Native, etc.). It has its weak points, but downright dismissing it (again, without any rationale) is unfair and disingenuous IMO.
You don't build static typing on top of dynamic typing; it's silly. You do it the other way around.
If you're going to be outside of the browser, just use Python, which is not only somewhat similar but also the world's most popular programming language.