01:06
<shu>
rbuckton: wrote up https://github.com/tc39/proposal-structs/blob/main/ATTACHING-BEHAVIOR.md, PTAL
01:23
<rbuckton>
A quick point regarding syntax, just as I mentioned before about wanting to avoid excess ceremony, I'm hoping we can go with something far shorter than shared struct class Foo {}. I imagine struct Foo {} and shared struct Foo {} would be sufficient to avoid ambiguity without needing the class keyword, and their behavior is different enough to justify the different syntax.
01:27
<rbuckton>

I'm also still not to keen on using class name as a global registry key, its far too easy to have collisions (so many things would be called Node, for example). I'd prefer the keying mechanism be divorced from the name of the struct somehow. In earlier discussions I'd recommended using UUIDs and decorators, i.e.:

@RegisteredStruct("92057993-84c2-4015-9a4e-f1d3810db4a2") shared struct Foo { }
02:06
<Ashley Claymore>
`shared struct com.bloomberg.ashleys.Node {}`
02:11
<Ashley Claymore>
shared structs don't have user code constructors (i now also see that the README.md is incorrect)
It could be nice if fields could still have initialisers of literal values `field = 0`
02:23
<shu>
Ashley Claymore: noted, good idea
02:23
<shu>
A quick point regarding syntax, just as I mentioned before about wanting to avoid excess ceremony, I'm hoping we can go with something far shorter than shared struct class Foo {}. I imagine struct Foo {} and shared struct Foo {} would be sufficient to avoid ambiguity without needing the class keyword, and their behavior is different enough to justify the different syntax.
also agreed, consider the syntax a strawperson. i don't love the keyword soup
02:24
<shu>

I'm also still not to keen on using class name as a global registry key, its far too easy to have collisions (so many things would be called Node, for example). I'd prefer the keying mechanism be divorced from the name of the struct somehow. In earlier discussions I'd recommended using UUIDs and decorators, i.e.:

@RegisteredStruct("92057993-84c2-4015-9a4e-f1d3810db4a2") shared struct Foo { }
i considered that, or a programmatic API to register shared structs. the issue is i'd prefer the registration to happen during evaluation instead of after for implementation complexity reasons. if it happens after, like with a @Register or a programmatic API, that means swapping out the guts of the constructor function, which i'd like to avoid
02:25
<shu>
that's not a dealbreaker, just a preference
02:26
<shu>
but do the broad strokes look good to you?
02:37
<shu>
i... suppose the @RegisteredStruct decorator could be implemented as applying during evaluation if it's some special built-in decorator that's not implementable by user code
02:38
<shu>
nothing in the decorator proposal precludes built-in native code decorators AFAIK
02:49
<rbuckton>
I've long believed there's room for built-in decorators with privileged capabilities that a runtime might be able to optimize ahead of time. For example, built in @enumerable(true|false), @writable(true|false), @configurable(true|false) decorators that can affect property descriptors since the Stage 3 proposal no longer has this capability.
02:50
<rbuckton>
Assuming they are trivially analyzable.
02:53
<rbuckton>
But an @RegisterStruct decorator has the opportunity to perform constructor replacement even without native code support, but I suppose in this case you're talking about it somehow patching the constructor to produce a this consistent with the registry during construction.
03:29
<Mathieu Hofman>

When evaluated, if the class name does not exist in the registry, insert a new entry whose key is the class name, and whose value is a description of the layout (i.e. order and names of instance fields, and whether the prototype is agent-local).
When evaluated, if the class name already exists in the registry, check if the layout exactly matches the current evaluation's layout. If not, throw.

  1. that doesn't explain what happens if the name exists and the layout matches (I guess the default is do nothing, aka first one wins)
  2. as I explained, any kind of simple agent wide registry keyed on string is a no go as it's effectively global mutable state that can be observed by the program (e.g. try to evaluate a shared struct definition with a new shape, see if it throws or not)
