Earlier quoted context omitted.
It's true that JS await is kinda like releasing a lock, but otherwise, you'd just use a mutex whenever you access shared state. Which is rare as you said, and also easy to enforce in various langs nowadays.
I said that shared state between connections is rare, but shared state within a connection is extremely common. And there are still multiple concurrent things going on within that connection context, requiring some concurrency mechanism. Locking mutexes everywhere sounds like a nightmare to me.
Cap'n Web: a new RPC system for browsers and web servers
141–150 of 305 posts
Re: Cap'n Web: a new RPC system for browsers and web servers
#142I was really excited when I saw the headline, but I was kind of disappointed to see it doesn't support schemas natively. I know you can specify them via zod, but I'm really not looking forward to any more untyped APIs, given that the lack of strict API typing has been by far the #1 reason for the bugs I've had to deal with in my career.
I really want to bake in some sort of support for generating type checks based on TypeScript types... so then your schemas are just TypeScript. Not sure why this doesn't seem to be a common practice TBH, might be missing something.
Yeah... I've been deep in this problem space myself. The two big points of friction are: 1. Requiring a build-step to generate runtime code from the TS types 2. TS doesn't officially support compiler transforms that do it
That said, the two most promising approaches I've found so far: 1. https://github.com/GoogleFeud/ts-runtime-checks -- it does exactly what you describe 2. https://arktype.io/ -- a very interesting take on the Zod model, but feels like writing native Typescript
Congrats on the launch, really exciting to see a way to get capabilities into the JS ecosystem!
Re: Cap'n Web: a new RPC system for browsers and web servers
#143Earlier quoted context omitted.
Yeah I now want to go back and redesign the Cap'n Proto RPC protocol to be based on this new design, as it accomplishes all the same features with a lot less complexity! But it may be tough to justify when we already have working Cap'n Proto implementations speaking the existing protocol, that took a lot of work to build. Yes, the new implementations will be less work than the original, but it's still a lot of work t…
Disclaimer: I took over maintenance of the Cap'n Proto C bindings a couple years ago. That makes sense. There is some opportunity though since the Cap'n Proto RPC had always lacked a JavaScript RPC implementation. For example, I had always been planning on using the Cap'n Proto OCaml implementation (which had full RPC) and using one of the two mature OCaml->JavaScript frameworks to get a JavaScript implementation. Lo…
Re: Cap'n Web: a new RPC system for browsers and web servers
#144Why not "application/octet-stream" header and sending ArrayBuffer over the network ?
Re: Cap'n Web: a new RPC system for browsers and web servers
#145Earlier quoted context omitted.
I said that shared state between connections is rare, but shared state within a connection is extremely common. And there are still multiple concurrent things going on within that connection context, requiring some concurrency mechanism. Locking mutexes everywhere sounds like a nightmare to me.
Ah I see. Well that is typically just fan-out-fan-in like "run these 4 SQL queries and RPCs in parallel and collect responses," nothing too complicated since the shared resources like the DB handle are usually thread-safe. It works out fine in Go and Java, even though I have unrelated reasons to avoid Go.
Re: Cap'n Web: a new RPC system for browsers and web servers
#146I find the choice of TypeScript to be disappointing. One of the reasons that capproto has struggled for market share is the lack of implementations. Is the overhead for calling into WASM too high for a Rust implementation to be feasible? A Haxe or Dafny implementation have let us generate libraries in multiple languages from the same source.
Genuinely curious, is the disappointment because it's limited to the JS/TS ecosystem?
My take is that by going all-in on TypeScript, they get a huge advantage: they can skip a separate schema language and use pure TS interfaces as the source of truth for the API.
The moment they need to support multiple languages, they need to introduce a new complex layer (like Protobuf), which forces design into a "lowest common denominator" and loses the advanced TypeScript features that make the approach so powerful in the first place.
Re: Cap'n Web: a new RPC system for browsers and web servers
#147JSON Serialization, seriously ??? Why not "application/octet-stream" header and sending ArrayBuffer over the network ?
I'm obviously a huge fan of binary serialization; I wrote Cap'n Proto and Protobuf v2 after all.
But when you're working with pure JS, it's hard to be much faster than the built-in JSON implementation, and even if you can beat it, you're only going to get there with a lot of code, and in a browser code footprint often matters more than runtime speed.
Re: Cap'n Web: a new RPC system for browsers and web servers
#148I find the choice of TypeScript to be disappointing. One of the reasons that capproto has struggled for market share is the lack of implementations. Is the overhead for calling into WASM too high for a Rust implementation to be feasible? A Haxe or Dafny implementation have let us generate libraries in multiple languages from the same source.
There’s no way you could implement this in 10kb of rust. It takes massive advantage of javascript’s dynamism to work. Also, I’m pretty sure ts / js are vastly more popular languages than rust. I suspect this will get a lot more use because it’s typescript.
Also, the audience of this library is very specifically TypeScript developers. If your app is Rust, you'd probably be happier with Cap'n Proto.
Re: Cap'n Web: a new RPC system for browsers and web servers
#149> RPC is often accused of committing many of the fallacies of distributed computing. > But this reputation is outdated. When RPC was first invented some 40 years ago, async programming barely existed. We did not have Promises, much less async and await. I'm confused. How is this a "protocol" if its core premises rely on very specific implementation of concurrency in a very specific language?
"RPC" originally referred to a programming paradigm where remote calls looked just like any other method calls, and it might not even be any of the programmer's business whether they're implemented in-process or on another machine. This obviously required wire protocols, client and server libraries, etc. to implement. There's been a renaissance in the tools, but now we mainly use them like "REST" endpoints with the t…
Re: Cap'n Web: a new RPC system for browsers and web servers
#150Couple random thoughts: I'm trying to see if there's something specifically for streaming/generators. I don't think so? Of course you can use callbacks, but you have to implement your own sentinel to mark the end, and other little corner cases. It seems like you can create a callback to an anonymous function, but then the garbage collector probably can't collect that function? --- I don't see anything about exception…
You can define a stream like: interface Stream extends RpcTarget { write(chunk): void; end(): void; [Symbol.dispose](): void; } Note that the dispose method will be called automatically when the caller disposes the stub or when they disconnect the RPC session. The `end()` method is still useful as a way to distinguish a clean end vs. an abort. In any case, you implement this interface, and pass it over the RPC connec…
Maybe the best solution is just an eslint plugin. Like this plugin basically warns for the same thing on another type: https://github.com/bensaufley/eslint-plugin-preact-signals
Overloading .map() does feel a bit too clever here, as it has this major difference from Array.map. I'd rather see it as .mapRemote() or something that immediately sticks out.
I can imagine a RpcPromise.filterRemote(func: (p: RPCPromise) => RPCPromise) that only allows filtering on the truthiness of properties; in that case the types really would save someone from confusion.
I guess if the output type of map was something like:
type MapOutput = RpcPromise | MapOutput[] | Record;
map(func: (p: RpcPromise) => MapOutput)
... then you'd catch most cases, because there's no good reason to have any constant/literal value in the return value. Almost every case where there's a non-RpcPromise value is likely some case where a value was calculated in a way that won't work.Though another case occurs to me that might not be caught by any of this:
result = aPromise.map(friend => {...friend, nickname: getNickname(friend.id, userId)})
The spread operator is a pretty natural thing to use in this case, and it probably doesn't work on an RpcPromise?