| 01:17 | <Mathieu Hofman> | So a shared struct instance has a stable identity across It does not require any new syntax, or extra magic in postMessage (like module blocks, or symbol identities being preserved). It only requires an automatically generated static property on shared struct classes that represent the kind, and some built-in behavior, plus the dynamic prototype lookup we discussed of course. I'm actually wondering if this could be prototyped (pun intented) in the current experiment. |
| 10:39 | <Ashley Claymore> | Right, that would be similar to having 'known' static functions that operate on the type and each agent individually imports the module to use those functions, except with the power to per-realm/per-agent register those functions as the prototype to use for that 'type' to get the capability of method dispatch. (also method chaining, but |> operator would also give that).'type' here being the identity created by the shared struct class syntax (the fan-out case).For the fan-in case, where a farm of workers start up on their own, each creating their own separate shared struct class (from the same module URL), as they are sent to a sink, the sink would need to register that one 'prototype' with the multiple 'type's that are received from each worker. |
| 11:54 | <Mathieu Hofman> | Right the fan in case would work too, there jsut would be different constructors and kinds, that each would have to be set to use the same prototype maker, or prototype implementation if the prototype doesn't care about exposing the realm local constructor. You're right at the end of the day this boils down to the capability of setting the dynamic prototype to use for instances. |
| 12:21 | <Ashley Claymore> | and to also capture a bit of what was discussed on the call last night: |
| 12:21 | <Ashley Claymore> |
|
| 12:24 | <Ashley Claymore> |
|
| 12:26 | <Ashley Claymore> |
|
| 12:26 | <Mathieu Hofman> |
|
| 12:27 | <Ashley Claymore> | yes unlikely to be observable, but if that is an implementation goal then it limits which semantics are performant/simple/memory-efficient etc |
| 12:28 | <Mathieu Hofman> |
|
| 12:28 | <Mathieu Hofman> | Quoted the wrong message |
| 12:29 | <Mathieu Hofman> | As for per instance memory, i believe it'd only be per kind / type memory, not per instance |
| 15:46 | <rbuckton> |
|
| 15:58 | <Ashley Claymore> | allows more userland solutions/experimentation maybe? |
| 15:59 | <Ashley Claymore> | though I guess that itself can be done in userland
|
| 15:59 | <rbuckton> | I'm not certain that's necessary, at least not for an MVP. |
| 15:59 | <Mathieu Hofman> | I did this so the program could attach the dynamic prototype without magic |
| 16:00 | <Mathieu Hofman> | We can always try to find more ergonomic ways, but this is flexible for experimenting |
| 16:00 | <rbuckton> | It just seems a bit like an overcomplication, IMO. |
| 16:01 | <Mathieu Hofman> | If you have an alternative I'm all ears |
| 16:02 | <Mathieu Hofman> | All the solutions I've heard so far rely on more syntax that doesn't exist today |
| 16:43 | <Mathieu Hofman> |
|
| 16:47 | <Ashley Claymore> | yep. I was imagining a userland experimental library where the prototype is registered with the library, and then the shared-struct is passed to a wrap function that returns a proxy wrapper for it which adds the proto look up (but loses the ability to be structured clone) |
| 16:48 | <Mathieu Hofman> | Oh yeah you can totally do this in userland with Proxies |
| 16:48 | <Mathieu Hofman> | at the expense of per realm proxy instances |
| 16:49 | <Ashley Claymore> | and code wouldn't be able to magically pass that proxied wrapper to another agent, they would know it needs to be unwrapped again |
| 16:49 | <Mathieu Hofman> | minus the postMessage identity preserving logic, but that can be emulated by wrapping postMessage, which gets hairy quickly, and makes it impossible to do cross agent gc of course |
| 16:50 | <Ashley Claymore> | or structuredClone would need a new handler similar to toJSON where it can extract out the struct automatically |
| 16:50 | <Mathieu Hofman> | aka no cycle collection. if no cycle, you can use weakrefs |
| 16:50 | <Mathieu Hofman> | nah that really needs to be in the engine (at least until I get to propose my API to support distributed GC) |
| 16:51 | <Mathieu Hofman> | I have been toying with identity preservation through postMessage for a few years now, that's what got me interested in TC39 in the first place |
| 16:52 | <Mathieu Hofman> | (yes I know different standard groups, but the gc API needs to be in the language) |
| 16:52 | <Mathieu Hofman> | wrapping postMessage is no fun, it's very inception |
| 16:53 | <Ashley Claymore> | right now I kinda like the idea that shared structs that want a prototype are top-level-const exported export shared struct class Foo {} , and the module they are declared is what is 'attached' to the 'type' as an internal slot. for other agents to load. It could have an overlap with the import-defer-eval proposal, where the module is sync loaded on the first prototype access to be lazy and reduce the cost when the methods are not accessed by other agents |
| 16:55 | <Mathieu Hofman> | yeah maybe that's what the ergonomic solution ends up looking like, but module blocks and/or import defer do not exist today if shu want to experiment with something right away. My proposal is about enabling this with what we have today, and the dynamic prototype lookup we'll need anyway |
| 16:55 | <Mathieu Hofman> | no need to mess with module logic |
| 16:56 | <Ashley Claymore> | (I don't think) this would need module blocks, the part that is attached to the internal slot can be, as littledan said, a URL string |
| 16:58 | <Mathieu Hofman> | I'm personally not a fan of tying module specifier strings into the solution |
| 17:21 | <rbuckton> | Is there any reason the origin trial API couldn't be extended to provide a registration mechanism for the shared struct type with a user-provided unique ID (just a string, but could contain a URI, UUID, etc.) and the thread-local prototype to use?
The user-supplied type identity would allow the user to define the same struct in different bundles, and the registry wouldn't be global, but would rather be thread-local, so there'd be no global mutable registry to worry about. It also doesn't really matter if the prototypes differ slightly between realms/threads, since they all access the same underlying data. We could eventually extend this to syntax, possibly even using decorators:
|
| 18:02 | <Mathieu Hofman> | I fail to see how a user supplied value provided after shared struct creation would work. Unless you'd somehow remap types you may have already seen. IMO you'd at least need to pass your unique ID as part of the SharedStructType.prepare call. But in general I don't like strings for unique IDs, and since we already have object that carry identity across agents, I thought it'd make sense to reuse them. |
| 18:03 | <Mathieu Hofman> | Btw, the prototype attaching I suggested can be made to have a shape more similar to your suggestion above |
| 18:49 | <Mathieu Hofman> |
SharedStructType.getConstructor() is needed to allow the program to avoid duplicating type definitions in each agent/realm. I think the burden of such deduplication should be on the program, not on the engine. If the engine had to deduplicate itself, you'd either need a user provided type identifier at "prepare" time, and a global lock around such type definitions, or you'd need to somehow be able to collapse separate definitions into a single one. In either case you'd have to figure out what to do if the shape definition does not match, and in the case of definition collapse, how do you communicate the error to the program? By having the type definition generate the unique type identifier, you avoid all those complications in the engine, at the cost of putting more type hydration burden on the program. |