03:31
<Mathieu Hofman>
Ok I hadn't seen the registry freezing thing. It feels weird to not be able to create new registered shared structs, as that would completely nerf the point of the registry
03:32
<Mathieu Hofman>
That also makes a program potentially become invalid after freezing of the registry
03:39
<Mathieu Hofman>
Also because the registry is agent local, what would be the behavior in case of the same declaration in 2 realms, especially if one of those is a ShadowRealm? That "surprise" is more than that, it's a blatant violation of the callable boundary.
03:43
<Mathieu Hofman>
Regarding the agent-local fields, it feels weird to have static nonshared prototype automatically be created as an object instead of undefined like what a field would be. It also entices authors to go back to the pre-es5 way of populating the prototype, with assignment, which is a typical trigger of the override mistake. Which raised the question, what is the __ proto __ of that automatically created prototype object? If non-null, it's definitely going to cause override mistake issues in frozen intrinsics environments.
04:08
<shu>
That also makes a program potentially become invalid after freezing of the registry
the pointer of the registry is to be a communication channel. if you want to plug that channel, you'll have to coordinate shared struct types yourself without the registry, so yes, it does defeat the point of the registry
04:08
<shu>
just like deleting capabilities defeat the point of those capabilities. isn't that the point of deniability?
04:10
<shu>
Also because the registry is agent local, what would be the behavior in case of the same declaration in 2 realms, especially if one of those is a ShadowRealm? That "surprise" is more than that, it's a blatant violation of the callable boundary.
yes, this would need to be censored in the callable boundary if it's agent-local instead of realm-local
04:10
<shu>
Regarding the agent-local fields, it feels weird to have static nonshared prototype automatically be created as an object instead of undefined like what a field would be. It also entices authors to go back to the pre-es5 way of populating the prototype, with assignment, which is a typical trigger of the override mistake. Which raised the question, what is the __ proto __ of that automatically created prototype object? If non-null, it's definitely going to cause override mistake issues in frozen intrinsics environments.
i'm fine with undefined, and manually assigning it
04:11
<Mathieu Hofman>
Except here that registry is syntactic.
04:11
<shu>
see ron's built-in decorator idea. i'm not wedded to syntax or even a programmatic API, though i have implementation reasons to prefer not programmatic, it is not instrumental
04:11
<Mathieu Hofman>
Decorators are still syntax
04:12
<shu>
delete O.p is still syntax...
04:12
<shu>
what line are you drawing?
04:13
<Mathieu Hofman>
But even if it was programmatic, that makes a program potentially become invalid. There is almost no existing capability exposing state built into the language today
04:13
<shu>
there is no precedent for this, agreed
04:13
<shu>
it is a new capability
04:13
<Mathieu Hofman>
I'm drawing the line at no new built-in exposing some global state
04:14
<shu>
this global state can be disabled for programs that don't use it
04:14
<shu>
if your program uses it and depends on the communication channel to get around a pretty bad DX issue, then... you opt into it
04:14
<Mathieu Hofman>
Or at least, it has to be entirely deniable, not just partially.
04:15
<shu>
in what sense is freezing the registry at program start not entirely deniable?
04:15
<shu>
in that case, you can't ever use registered shared structs, you must pass them around
04:16
<Mathieu Hofman>
Because creating a shared struct that would use that registry is disconnected, and is undeniable syntax.
04:16
<shu>
okay, then let's say the API is programmatic
04:16
<Mathieu Hofman>
It changes the behavior of other code
04:16
<shu>
and you can also delete the function that does the registering
04:17
<shu>
so deleting any existing function-based capability can change behavior of other code
04:17
<shu>
i don't see some bright line here
04:20
<shu>
even simpler, let's say the registry API is just its own global, which is configurable. registration is completely deniable
04:21
<shu>
for people who prefer a decorator-based approach, easy enough to write a class decorator that calls that API
04:22
<Mathieu Hofman>
It's late for me to articulate it, but I feel extremely uncomfortable with such a global state being added to the language, and the mitigation to deny that feature. Maybe if it was normative optional it'd be acceptable?
04:22
<shu>
sure, if normative optional makes you more comfortable
04:22
<shu>
we've done similarly for WeakRefs and finalizers
04:22
<shu>
yes let's pick this up tomorrow in the working session call
15:51
<rbuckton>
shu: I have some thoughts on the struct syntax, which I've posted here: https://gist.github.com/rbuckton/e1e8947da16f936edec1d269f00e2c53
15:52
<rbuckton>
Given that static shared prototype; looks too much like a field definition, I opted to use with shared prototype; instead.
15:58
<rbuckton>
Also, given this back and forth on the registry, I still think the correlation based registry is still worth considering. Its more akin to Symbol.for(), since you cannot observe whether something is registered and it doesn't require API deniability.
15:59
<shu>
let's discuss the registry in depth at the working session call today
16:00
<shu>
which, PSA, is pushed back 30 minutes
16:00
<shu>
i had a last minute conflict, packed meeting today, sorry
16:00
<rbuckton>
ah, that's a problem. I have a hard stop at 2PM EDT/11AM PDT as I am hosting a meeting at that time.
16:01
<shu>
Also, given this back and forth on the registry, I still think the correlation based registry is still worth considering. Its more akin to Symbol.for(), since you cannot observe whether something is registered and it doesn't require API deniability.
i was thinking about a programmatic registry as well that you'd need to first postMessage back and forth
16:01
<shu>
ah, that's a problem. I have a hard stop at 2PM EDT/11AM PDT as I am hosting a meeting at that time.
then let's try to get as much as we can in 30 minutes, i suppose
16:02
<rbuckton>
If I have to asynchronously wait for an onmessage in the main thread before I can start sending data to the worker, that won't work for my use cases.
16:03
<rbuckton>
If the Worker has to do all the work before it sees the first message I post, that's fine.
16:30
<rbuckton>
Hmm. SharedArray only allows a max of 16382 ((2**14)-2) elements?
18:31
<lpardosixtosms>
Hmm. SharedArray only allows a max of 16382 ((2**14)-2) elements?
Back when I implemented it there was a limit on the size of the objects that could be allocated in the engine's shared heap. I think that it is not the case anymore.
20:19
<shu>
rbuckton: thinking about your static declarative syntax idea
20:21
<shu>
what you can have is, like a layout declaration that is completely static and deduplicated, decoupled from shared struct declarations. shared struct declarations would then take a layout, and produce constructor functions in the executing Realm per-evaluation, much like classes, but since they are given a layout, they can be hooked up under the hood
20:23
<rbuckton>
can you provide an example of what this might look like, roughly?
20:25
<shu>

