Note if you are a title-only-before-commenting reader that this is not a generalized apologia for Go, but specifically for the case of porting the Typescript compiler, so reciting the Litany Against Go in a general sense is not really on topic.
(The Litany, naturally, is:
I must not Go.
Go is the mind-killer.
Go is the little-death that brings total obliteration.
I will face Go.
I will permit it to pass over me and Go through me.
And when it has gone past, I will turn the inner eye
to see its path.
Where the Go has Go'ed there will be nothing.
Only I will remain.
)
(OP author here) Lots of people reading too much into the tea leaves here; this is just a matter of picking the best tool for this particular task, and our task (porting a JS codebase to the fastest available native target that still works in terms of not altering program structure as part of the port) is pretty unusual as far as things go
I would also recommend reading kdy1's observations when faced with the same task: https://kdy1.dev/2022-1-26-porting-tsc-to-go . The only caveat I'd add is that I can't tell where the 62x measurement in that post came from. Our own experiments doing Rust and Go implementations showed them within the margin of error, with certain phases being faster in one vs the other.
Since you wrote this it looks like Anders replied [1] to one of the threads.
I have to agree with the sentiment that is a success story that the team is allowed to use the best tool for the job, even if it suffers from "not built here".
This is really healthy and encouraging to see in these large OSS corporate-sponsored projects, so kudos to you and the team for making the pragmatic choice.
Would you say C# AOT could have been competitive in startup time and overall performance, and the decision came down to the other factors you've noted? I think everyone would have expected C# AOT to be the default choice so it might be nice to see some more concrete examples of where Go proved itself more suitable for your use-case.
C# AOT is quite strong and would be a great choice in a lot of contexts.
Go was just very, very strong on port-friendliness when coming from the TS codebase in particular. If you pull up both codebases side-by-side, it's hard to even tell which is which. C# is more class-oriented and the core JS checker uses no classes at all, so the porting story gets quite a bit more difficult there.
Idiomatic C# is typically quite different and heavily class-based, but then a compiler would usually look very different than an Enterprise C# application anyway. You can use C# more in a functions and data structures way, I don't think there is something fundamental blocking this. But I guess there are many more subtle differences here than I can think of. Go is still quite a bit lower level than C#.
Couldn't you just use static classes? I don't see how that would be a factor at all, seems like a very superficial reason that would be easy to work around.
> many languages would be suitable in a ground-up rewrite situation.
> By far the most important aspect is that we need to keep the new codebase as compatible as possible, both in terms of semantics and in terms of code structure
> languages that require fundamental rethinking of memory management, mutation, data structuring, polymorphism, laziness, etc., might be a better fit for a ground-up rewrite, but we're undertaking this more as a port
My initial thought was "why not Rust" and those passages do a pretty good job of answering the question, IMO.
It's a fair question! Go is certainly more widely used in web dev circles (e.g. esbuild) than C# is, that might go some way to answering the question.
I think folks want TypeScript to use C# because it's Microsoft, I'm glad they didn't just do that for corporate reasons. But I'm open to there being non-corporate reasons!
I agree that it's good they looked passed typical corporate reasons. I admit it's still strange to see.
At the end of the day, companies like Microsoft are in the business of making money and that's one of the reasons they made C#. If they don't see the profit in using one of their own products (and one that competes with Go) in their future products then why would I use them for mine?
It's like if they used Google Cloud instead of Azure, it'd be very weird.
Generally, I see Go replacing a lot of projects that would have previously been written in Java (namely network services/backends). Compiler is a bit of an exception, for sure.
Yes… there’s a lot of overlapping territory, for sure. Just like how Java relaces a lot of projects that would have previously been written in C++ or C.
Could you perhaps elucidate with a couple of bullet points? In my view Java / C# and Go are technically the same thing. There might be some cultural differences though, which honestly, people seem to care more about anyway.
On the technical side, Java and C# are OOP languages with nominal typing, compacting GCs (at least by default), objects are reference types, can't have a reference to a variable, strings are UTF-16, async is available via promises / tasks / futures, async scheduling is cooperative, and module boundaries are primarily type boundaries. Applications are typically shipped as platform-independent with a separate runtime. Interfaces are intrinsic to objects, from a runtime perspective. You can overload methods. Easy to dynamically load code at runtime.
Go has structural typing, non-compacting GCs, allows you to freely choose value / reference types, can make references to variables, strings are bytes or UTF-8, async is built-in to the runtime, async scheduling is preemptive, and module boundaries are defined by the package you’re in. Applications are typically shipped as native code with a linked-in runtime. Interfaces are extrinsic to objects, from a runtime perspective. You cannot overload functions. Hard to dynamically load code at runtime.
When I write it down like this, they don’t seem all that similar to me. They’re all… garbage collected and imperative? Memory safe (or mostly) by default? That doesn’t seem like a lot of commonalities.
“They are all used for backend programming” isn’t really that interesting because backend is where you can choose whatever language you want. Facebook has a ton of Hack backend, Jane Street has OCaML, etc.
There are more overlaps with Java. It is gaining value types. Async is built into the runtime (see virtual threads), with preemptive scheduling. Its strings can be represented as bytes internally as well. It also has packages and modules for managing boundaries.
I think whimsicalism's point may be that, if C# isn't the tool of choice for this port, then that's the death knell of C#. The death knell isn't what kills it; that's the death blow. The death knell is sounded after it's dead. If C# wasn't the choice here, then C# is already dead.
That may be overstating how dead C# is. But I think it is accurate to whimsicalism's point.
I don't think so. It isn't as if C# isn't competitive in many spaces. However, honestly, if you want to create a simple cli app, Go is a better choice than C#. With C#, you would either need to bundle .NET or try to use AOT features which will either make for a huge executable or development friction. With Go, that is just how it works out of the box so I get it from that standpoint.
I still don't understand their rationale for not using Rust however. This is going to be run millions of times a day by web devs - it makes sense to make it as fast as possible. Optimizing for similarity of codebases doesn't seem right imo, and a dubious position to begin with as Go and Typescript aren't that similar anyway.
> Optimizing for similarity of codebases doesn't seem right imo,
It's simple, it's cheaper and faster (Time to market) to do Go than Rust. Rust has a pretty high learning curve, and there aren't many people writing it internally in MSFT I guess. Go's syntax is literally C + Python, from my previous experience, ramping up is a pretty fast affair as an experienced dev.
I don't think C# is being harmed by Typescript not being ported to it. There are a lot of companies that have used C# for years, and will continue to do so. They don't care if the TS compiler is not built with it.
There was a movement for C# to be a real developer language that is bonafide cross-platform. If Microsoft is citing cross-platform reasons to not dev with C#....
New companies aren't generally starting with Microsoft as their OS, these 'lots of companies' are legacy/enterprise stragglers and/or European.
There's an answer for that on the thread: they wanted to get as close to native code as they could while retaining GC, and Go is "native-native" (if you will), while C# is bytecode-native.
It's hard to tell which camp is more angry: the C# developers stating Microsoft is ditching its own ecosystem or the Rust developers unwilling to accept anything in this category not being written in Rust.
From my understanding, all three options (Go, C#, and Rust) represent a significant speedup. The only legitimate concern I see is the one around WASM support, which is quite valid.
But at the end of the day, it seems like the team is making a decision that maximizes speed and deliverability, with the offset being that some cracks are now present (e.g. WASM). I can respect them for that choice.
> It's hard to tell which camp is more angry: the C# developers stating Microsoft is ditching its own ecosystem or the Rust developers unwilling to accept anything in this category not being written in Rust.
Is it really? I must be reading the wrong comments somehow. I've seen lots of Rust people express only brief and mild surprise: "Wait what? Oh, okay, that makes sense. Carry on." None of them are taking this as a death knell for Rust.
Meanwhile, the C# camp is erupting in a crisis of faith because Anders Hejlsberg has eaten something other than his own dogfood. They are outright grieving over this announcement with much wailing and gnashing of teeth.
The problem is there are multiple competing priorities at play here. I'm sure the dev team is aware of the poor WASM support. Still, as stated in the OP, they seriously value the ability to efficiently rewrite their current code in a more performant language without drastically altering it. Obviously, to someone depending on WASM, the latter brings no value to them, so they can more easily criticize the dev team because they are simply ignoring that priority.
I have written a lot of code in many languages, but most recently in Go and Rust. I had actually considered myself a Rust convert up until recently, and if you look at my comment history, you will find me calling out Go for being a poorly designed language on multiple occasions.
I have changed my mind.
Why? Well, I started writing Go again, so I had experience writing both Rust and Go within a close timeframe to each other. These are very different languages with very different strengths. I think choosing one over the other largely comes down to what you are writing. For deep, complex typed algo code esp. things that need to be very fast, I would likely still choose Rust, but Go really excels at writing infra code. Code that needs to be fast, but more importantly needs to be iterated quickly, and Go compiles MUCH faster than Rust.
Most of my objections to Go I find in the "programming language theory part of my brain". I am a language designer at heart, and by that, I mean I iterate on language designs and have a deep love for PLT. The issue I have found is that, while Rust is awesome in theory (and still one of my favorite languages), actually building things quickly is more satisfying to me, and I am finding I can build in Go about 2x the rate as in Rust. I didn't think this was the case until I actually started tracking my own iteration speed. Things like compile time DO matter, because you are always compiling at some level, even if just running tests. Also, every language feature is just a tiny bit of friction, and Go is so simple that friction is nearly non-existent.
Most of my Go language objectionss, such as nil pointers, lack of enums, multi return vs tuples are much less an issue in practice than they are in theory, and once you sit down and actually think on 'why' Go might have done what it did, it actually makes some sense. I had a few lightbulb moments I could expand on in a different post.
In summary, Go and Rust are very different languages. I really like both, but in different use cases. I am back to writing more Go than Rust, but I expect to write some of both going forward.
That's interesting. I have some experience in go and rust. A bit more in go although that is also longer ago. And my experience was reverse from yours. Yes go is simpler but just the insane amount of time it took for me to read the existing codebase in go compared to an approx equivalent rust project was the most large difference for me. The code in rust is just so much faster to read in the large. The signal to noise ratio is much higher. Otoh go code is easier to read in the small but harder to read in the large.
> Go also offers excellent control of memory layout and allocation (both on an object and field level) without requiring that the entire codebase continually concern itself with memory management.
I guess this sheds a bit of light in to the why not rust debate.
I've written parsers and compilers in Rust. I used DAGs in a fairly standard way for stuff that needs it like references. I also work on a complicated VM with a custom memory allocation strategy professionally. Some of my other Rust projects include: a frontend web application that runs in the browser, a client library that has facades for Node, Python, and Ruby, and a Kafka Consumer that handles ~20 GiB/second of data throughput with relatively modest resourcing.
What he's saying here doesn't make any sense. It sounds like they threw in someone who doesn't know Rust at all, didn't bother to ask any questions, didn't reference any existing code, into trying to write custom memory management strategies and data structures and then bounced off the surface. That isn't how you do things in any language, it's bizarre and sounds like Rust was set up to fail. I wouldn't expect this scenario to succeed in any language on a non-trivial project like the TypeScript compiler.
What's even more bizarre is TypeScript actually has better support for ADTs than Golang (which is especially impactful when writing things like type checkers and compilers). I don't even _like_ TypeScript and I can see that. I've written 5-figures-ish of Golang for some work projects like a custom Terraform provider and it's horrific to model just a JSON schema in Golang's type system. Some of the problem is Hashicorp's terrible SDK but not all of it by any means.
Usually the problem is someone just not knowing how to write Rust. The modal subjective experience of writing Rust code as an experienced user is "Python with nice types, async, and executes fast." If you need to handle stuff with weird non-deterministic lifetimes you can use stuff like `slotmap`. If you need DAGs, use `petgraph`. I think pitching Rust as a "low-level systems language" might be misleading some people on how ~95% of Rust users are actually working in the language.
I once was in charge of a legacy C codebase that passed around data files, including to customers all around the world. The “file format” was nothing more than a memory dump of the C data structures. Oh, and the customers were running this software on various architectures (x86, sparc - big endian, alpha, itanium) and these data files had to all be compatible.
Occasionally, we had to create new programs that would do various things and sit in the middle of this workflow - read the data files, write out new ones that the customers could use with no change to their installed software. Because the big bosses could never make up their minds, we at various times used C#, Go, Python, and even C.
They all work just fine in a production environment. Seriously, it’s fine to choose any of them. But C# stands out as having the ugliest and sketchiest code to deal with it. But it works just fine!
More telling though. I used this scenario in interview questions many times. How would you approach it with C#? 99% of the time I would get a blank stare, followed by “I don’t think it’s possible”. If you want developers that can maintain code that can do this, perhaps don’t choose C# :)
Two things that have brought up in interviews. They don't seem to believe that AOT compiled C# is mature enough and can give the best possible performance on all their supported platforms. Their current codebase consists of more or less pure functions acting on simple data structures and since they want the port to be as 1:1 as possible, idiomatic Go is closer to that style than idiomatic C#.
Requiring all typescript users to install the .net runtime will probably kill adoption, especially on linux build servers. It still requires custom microsoft repos, if they're even available for your distro, and is barely upstreamed.
For Go, you just run the binary without any bullshit. This can easily be wrapped in an npm package to keep the current experience (`npm install` and it works) on all platforms.
I don't know why some people are losing their minds over it being in go... ok, so I kinda expected rust because that's what all the cool kids are doing - rewriting in rust - but I think go is a pretty good choice for a domain like this.
Rust is a PITA. I started a few Rust projects as well, but it just doesn't vibe with me. I don't like the verbosenes of Go, but it's mostly consistent, and there are accepted best practices. Rust is like the Perl of system programming.
In my experience, Rust has been a language that writes as terse and bloated at the same time; though it is perhaps my own skill issue. I tried hard to love the language but I ended up walking away.
They're both blub languages, designed to be simple and boring but with lots of grafted-on cruft from the designers not realizing that complexity is a waterbed.
Normally I don't think anyone would be losing their mind, but this is a Microsoft team creating a widely popular Microsoft product and they use a Google product (Go) instead of using another Microsoft product (C#) that is arguably competing with it. It strikes as odd why a Microsoft team isn't comfortable with/trusting a Microsoft product.
I think it makes sense that some people are taking this in a way that maybe they shouldn't trust in C#'s future either.
yeah, i saw that inference too; but i think it's a silly one - it would be like those same people being upset that new windows core features aren't written in C# (and, we shouldn't forget the other targeting languages)
but i'm also a fan of languages, so my opinion may be screwy ;p
The fact that the Windows team rejected C# actually was a reason to think twice about it (and Office, and ...) . If nothing else it had deep implications for .NET's backwards compatibility story, and it sent the message that people with existing C++ codebases shouldn't view it as a natural next step despite the .NET team originally pushing dialects of C++ and P/Invoke pretty hard.
For everyone asking Why Not C#? I think it's fair, given Anders who designed both C# and TypeScript was the one who announced this on X and is involved, to take them at face value on things such as "Idiomatic Go strongly resembles the existing coding patterns of the TypeScript codebase, which makes this porting effort much more tractable." Maybe after doing a bunch of prototypes, like they said they did, it was just the obvious choice at the level of the code itself, and I think if that's where they focused their decision rubric instead of on other factors like NIH or the current hot thing it's a refreshing way of making a decision.
> Languages that allow for a structurally similar codebase offer a significant boon for anyone making code changes because we can easily port changes between the two codebases.
From this I can understand why they did not go with, say, Rust - but there are other compiled languages with garbage collection that could have worked well. Why Go over Crystal? I imagine it's that the Go is the most mature ecostystem (i.e. the interop) but it feels like a missed opportunity.
With typescript 7 written in Go, am I able to download typescript 7 as an npm module in my package and use that? No, right? I have to have it installed on my system and maybe have a version manager to use different versions across projects. Or maybe it can be compiled to WA and published to npm?
I think if you looked at the typescript code base you’d understand.
It’s a mess!!!
As most functional projects are. There’s just data structures and code and good luck finding anything or trying to figure out why something is where it is.
Seeing all the comments about Rust in that other discussion was kind of disappointing. I have a feeling if they chose Rust, go fans wouldn't be complaining about it not being written in Go.
Note if you are a title-only-before-commenting reader that this is not a generalized apologia for Go, but specifically for the case of porting the Typescript compiler, so reciting the Litany Against Go in a general sense is not really on topic.
(The Litany, naturally, is:
reply