7 ms·
Go is quite difficult to embed in other applications due to the runtime. What do you see as the future for use cases where the typescript compiler is embedded
by noodletheworld 2y ago
Go is quite difficult to embed in other applications due to the runtime.
What do you see as the future for use cases where the typescript compiler is embedded in other projects? (Eg. Deno, Jupyter kernels, etc.)
There’s some talk of an inter process api, but vague hand waving here about technical details. What’s the vision?
In TS7 will you be able to embed the compiler? Or is that not supported?
- DanRosenwasser 2y agoWe are sure there will be a way to embed via something like WebAssembly, but the goal is to start from the IPC layer (similar to LSP), and then explore how possible it will be to integrate at a tighter level.
- _benton 2y agoGolang is actually pretty easy to embed into JS/TS via wasm. See esbuild.
- curtisblaine 2y agoEsbuild is distributed as a series of native executables that are selectively installed by looking at arch and platform. Although you can build esbuild in wasm (and that's what you use when you run it in the browser), what you actually run from .bin in the CLI is a native executable, not wasm.
- nine_k 2y agoWhy embed it if you can run a process alongside yours and use efficient IPC? I suppose the compiler code should not be in some tight loop where an IPC boundary would be a noticeable slowdown. Compilation occurs relatively rarely, compared to running the compiled code, in things like Node / Deno / Bun / Jupyter. LSPs use this model with a pretty wasteful XML IPC, and they don't seem to feel slow.
- wokwokwok 2y agoBecause running a parallel process is often difficult. In most cases, the question becomes: So, how exactly is my app/whatever supposed to spin up a parallel process in the OS and then talk to it over IPC? How do you shut it down when the 'host' process dies? Not vaguely. Not hand wave "just launch it". How exactly do you do it? How do you do it in environments where that capability (spawning arbitrary processes) is limited? eg. mobile. How do you package it so that you distribute it in parallel? Will it conflict with other applications that do the same thing? When you look at, for example, a jupyter kernel, it is already a host process launched and managed by jupyter-lab or whatever, which talks via network chatter. So now each kernel process has to manage another process, which it talks to via IPC? ... Certainly, there are no obvious performance reasons to avoid IPC, but I think there are use cases where having the compiler embedded makes more sense.
- jchw 2y ago> So, how exactly is my app/whatever supposed to spin up a parallel process in the OS and then talk to it over IPC? Usually the very easiest way to do this is to launch the target as a subprocess and communicate over stdin/stdout. (Obviously, you can also negotiate things like shared memory buffers once you have a communication channel, but stdin/stdout is enough for a lot of stuff.) > How do you shut it down when the 'host' process dies? From the perspective of the parent process, you can go through some extra work to guarantee this if you want; every operating system has facilities for it. For example, in Linux, you can make use of PR_SET_PDEATHSIG. Actually using that facility properly is a bit trickier, but it does work. However, since the child process, in this case, is aware that it is a child process, the best way to go about it would be to handle it cooperatively. If you're communicating over stdin/stdout, the child process's stdin will close when the parent process dies. This is portable across Windows and UNIX-likes. The child process can then exit. > How do you do it in environments where that capability (spawning arbitrary processes) is limited? eg. mobile. On Android, there is nothing special to do here as far as I know. You should be able to bundle and spawn a native process just fine. Go binaries are no exception. On iOS, it is true that apps are not allowed to spawn child processes, as far as I am aware. On iOS you'd need a different strategy. If you still want a native code approach, though, it's more than doable. Since you're on iOS, you'll have some native code somewhere. You can compile Go code into a Clang-compatible static library archive, using -buildmode=c-archive. There's a bit more nuance to it to get something that will link properly in iOS, but it is supported by Go itself (Go supports iOS and Android in the toolchain and via gomobile.) Once you have something that can be linked into the process space, the old IPC approach would continue to work, with the semantic caveat that it's not technically interprocess anymore. This approach can also be used in any other situation you're doing native code, so as long as you can link C libraries. If you're in an even more restrictive situation, like, I dunno, Cloudflare Pages Functions, you can use a WASM bundle. It comes at a performance hit, but given that the Go port of the TypeScript compiler is already roughly 3.5x faster than the TypeScript implementation, it probably will not be a huge issue compared to today's performance. > How do you package it so that you distribute it in parallel? Will it conflict with other applications that do the same thing? There are no particular complexities with distributing Go binaries. You need to ship a binary for each architecture and OS combination you want to support, but Go has relatively straight-forward cross-compiling, so this is usually very easy to do. (Rather unusually, it is even capable of cross-compiling to macOS and iOS from non-Apple platforms. Though I bet Zig can do this, too.) You just include the binary into your build. If you are using some bindings, I would expect the bindings to take care of this by default, making your resulting binaries "just work" as needed. It will not conflict with other applications that do the same thing. > When you look at, for example, a jupyter kernel, it is already a host process launched and managed by jupyter-lab or whatever, which talks via network chatter. > So now each kernel process has to manage another process, which it talks to via IPC? Yes, that's right: you would have to have another process for each existing process that needs its own compiler instance, if going with the IPC approach. However, unless we're talking about an obscene number of processes, this is probably not going to be much of an issue. If anything, keeping it out-of-process might help improve matters if it's currently doing things synchronously that could be asynchronous. Of course, even though this isn't really much of an issue, you could still avoid it by going with another approach if it really was a huge problem. For example, assuming the respective Jupyter kernel already needs Node.JS in-process somehow, you could just as well have a version of tsc compiled into a Node-API module, and do everything in-process. > Certainly, there are no obvious performance reasons to avoid IPC, but I think there are use cases where having the compiler embedded makes more sense. Except for browsers and edge runtimes, it should be possible to make an embedded version of the compiler if it is necessary. I'm not sure if the TypeScript team will maintain such a version on their own, it remains to be seen exactly what approach they take for IPC. I'm not a TypeScript Compiler developer, but I hope these answers are helpful in some way anyways.
- mappu 2y agoGo has buildmode=c-shared, which compiles your program to a C-style shared library with C ABI exports. Any first call into your functions initializes the runtime transparently. It's pretty seamless and automatic, and it'll perform better than embedding a WASM engine.
- jillyboel 2y agoJavascript is also quite difficult to embed in other applications. So not much has changed, except it's no longer your language of choice.
- demurgos 2y agoTypeScript compiles to JavaScript. It means both `tsc` and the TS program can share the same platform today. With a TSC in Go, it's no longer true. Previously you only had to figure out how to run JS, now you have to figure out both how to manage a native process _and_ run the JS output. This obviously matters less for situations where you have a clear separation between the build stage and runtime stage. Most people complaining here seem to be talking about environments were compilation is tightly integrated with the execution of the compiled JS.