strawperson syntax:

// Special new declarative syntax
// Can't anything that actually evaluates, so no method decls, no static initializers, etc
layout SharedThingLayout {
  x; y;
  with nonshared prototype;
}

// Declaration that's actually evaluated and produces a constructor function
shared struct SharedThing layout SharedThingLayout {
  // allowed because SharedThingLayout has thread-local prototype, so there's a place to install m()
  m() { ... }
  // allowed because x exists in the layout
  x = 42;
  // disallowed because z doesn't exist in the layout
  z = "foo";
}
20:25
<shu>
there's no communication channel there AFAIK
20:25
<shu>
layouts will be transparently keyed by, like, source location
20:25
<shu>
or Parse Node in specese
20:26
<shu>
actually maybe Parse Node isn't sufficient, we might need a new concept
20:26
<shu>
since you reparse modules multiple times
20:26
<shu>
but that seems like a mechanical problem to describe...
20:26
<shu>
maybe a host hook
20:26
<rbuckton>

What happens if I do this:

shared struct SharedThing1 layout SharedThingLayout {...}
shared struct SharedThing2 layout SharedThingLayout {...}
20:27
<shu>
easier for me to describe concretely in V8 implementation terms: you get 2 different constructor functions in your current Realm, both backed by the same map
20:27
<rbuckton>
Transparently keying by source location is fine as a fallback, but still doesn't work with bundlers.
20:28
<shu>
why not, they duplicate?
20:28
<shu>
why would a bundler make multiple copies of the same code
20:28
<rbuckton>
I fail to see how that resolves the correlation issue?
20:28
<shu>
think of map as the type
20:29
<rbuckton>
But how do I say that a SharedThingLayout in two threads are the same thing?
20:29
<shu>
that resolves the correlation issue because SharedThing1 instances have the same type in the engine as SharedThing2 instances
20:29
<rbuckton>
No, I think we're talking past each other
20:30
<shu>
oh, because i assume what you're doing is import { SharedThing } from 'structs.js', and 'structs.js' has the layout declaration
20:30
<shu>
so when multiple threads import it, they get the same deduplicated layout
20:30
<rbuckton>
forget thing1 and thing2. I'm talking about main thread SharedThingLayout and child thread SharedThingLayout
20:30
<rbuckton>
That's the problem.
20:30
<shu>
i understand, i'm saying there's one layout
20:30
<shu>
that layout is keyed off source location, in structs.js:NNN
20:30
<rbuckton>
oh, because i assume what you're doing is import { SharedThing } from 'structs.js', and 'structs.js' has the layout declaration
that's the problem
20:30
<shu>
why is that a problem?
20:31
<rbuckton>
Main thread loads main.js, which is a bundle that includes layout SharedThing. Child thread loads worker.js which is a bundle that includes layout SharedThing in a different path and source location.
20:31
<shu>
so that comes back to my original question: do bundlers duplicate?
20:32
<shu>
you're telling me bundlers duplicate?
20:32
<rbuckton>
What deduplication?
20:32
<shu>
not _de_duplicate, duplicate
20:32
<rbuckton>
yes.
20:32
<shu>
main.js and worker.js in your example includes two, different inline copies of the layout source text
20:32
<shu>
is that right?
20:33
<rbuckton>
It depends on the bundler. Some bundlers and bundle configurations will just pack everything into a single file per entrypoint. Some bundlers/configurations will use shared entry points.
20:33
<shu>
like... just don't do that?
20:33
<shu>
bundlers can split out a 'layouts.js'
20:33
<shu>
because layouts will be specced to have this behavior
20:33
<shu>
if you duplicate it, that's not a semantics-preserving transformation
20:33
<shu>
so don't do that
20:34
<rbuckton>
Except for tree shaking
20:34
<shu>
tree shaking will be nonobvious in light of multithreading
20:35
<shu>
i do not see this as a problem that needs to be designed around
20:35
<rbuckton>
Tree shaking would mean the bundler can't elide any layout it sees, or any other code in the same file, lest the source positions change
20:36
<shu>
we're fundamentally talking about sharing across all worker threads
20:36
<shu>
sharing layouts requires a whole-world assumption
20:37
<shu>
you can't tree shake individual worker threads' code for shared layouts. the bundler instead needs to generate the set of shared layouts
20:37
<rbuckton>
You might as well define your layouts in a non-JS file, since you can't really put anything else with them for fear it can't be tree shaken to reduce bundle size.
20:37
<shu>
because the point is that they are ... shared
20:37
<shu>
i seriously doubt people want to express this out-of-band
20:38
<rbuckton>
you also have evaluation order issues. Unless we don't allow computed property names in layouts (i.e., no built-in symbols).
20:38
<shu>
there are no evaluation order issues because these are not evaluated, these are declarative
20:38
<shu>
so you are absolutely right, there are no computed property names
20:39
<rbuckton>
Do you need to define all instance fields in a layout?
20:40
<shu>
vs...?
20:40
<rbuckton>
If so, then you wouldn't be able to define symbol-named fields
20:40
<shu>
how do you not define all instance fields in a layout? these things are constructed sealed
20:40
<rbuckton>
I'm saying that if you must define them all ahead of time, and you can't use computed property names, you can't use symbols.
20:40
<rbuckton>
even for nonshared fields.
20:41
<shu>
and i'm saying that sounds good to me
20:41
<shu>
nonshared fields refer to field storage, not field names
20:41
<shu>
the field names are still shared
20:41
<shu>
strings are obviously shared
20:41
<rbuckton>
I disagree.
20:41
<shu>
i don't think symbols are so easy to use shared
20:42
<rbuckton>
Maybe not, but a lot of projects use user-defined symbols on classes currently, including NodeJS. That becomes another stumbling block to migrating to structs.
20:42
<shu>
how do you pass those user symbols around among threads?
20:42
<shu>
since symbols have identity
20:43
<rbuckton>
At the very least, you might be able to require they use symbols from Symbol.for() somehow so that they have the same identity, or you have to somehow correlate those as well somehow.
20:43
<shu>
there is literally 0 reason to use Symbol.for over strings
20:43
<shu>
they are just worse strings
20:45
<rbuckton>
Its very frustrating that threads can't just share the same code, like almost any other language with multithreading.
20:45
<shu>
the original sin is we made code have identity and first-class values
20:45
<shu>
hard to walk that back
20:46
<shu>
it's also very frustrating classes have identity and are first-class values
20:46
<shu>
i'm happy to try to carve out a space where some things don't have identity, like layouts
20:46
<Mathieu Hofman>
Its very frustrating that threads can't just share the same code, like almost any other language with multithreading.
Moddable does it with frozen realms
20:47
<rbuckton>
I don't think that's [functions having identity] so much a problem. It's a problem for sharing, sure, but would that apply to a threading model where you don't have to spin up a whole new copy of your application code.
20:47
<Mathieu Hofman>
https://github.com/Moddable-OpenSource/moddable/blob/public/documentation/xs/XS%20Marshalling.md#full-marshalling
20:47
<shu>
i think it is very much a problem
20:47
<shu>
everything having identity and being first-class values means by default they are not threadsafe
20:48
<rbuckton>
Sure, its not threadsafe. How is that bad?
20:48
<shu>
so... you can't just spin up a new thread without also loading a whole new copy of the world?
20:49
<rbuckton>
Why do you need a whole new copy of the world?
20:49
<shu>
i don't know what we're talkign about anymore, i was responding to your "it's frustrating" comment with my own reasons for why i find it frustrating
20:49
<shu>
we can drop this subthread, not a productive one
20:51
<shu>
back to the declarative layout idea at hand, yes, symbol-keyed names being precluded is a DX con
20:52
<rbuckton>
My point is more that, if we actually baked multithreading into the language, such that you don't have to spin up a copy of your application and could just use existing references, then we wouldn't have the correlation issue. We'd have other issues instead, but they are the pretty much the same issues as any other language with multithreading.
20:53
<Mathieu Hofman>
They are definitely the issues I don't want to see in JS
20:54
<shu>
My point is more that, if we actually baked multithreading into the language, such that you don't have to spin up a copy of your application and could just use existing references, then we wouldn't have the correlation issue. We'd have other issues instead, but they are the pretty much the same issues as any other language with multithreading.
actually agree, but that requires like a parallel SharedFunction prototype chain or whatever, and that bifurcates the world in a weird way that was a non-starter last time i tried
20:54
<Mathieu Hofman>
I've spent some time in golang lately, and for a language that's supposed to make threads easier to deal with, I'm sorry but it was not
20:54
<rbuckton>

but as you say, back to the layout idea. Would this be so bad, though:

shared struct S {
  with identity "46e6d6a9-7e62-46d9-9dfc-6288740eed8c"; // <- correlation at declaration level
  x;
  y;
}
20:54
<shu>

anyway, i can live with something like:

  • we have layouts, which are declarative and static, and are pretty restrictive. but they are deduplicated up front and the correlation thing "just works". bundlers will need to learn they can't be tree-shaken as normal
  • shared struct declarations don't have to use a declared layout. if they don't, then they can have computed property names. you can have a correlation thing built in userland if you go that route
20:55
<shu>

but as you say, back to the layout idea. Would this be so bad, though:

shared struct S {
  with identity "46e6d6a9-7e62-46d9-9dfc-6288740eed8c"; // <- correlation at declaration level
  x;
  y;
}
yeah i can live with it, so long as source location fallback exists
20:55
<rbuckton>
Sure. If that's the case, do we need the layout thing?
20:55
<shu>
why yes, because the 85% use case won't need computed property names
20:55
<rbuckton>
I think it adds far too much complexity.
20:56
<shu>
and the correlation API doesn't??
20:56
<shu>
i am so confused
20:56
<shu>
this seems vastly simpler to use as a developer than manually coordinating
20:57
<rbuckton>

I'm talking about the idea I suggested in the meeting:

  • no correlation api
  • no struct reevaluation (always the same instance in a given thread)
  • declarations correlated by either explicit token (i.e., with identity "foo"), or by source location
20:57
<Mathieu Hofman>
if the layouts are shareable, is it unacceptable from a DX point of view to have factories for the shared struct, so that you create the shared struct after having received the layout ?
20:58
<shu>
rbuckton: i'm hung up on the second bullet-point without the introduction of a static, declarative concept like layout
20:58
<shu>
i don't know what "no struct reevaluation" means
20:58
<rbuckton>
the struct is the layout
20:58
<shu>
but the struct isn't a static thing
20:58
<shu>
it can include static initializers, etc
20:58
<shu>
and computed property names, as we've been debating
20:59
<rbuckton>
My suggestion was that we make struct a static thing, per-thread at least.
20:59
<shu>
it's like a static variable in C/C++ or something? the first evaluation caches it to something, subsequent uses never evaluate it again? that's... really weird, given closures?
21:00
<rbuckton>
Yes, something like that. Yes its weird.
21:00
<shu>
if the layouts are shareable, is it unacceptable from a DX point of view to have factories for the shared struct, so that you create the shared struct after having received the layout ?
no qualms from me?
21:01
<shu>
rbuckton: okay i guess it's possible, i just find those semantics really weird and less sensible than having a separate declarative, static concept
21:01
<shu>
what you're saying isn't static, it's cache-on-first-eval
21:01
<shu>
rather, singleton
21:01
<shu>
i'd prefer static semantics, you're saying singleton suffices
21:02
<shu>
why is singleton semantics needed if you deduplicate with an identity?
21:02
<rbuckton>
no qualms from me?

I don't think this works. that's back to the thing1/thing2 issue. If I can write:

layout L { ... }
shared struct S1 layout L { ... }
shared struct S2 layout L { ... }

then I have two or more potential prototypes to contend with in a given thread.

21:02
<shu>
no you have one prototype
21:02
<shu>
L says "I have a per-thread [[Prototype]] slot"
21:03
<shu>
S1 and S2 refer to the same slot
21:03
<rbuckton>
But S1 and S2 could define conflicting methods with the same names.
21:03
<shu>
that sounds like they have different layouts!
21:03
<rbuckton>
No, that sounds like a very easy to run into user error.
21:04
<shu>
i really do not understand what you're getting at
21:04
<shu>
maybe layout was a poor choice of words here
21:04
<shu>
let's just call it nominal_shape to be unambiguous
21:04
<rbuckton>
I'm having a hard time understanding what it's intending to solve.
21:05
<shu>
the correlation problem!
21:05
<rbuckton>
It sounds like it solves the "v8 internal map" problem, not the correlation problem?
21:06
<shu>
S1 layout L and S2 layout L is intended to behave like, statically, Registry.register(L, S1) and Registry.register(L, S2), where L is the registry key
21:06
<rbuckton>
Ok.
21:06
<shu>
It sounds like it solves the "v8 internal map" problem, not the correlation problem?
those are the same problem to me
21:06
<rbuckton>
So I do both of those in the same thread, what happens?
21:08
<shu>

assuming L has a per-thread prototype declared, you have:

  • S1 is its own constructor function
  • S2 is its own constructor function
  • instances of S1 are indistinguishable from instances of S2
  • S1.prototype is the same slot as S2.prototype, so S1.prototype = foo is also reflected as S2.prototype === foo
21:08
<shu>
the same semantics as if S1 and S2 were in different threads
21:09
<rbuckton>
What belongs to a shared struct S1 layout L {} then? the implementation?
21:09
<shu>
could you clarify what you mean by "belong"?
21:10
<rbuckton>
What is the point of shared struct in this model? What does it bring to the table aside from a constructor function?
21:11
<rbuckton>
In your first example, you showed initializers and methods.
21:11
<shu>
that's one part: shared struct declarations have evaluation semantics, and actually creates the constructor function, because those things are unshareable functions
21:11
<rbuckton>
Ok.
21:11
<shu>
the other part is, because it has evaluation semantics, it could have static initializers and method declarations that install those things onto the per-thread prototype
21:12
<rbuckton>

Now I do this:

shared struct S1 layout L {
  foo() { print("foo"); }
}
shared struct S2 layout L {
  foo() { print("bar"); }
}
new S1().foo();

What should I expect?

21:12
<shu>
if that's the textual order, "bar", as S2's evaluation will overwrite S1's
21:12
<rbuckton>
Does S2 overwrite foo?
21:12
<shu>
backing up, i think the missing context is i consider S1 layout L and S2 layout L to be code that you shuoldn't write
21:13
<shu>
the point of layout isn't to refactor common layouts (really poor choice of words)
21:13
<rbuckton>
Yes, you shouldn't write it, but you can write it.
21:13
<shu>
it's to separate static parts from runtime evaluation parts
21:13
<shu>
yes, and i'm explaining that it'll just have overwriting semantics
21:13
<rbuckton>
And if layout and shared struct must be tied together 1:1, there's no reason they should be separate.
21:13
<shu>
okay, i see our tastes differ substantially here
21:14
<shu>
your view seems to be, it is more important to syntactically bundle them, even if it means the semantics we get are singleton semantics instead of more static-y semantics
21:14
<shu>
my view is, it is less important to syntactically bundle them than to get static-y semantics
21:15
<shu>
why do it static-like if we don't go all the way?
21:15
<rbuckton>
I'm saying that, whatever restrictions we would have on the declaration of layout L {}, we could just have on shared struct S {} and not need the extra confusing separation of syntax.
21:15
<shu>
oh
21:16
<shu>
shared struct {
  // the following 3 lines have static semantics
  with nonshared prototype;
  x;
  y;
  // this has evaluation semantics
  m() { }
}

?

21:17
<rbuckton>
Maybe that means shared struct isn't bundleable, and you need to stripe your bundle to ensure shared structs are always imported from the same place.
21:17
<shu>
that still requires some things you didn't like, like restriction around computed property names
21:17
<rbuckton>
Yes, that's precisely the syntax I proposed in https://gist.github.com/rbuckton/e1e8947da16f936edec1d269f00e2c53
21:19
<rbuckton>
Why do computed property names have to be restricted? I'd like to be able to use [Symbol.iterator], among others, or I can't migrate to shared structs for some objects. And arguably, you'd want to be able to define [Symbol.dispose] as well.
21:19
<rbuckton>
Or [util.inspect.custom]
21:19
<shu>
if the layout portion of a shared struct declaration have static semantics instead of singleton semantics, how do you allow symbols?
21:19
<shu>
symbols do not exist at static time
21:20
<shu>
i have to go to a meeting but something is still very muddled for me here, i don't understand the semantics you have in mind
21:20
<shu>
i don't personally design things syntax first
21:20
<rbuckton>
IMO, if this solution doesn't allow for even the use of built-in symbols, it's not viable.
21:21
<rbuckton>
This isn't even all about syntax, its about what capabilities you are exposing or restricting. I would have the same concern if this was all API based with the same restrictions.
21:21
<shu>
okay, then i think the only viable thing we can both live with is singleton semantics, or a programmatic registry
21:21
<shu>
a static-first approach must have the computed property name restriction
21:22
<rbuckton>
If that's the case, so be it. I don't think I could support a mechanism that doesn't allow them.
21:22
<shu>
yeah that's fine
21:23
<shu>
i think we can make singleton semantics much less confusing by adopting the other restriction you raised during the call, like only allowing these at top-level
21:24
<shu>
can you elaborate on what you had in mind for the singleton semantics? is it keyed off source location? is it only singleton semantics if a with identity 'UUID' modifier is present?
23:02
<shu>
rbuckton: will you be in tokyo btw?
23:24
<shu>
wait a second, isn't there a pretty simple solution to the communication channel problem? if the key to the shared global registry is the combination of source location + with identity 'UUID'
23:24
<shu>
if it's the combination, you can't try to evaluate another definition to test for a collision
23:25
<shu>
it's specced to be a different key and will never collide
23:25
<shu>
Mathieu Hofman: am i missing something? ^