00:03:52  <devsnek>i didn't really understand why his proposal specifies that primordial builtins share a function though
00:04:58  <devsnek>that makes you have to do that awkward fn.call(x) instead of just fn(x)
00:05:29  * AtumTquit (Read error: Connection reset by peer)
00:21:16  * araijoined
02:20:04  * howdoijoined
02:32:14  <ljharb> devsnek it didn’t get stopped. He was asked to come back with more use cases and he never came back, and then withdrew it.
03:08:13  <devsnek>ljharb: is there a reason the term "builtin" was chosen
03:08:30  <devsnek>for the symbol
03:15:43  <ljharb>devsnek: i dunno, that’s actually the main thing i dislike about it :-) that name doesn’t work because nay object can participate.
03:16:14  <devsnek>ye that's what confused me
03:16:23  <devsnek>but I'm rewriting it a bit anyway
03:16:30  <devsnek>name bikeshedding galore
03:50:05  * IgnoredAmbiencequit (Ping timeout: 240 seconds)
04:22:52  * keith_millerquit (Quit: My MacBook has gone to sleep. ZZZzzz…)
04:26:09  * keith_millerjoined
04:41:02  * jmdyckquit (Remote host closed the connection)
04:48:41  * keith_millerquit (Quit: My MacBook has gone to sleep. ZZZzzz…)
04:51:30  * keith_millerjoined
08:01:11  <annevk>ljharb: I think it needs to be a list, to support subclassing
08:01:46  <annevk>ljharb: e.g., you want an easy answer to is this HTMLAnchorElement object an Element
08:02:24  <annevk>ljharb: without a registry of all things that are subclasses of Element
08:24:05  * keith_millerquit (Quit: My MacBook has gone to sleep. ZZZzzz…)
08:24:53  * IgnoredAmbiencejoined
08:41:16  * keith_millerjoined
08:56:15  * araiquit (Remote host closed the connection)
09:45:11  * keith_millerquit (Quit: My MacBook has gone to sleep. ZZZzzz…)
10:25:08  * mylesborinsquit (Quit: farewell for now)
10:25:39  * mylesborinsjoined
11:22:36  * keith_millerjoined
12:18:18  * jmdyckjoined
12:41:42  * AtumTjoined
13:15:19  * keith_millerquit (Quit: My MacBook has gone to sleep. ZZZzzz…)
13:28:30  * howdoiquit (Quit: Connection closed for inactivity)
13:33:18  <devsnek>thats what i meant with like walking up prototype chains
13:33:58  <devsnek>it gets really messy though cuz eventually everything is an object or `undefined`
13:34:59  <annevk>devsnek: you cannot walk up prototype chains
13:35:22  <annevk>devsnek: if I do instance.__proto__ = null this brand check should still work for the instance
13:35:39  <annevk>(assuming you want to expose the underlying primitive, that is)
13:35:54  <devsnek>so there needs to be magic secret sauce
13:39:33  <annevk>internal slots are pretty common
13:39:58  * isHavvyquit (Read error: Connection reset by peer)
13:40:28  <devsnek>how would someone fake it then
13:40:42  <devsnek>i think pretending to be a Date or a Map or something is an important feature to have
13:42:35  * araijoined
13:42:54  * Havvyjoined
13:48:08  <devsnek>also now that i think about it, if you kill the prototype of something its not really that thing anymore
13:57:34  <annevk>sure it is
13:58:05  <annevk>being able to pretend certain objects would be bad for security
14:04:23  <bradleymeck>devsnek: you can see an example here https://codepen.io/anon/pen/dejMdJ?editors=0012 , internal slots are like private access things that hosts/spec can use but JS itself cannot
14:07:04  <devsnek>oh i know what internal slots are
14:08:04  <devsnek>i'm just trying to also balance in userland
14:08:13  <devsnek>userland doesn't have internal slots
14:14:10  <bradleymeck>private fields can act like them
14:14:29  <bradleymeck>sharing them gets wonky though for now
14:15:15  <bradleymeck>you could also emulate them with weakmaps if you wanted to really; it looks different at a glance, can act the same though.
14:20:36  <devsnek>https://gist.github.com/devsnek/7e8d4a37c774819639088648ee9d1180
14:21:47  <devsnek>now ljharb's point about being able to save <something> that will always tell you if `x` is a Map or Array or something
14:32:00  <devsnek>without that safety i came up with this: https://gist.github.com/devsnek/dd77b256d50a80570541dd4ab74bfc27
14:32:04  * keith_millerjoined
14:34:16  <caitp>can we have a new rule that `function factory(v) { return new class { #private = v; value() { return this.#private; } } }; factory("x").value.call(factory("y"));` returns "y" instead of throwing?
14:35:40  <devsnek>well its two separate classes
14:35:58  <devsnek>no connection whatsoever beyond the shape
14:36:34  <caitp>that's besides the point, the point is that it will let me get away with treating the names as constant without breaking that edge case that I don't really care about
14:36:58  <bradleymeck>which edge case?
14:37:42  <caitp>the case where 2 distinct classes come from the same source code, sharing private symbols instead of having different versions of the same symbols
14:38:03  <devsnek>but its explicitly two separate classes
14:38:11  <bradleymeck>if they are different wouldn't it mean that it wouldn't return "y"?
14:38:34  <bradleymeck>it sounds like they would need to be the same to have it return "y" not different
14:38:35  <caitp>the spec says it's not allowed
14:38:49  <caitp>but, I'd rather it is allowed because it lets me get away with some simpler stuff
14:39:18  <devsnek>if you're producing different classes the symbols should be different
14:39:37  <devsnek>the symbols are within the scope of the new class
14:40:14  <devsnek>if you wanted to share something between them you should create some sort of key or cache outside the individual scope
14:40:41  <caitp>they should be per the spec, but I don't think they should be really
14:40:51  <bradleymeck>caitp: that would make private fields friendly in the same source location, which seems odd when the classes are different instances / means you need to have new source locations or tracking fields for comparing identity for non-friendly classes
14:40:51  <caitp>it doesn't really give us anything special
14:41:17  <bradleymeck>caitp: it lets you avoid doing identity checks for non-friendly classes generated using the same utility functions etc.
14:42:37  <devsnek>wasn't someone going to propose private symbols that could be used on any object
14:45:24  <bradleymeck>there is an email going around with ideas about `private #foo;` declarations
14:45:31  <bradleymeck>no proposal yet
14:46:33  * gibson042joined
14:46:37  <bradleymeck>that might get a big confusing with the source location is identity idea
14:48:04  <devsnek>i think a weakmap or `private #foo` would be more obvious approaches
14:48:50  <devsnek>source location as identity isn't obvious unless you explicitly know about that behaviour
14:52:03  <bradleymeck>source location as identity doesn't seem insane, just not how other things work (except kind of template string objects...)
14:56:27  <caitp>bradleymeck: what exactly is "unfriendly" about another class declared in the same source location?
14:57:09  <bradleymeck>two classes generated by a utility function may not be intended to have access to each other's private data
14:57:13  <caitp>it seems like that wouldn't be the common case
14:57:23  <caitp>but then, they could just not access each other's private data
14:57:39  <caitp>because whoever wrote that utility function owns that code and can decide to read or not read it
14:57:41  <devsnek>thats how underscore properties work today
14:57:43  <bradleymeck>i think it not matching how classes etc. work is more concerning since it makes differences in how people need to think about allocation/identity
14:57:48  <caitp>maybe that would involve a brand check, but who cares?
14:58:42  <bradleymeck>caitp: mostly concerned about how it seems to go against other language design choices, not so much edge casey nature of your argument
14:59:03  <bradleymeck>JS is already hard enough to learn, making more mental models needed seems something we should avoid
14:59:34  <caitp>I dunno, maybe it's weird to people who come from haskell or something
15:00:30  <caitp>but that particular "weird" seems more natural
15:02:48  <bradleymeck>i'm not coming from haskell?
15:02:59  <caitp>what I want to do is have the private field names embedded in the baseline code itself (and definitely embedded in jit code), without needing cache invalidation if the outer lexical scope is different from an earlier invocation of the same function
15:03:24  <caitp>or anything in the scope chain
15:03:46  <caitp>so making source location == identity gives us that
15:04:04  <caitp>makes life better
15:04:09  <bradleymeck>that seems to be in conflict with functions/classes getting a new instance per time declaration is reached, and how variable bindings aren't shared between inner functions
15:04:12  * srl295joined
15:04:22  <caitp>private is still private for real-world intents and purposes in that scenario,
15:04:39  <bradleymeck>it seems very much a CSS like idea of designing the language rather than JS to have these shared slots
15:04:51  <caitp>and for cases where a little "extra" private is needed (which I think are rare/nonexistent), you can add a brand check
15:05:02  <caitp>not making the weird/complex behaviour the default just makes sense
15:05:17  <bradleymeck>how is the behavior of getting new slots like all the other JS contructs weird?
15:05:20  <bradleymeck>constructs*
15:05:40  <bradleymeck>should have said instance instead of slot probably
15:05:53  <bradleymeck>we don't return the same function for nested ones
15:06:00  <bradleymeck>why do that for private fields?
15:06:09  <bradleymeck>same for Symbol creation
15:07:14  <caitp>the use case for making `factory("x").value.call(factory("y"))` throw, is weird
15:07:18  <caitp>I don't think that's something people really care about in practice
15:07:31  <caitp>and for the few cases where people do want that, they can get it other ways
15:08:15  <caitp>language design is about how people use the language, not about consistency with how it's modelled on the inside, which has been crazy from the get-go
15:08:20  <devsnek>when i saw your example
15:08:29  <devsnek>i immediately assumed that `factory("x").value.call(factory("y"))` should throw
15:08:44  <caitp>why would you think that
15:09:00  <devsnek>because its two completely separate classes
15:09:09  <devsnek>that happen to have the same shape
15:09:12  <caitp>yes, but so what
15:09:15  <caitp>it doesn't really matter
15:09:26  <caitp>none of that really takes away the private-ness of those fields
15:10:20  <caitp>if you care about them being distinct, and you probably don't, but if you do, you should make it explicit that they're distinct
15:10:38  <caitp>imo
15:11:33  <bradleymeck>i disagree but idk, different language backgrounds
15:13:58  <caitp>do you agree that if you are maintaining factory() and the class instance returned from it, you have full control over whether it accesses private fields from the same instance or different ones, and if it throws or not?
15:14:35  <caitp>and then you can do whatever you want with it, while still letting your friendly neighborhood implementer get away with a simple way to do it that depends less on lexical nonsense?
15:15:12  <caitp>and outside stuff that it actually needs to be protected from still can't get at it?
15:15:36  <devsnek>`you should make it explicit that they're distinct`
15:15:49  <devsnek>how would you explicitly make the private symbols distinct if they cache by source position
15:15:59  <caitp>by doing a manual brandcheck
15:16:23  <devsnek>but then they aren't private anymore
15:16:39  * keith_millerquit (Quit: My MacBook has gone to sleep. ZZZzzz…)
15:16:43  <devsnek>they're just things with special names that are private by convention
15:16:54  <devsnek>like underscore properties
15:17:28  <caitp>`factory(v) { const MYBRAND = Symbol("derp"); class { #myBRAND = MYBRAND; ... }` << you always know your private fields actually belong to you if this.#myBRAND === MYBRAND
15:17:31  * jwaldenjoined
15:17:53  <caitp>so you have an easy way to check if access is valid or not, _if_ you care, which you don't
15:17:57  <caitp>because why would you
15:18:44  <devsnek>MYBRAND is a new symbol in this context
15:19:14  <devsnek>that falls back on bradley's point about what you expect from existing language features
15:19:49  <caitp>...
15:19:50  <caitp>what
15:21:03  <caitp>`if (this.#myBRAND !== MYBRAND) throw new BadAccess("You can't call this method with this receiver, sorry!");` and using a constant shared by all instances of the same code for the private symbol, instead of requiring dynamic scope lookups
15:21:22  <caitp>for the really small number of users who care about that behaviour, it's easy to get
15:21:32  <caitp>for everyone else, simpler is better
15:21:37  <devsnek>ok wow that just
15:21:44  <devsnek>ties my brain in knots
15:23:05  <devsnek>why not `const sharedData = new WeakMap(); function factory(v) { return new class { constructor() {sharedData.set(this, 'x')} value() { return sharedData.get(this); } } }`
15:23:24  <devsnek>s/'x'/v
15:24:34  <devsnek>i think you're trying too hard to use private symbols
15:24:46  <bradleymeck>caitp: can do that, but I think it doesn't make sense from the existing language constructs
15:24:48  <caitp>uh
15:25:16  <bradleymeck>it is confusing to me given all my time coding JS
15:25:31  <caitp>use them for what? I'm implementing the feature, and I'd rather implement it in a way that lets me use constants embedded in code instead of doing dynamic lookups
15:25:40  <devsnek>it took me a solid 15 seconds of just staring at that brand check to understand it
15:25:41  <caitp>so lets change the proposal to let me do that
15:25:55  <caitp>everybody is happier that way
15:26:01  <ljharb>i think what you’re suggesting would break bundling use cases
15:26:18  <ljharb>that two classes are in the same scope absolutely can’t mean they can share private data implicitly
15:26:23  <bradleymeck>caitp: I would not be happier since it doesn't match how other language constructs work
15:26:28  <caitp>how did it take you 15 seconds to understand "if this is not my brand, throw"?
15:26:49  <caitp>are you using a monospace font in your IRC client?
15:26:51  <ljharb>the brand is per-class tho, not per-scope.
15:27:14  <bradleymeck>caitp: i think that is the question you need to solve, why it doesn't appear to match and takes time to recognize what/why you need that check
15:27:34  <caitp>ljharb: if you declare 2 classes in the same space, they can't access each others #myBrand field, so isn't that kind of a moot point/
15:28:00  <ljharb>hm, maybe I’m misunderstanding what you’re suggesting then
15:28:00  <bradleymeck>caitp: wait... doesn't that go against your examples?
15:28:32  <caitp>if you can read #myBrand, you know the class is the the same one (in terms of where it appears in source), so you can use the SYMBOL to check if shares the same lexical scope or not
15:28:41  <caitp>which gives you the complete identity
15:28:41  <ljharb>a class defined in a factory method is a different class every time
15:28:56  <ljharb>this was discussed in committee and we explicitly went with the current state, iirc
15:28:58  <caitp>...yes, and you can use the symbol to do a brandcheck there
15:29:05  <caitp>if you really want that, which you probably don't
15:29:17  <ljharb>iow, we explicitly chose that source location didn’t give you sharing across factory calls
15:29:28  <caitp>99/100 you won't care about that, and won't even be declaring classes in a closure
15:29:58  <caitp>yeah we explicitly chose it, but lets go back and rewrite that history so that I can get away with embedding constants in the code instead of the dynamic lookup
15:30:03  <ljharb>there’s a hazard if two react components created by the same HOC, and that’s not 1/100 - it’s much more commo /100
15:30:18  <ljharb>no. That would break react HOC patterns and i would not allow that change to go in.
15:30:32  <caitp>if they want the brand check, they can do it themselves
15:30:34  <caitp>no harm no foul
15:30:38  <caitp>most of the time people won't care
15:31:16  <bradleymeck>caitp: the harm is that you need to figure out how to make it match all the other design choices about allocating new things when they are encountered rather than sharing static instances across invocations
15:31:17  <ljharb>but when they do care it would silently break encapsulation. That’s not an edge case, that’s a failure of the security model.
15:31:32  <caitp>I don't think matching the other design choices is that important
15:31:50  <bradleymeck>caitp: i do~ and that would be a big deal for me
15:32:14  <caitp>it makes them more like strings that can only occur within a particular area in source code
15:32:45  <bradleymeck>so use strings for those things?
15:32:57  <ljharb>these aren’t meant to be like strings.
15:33:00  <caitp>"more like" strings, but not strings
15:34:34  <bradleymeck>idk, this all seems to be making language decisions that don't match the rest of the language and have concerns about sharing private data just for a specific compiler optimization
15:34:55  <caitp>you can always statically resolve the symbol to use, but you can't embed the right symbol in code unless the symbol stays the same even if it's used on an instance of the same class source declared in a different closure invocation
15:34:59  <bradleymeck>if there was an argument that the current model makes everything slow it seems it might be a more persuasive angle
15:35:14  <caitp>embedding the symbol in code is good and simple, ergo we should do that
15:35:27  <bradleymeck>caitp: that point is not simple or obvious to me
15:35:45  <bradleymeck>it carries a lot of other arguments so I don't naively see that we should do that
15:36:46  <caitp>the alternative is walking the scope chain every time it's used, which is doable, but I'd rather not :D and we don't lose anything of substance by not doing that
15:37:16  <bradleymeck>i see substance lost
15:37:40  <bradleymeck>consistency and learnability are increasingly bigger concerns as JS gets more complex
15:37:51  <caitp>the _only_ difference is that in the rare cases where you care about the 2 classes not being able to access each others private fields, you have to add that rule yourself
15:38:00  <caitp>which is really not that big of a deal
15:38:10  <ljharb>as do i. just because i call an HOC twice doesn’t mean one component should be able to steal the private data of another made in the same factory
15:38:17  <ljharb>it’s not rare
15:38:27  <ljharb>class factories are common, especially with HOCs in react
15:38:56  <caitp>if you don't want the component to steal its own private data that it defined itself, from itself, then the component should just not do that
15:39:01  <ljharb>and “you have to add that rule yourself” is a foot gun, and goes against the idea that private fields are private by default. You should have to do extra work to expose, not to keep secret
15:39:19  <devsnek>"should just not do that" you might as well just use known property names then
15:39:30  <caitp>it's not the same as a property name
15:39:33  <devsnek>it invalidates privates as private
15:39:37  <caitp>property names are exposed to the world
15:39:44  <caitp>privates are exposed to not-the-whole-world
15:40:02  <caitp>but making them different in each closure invocation really doesn't add anything important
15:40:14  <caitp>like, it really doesn't
15:40:20  <ljharb>Privates are exposed only to that class - each time you call a factory it’s a distinct class.
15:40:30  <bradleymeck>caitp: there is problems beyond how common something is, I think you are making claims of how common this use case is, but need more data on it to show it as uncommon and that the mental model mismatch is worth it
15:40:45  <ljharb>it does add something important, and enough of us in the committee we’re convinced it does to make that decision.
15:41:22  <caitp>does React _really_ care if SomeFancyMenu can read #menuName from a different copy of the same component
15:41:42  <ljharb>not react. The user of the HOC
15:41:50  <ljharb>like imagine react-redux’s connect
15:41:56  <bradleymeck>caitp: you could gather data, but not sharing by default seems a reasonable encapsulation choice
15:42:08  <bradleymeck>for private at least
15:42:24  <ljharb>It would really really break a lot of things if any two connected components could access private state defined in the Connect HOC
15:42:36  <ljharb>each other’s state, i mean.
15:42:40  <caitp>bradleymeck: I don't consider "sharing with yourself" to really be "sharing" or "leaking" anything
15:43:03  <bradleymeck>caitp: when I write utilities / mixins it is more than myself
15:43:15  <ljharb>You can say “just don’t do that” but that means that factories/HOCs can’t ergonomically use private state without also using a WeakMap
15:43:24  <caitp>if you declare a private field in the mixin, your mixin is still the only thing that can access it
15:43:34  <caitp>because it doesn't mean the same thing to anybody else
15:44:01  <bradleymeck>caitp: yes, but now you need to protect from leaking your well known constants for checking, and check on every access
15:44:06  <ljharb>the mixin or any instance methods on the newly created class that have access, sure
15:44:09  <caitp>so, you can read the same data from other versions of the same mixin, but it's the same mixin, so it's sharing with yourself
15:44:11  <caitp>that's not a leak
15:44:45  <bradleymeck>caitp: I'm not entirely convinced that it isn't for when other people are creating classes through your utility
15:44:50  <ljharb>it’s a leak in the sense that they’re distinct classes that only happen to share the same source location
15:44:52  <caitp>conceptually no information is shared
15:45:09  <ljharb>it’s class-private, not lexically-private. That’s part of the design.
15:45:17  <bradleymeck>caitp: it puts burden on not-sharing private data to people using private fields
15:45:26  <bradleymeck>which seems... unusual
15:45:30  <caitp>it doesn't really
15:45:42  <caitp>I mean it does in the sense that they have to watch out if they don't want to share with themselves
15:45:49  <bradleymeck>caitp: i will disagree harshly on that point given that you have to custom code brand checks etc
15:46:09  <caitp>but in every other way, and the more common and important ways, they get the free privacy
15:46:21  <bradleymeck>i don't agree on free
15:46:51  <bradleymeck>you get a form of privacy that is shared between all classes in the same location and all classes of the same location need to add extra code in order to not share
15:46:52  <caitp>class C { #x = 1; } let secret = new C; C.#x = 2; << still illegal
15:47:03  <bradleymeck>it is closer to a friendly model than what i would expect of a private model
15:47:28  <bradleymeck>caitp: sure but my points remain about it not being private, but closer to friendly
15:47:56  <caitp>so what is a case where this information is shared between 2 classes in the same location, that is actually a problem?
15:48:03  <bradleymeck>anywho gtg for a bit, you can open issues in other places on github maybe
15:48:43  <bradleymeck>caitp: defining what you mean by "actually" would be hard without claims of how the existing model has invalid use cases
15:48:55  <bradleymeck>maybe make issues that such use cases are invalid on github?
15:50:12  <ljharb>anything where my factoried class has a brand checking method
15:50:58  <ljharb>i should never have to put forth effort to maintain privacy, the effort should be required to share. It’s not “myself” I’d be sharing with, it’s the two distinct classes that would easily be sharing with each other
15:51:12  <ljharb>if i *want* them to share, i should use what JS has for that: lexical scope
15:52:10  <ljharb>the functions returned from a HOF shouldn’t share variables defined within the new function’s body, either.
16:25:09  * keith_millerjoined
16:34:06  <caitp>ljharb: I mean, you can add all the brand checks you want, the question is are they really needed --- is your private data (or some use or mutation of it) able to escape outside the class, in such a way that leaks sensitive information, for ex.
16:35:17  <caitp>leaking to another "version" of the same class is fine, because you wrote that class, you're still in control of it and anything that happens to it
16:35:21  <caitp>so that's kind of a non-issue
16:38:19  <caitp>anyway, I don't think I care that much if I can get the spec changed, but implementor-wise it would be a nice change.
16:41:17  <Bakkot>caitp: this was discussed in https://github.com/tc39/proposal-class-fields/issues/60
16:44:39  * keith_millerquit (Quit: My MacBook has gone to sleep. ZZZzzz…)
16:47:38  <caitp>I've read that thread, and I think the arguments from miller & allen are not as compelling as they think they are
16:47:54  * keith_millerjoined
16:49:04  <caitp>the Mint/Purse thing is a perfect example of "make an explicit brand check in a case where it actually matters"
16:50:18  <Bakkot>You can argue that the author of that code ought to have written different code, but I don't think you can fault them for expecting it to work the way they intended. Principle of least surprise and all that.
16:50:53  <caitp>I don't think anyone has any reason to expect property names to behave like captured variables
16:50:58  <devsnek>"whoops I forgot to manually enforce these private symbols being private"
16:51:18  <devsnek>they aren't property names
16:51:18  <Bakkot>caitp: the privacy model is based on lexical scope
16:51:27  <Bakkot>which seems like a very good reason to expect them to behave like captured variables
16:51:28  <devsnek>they're symbols
16:51:36  <caitp>symbols are property names
16:51:50  <devsnek>property keys maybe
16:52:04  <caitp>ToName(v) will return v when v is a Symbol
16:52:30  <caitp>it's a property name, for all intents and purposes --- it has meaning tied to a specific lexical context, but it's not a variable or binding
16:53:26  <devsnek>right but they aren't strings is my point
16:53:35  <caitp>yes, but that's... not important
16:53:46  <caitp>really, that doesn't matter at all to this discussion
16:53:59  <devsnek>two symbols from different lexical scoped are two different symbols
16:54:11  <caitp>they don't have to be.
16:54:16  <caitp>there is no analogue in the language to compare it to
16:54:22  <devsnek>yes there is
16:54:25  <caitp>no, there isn't
16:54:29  <devsnek>Symbol()
16:54:41  <caitp>a private name is not a Symbol()
16:54:59  <devsnek>no but they behave and are used similarly
16:55:06  <caitp>they don't behave similarly
16:55:15  <caitp>they aren't used similarly
16:56:05  <devsnek>I think there's some huge difference in viewpoint here that arguing this won't fix
16:56:21  <caitp>lets talk about the differences
16:56:48  <caitp>if instead of a private field I use a lexically declared Symbol, will that symbol leak if I do Object.getOwnPropertyDescriptors()?
16:56:49  <Bakkot>caitp: in your issue you say that the current design requires you to walk up the scope chain
16:56:50  <Bakkot>why this?
16:57:15  <caitp>will it leak if I use a private field?
16:58:01  <caitp>is it possible for me to alias the Symbol / bind it to a different location, like `let x = symbol;`? is that possible with private fields?
16:58:27  <caitp>can the bound Symbol change at different times? can the PrivateField be bound in the first place?
16:58:50  <caitp>they're different in the most important ways
16:59:29  <caitp>having a private field behave like a captured variable isn't really any less surprising than the alternative, because there is no analog for them
16:59:51  <caitp>they aren't variables, so why would they be captured?
17:06:20  <Bakkot>caitp: because they're private to the class. if there's two classes, I expect theres
17:06:23  <Bakkot>two variables.
17:06:36  <Bakkot>"private to the source text of the class" is... not a concept I have in my head.
17:06:50  <Bakkot>er, two *names, rather
17:06:52  <caitp>why do you expect theres variables?
17:06:58  <caitp>you didn't declare any variables did you?
17:07:03  <caitp>you can't access those variables directly?
17:07:12  <Bakkot>sorry, "variables" was a typo, I meant "names".
17:07:26  <caitp>implementation-wise we're treating them as variables, but in the language sense, they aren't that
17:07:41  <caitp>there's no good reason we need to make them behave like that, it's not more or less surprising
17:07:51  <caitp>so what it comes down to is ease of use
17:07:57  <caitp>and implementation sense
17:08:00  <Bakkot>I very much do not share your intuitions about which of these is more surprising
17:08:25  <caitp>I didn't say one is more surprising, I said they're both equal, because there's no analog in the language
17:08:40  <caitp>there's no good reason why a property name should be captured
17:08:42  <devsnek>i think the language enforcing that private things are private is like the bare minimum
17:08:50  <caitp>or have dynamic scope
17:09:14  <Bakkot>right, and my position is that the proposed change would be much more surprising than the current behavior, at least to me
17:09:15  <caitp>in terms of the historic features of the language, I mean
17:09:31  <caitp>so what's surprising about it?
17:09:48  <Bakkot>caitp: still want to understand what you mean by "dynamic scope" here, or why scope would have to participate at all at runtime
17:10:00  <devsnek>i would be surprised if private names leaked between separate classes
17:10:10  <devsnek>especially when they're wrapped in the class's closure
17:10:57  <caitp>in the case we were talking about earlier, where you return a class or instance of a class from a factory function, and private names declared in that class have to be different for each evaluation of the class, the dynamic scope is one class evaluation vs the next
17:13:14  <caitp>devsnek: they aren't separate classes, they're separate evaluations of the same class --- it's like a closure in a js implementation, it'll have the same class will share a lot of stuff when it's evaluated multiple times, so implementations create data structures to allow for that sharing
17:13:17  <Bakkot>caitp: at a very high level, because I expect private names to be per-class, not per-class-source-text; I never expect the language to have a concept of "per-*-source-text". for example, I do not expect the language to distinguish between `let cs = [class{}, class{}]` and `let cs = []; for(let i = 0; i < 2; ++i) cs.push(class{})`, and would be surprised if it did. at a lower level, because my mental model for how privacy of private
17:13:18  <Bakkot>fields works is that it works by lexical closure, which it does currently, rather than some new concept of private-to-field.
17:13:27  <caitp>yes, different evaluations of the same class have different identity, sure
17:14:05  <caitp>but, given that it's the same class, it's really not that big of a deal if it leaks its private names to other evaluations of itself
17:14:09  <Bakkot>caitp: I think it's misleading to reason from "implementations create data structures to allow for that sharing". I don't think that does or really should participate in how people think about the semantics of the language.
17:14:14  <devsnek>it's not the same class
17:14:32  <caitp>it is the same class, just a different evaluation of the same class
17:14:38  <devsnek>no it's not
17:14:46  <devsnek>it's not the same class
17:14:58  <caitp>if you put your dynamic language hat on, then it stops being the same class because you're thinking about what's happening at runtime versus what's happening statically
17:15:29  <devsnek>we can't just ignore what happens at runtime
17:15:33  <caitp>sure we can
17:15:41  <caitp>it's totally up to us to ignore what happens at runtime in this case
17:15:44  <caitp>and we'd be better off if we did
17:16:09  <caitp>marginally
17:16:36  <caitp>and why, because for the common cases we should see a 5-10% boost
17:16:41  <caitp>and for uncommon cases, who cares
17:17:11  <devsnek>boost of what
17:17:16  <devsnek>confusion?
17:17:18  <caitp>that runtime speed
17:17:21  <caitp>go fast
17:17:28  <caitp>paint your js engine red
17:18:11  <devsnek>you want to speed up engines by caching the creation of private symbols by source position
17:18:36  <caitp>no, I want to speed up the engine by embedding the symbol in code that uses that symbol
17:18:57  <caitp>so that, instead of doing a dynamic lookup for that symbol, I just say "here's a pointer to the symbol I want, use it"
17:18:57  <devsnek>that philosophy applies to literally any created thing
17:19:44  <Bakkot>caitp: still would like to understand what you mean by "dynamic lookup" here
17:20:30  <caitp>Bakkot: you're only generating one code for all evaluations of that method that uses the private field
17:20:51  <caitp>if the private field is a different name in each of those evaluations, you have to look it up, it's not constant
17:21:01  <caitp>if we make it constant, we avoid that
17:21:03  <devsnek>you're designing this backward
17:21:14  <Bakkot>caitp: suppose I have `function f(){ let x = Math.random(); return class { m(){ return x; } }; }`
17:21:14  <devsnek>make a good interface and then optimise it
17:21:18  <devsnek>not the other way around
17:21:28  <Bakkot>is the lookup of `x` dynamic in the sense you mean?
17:21:33  <caitp>I don't think this is any better of an interface
17:21:45  <caitp>as awb said, there are pros/cons to both
17:22:11  <devsnek>the tiny perf boost doesn't seem worth the confusion and lack of privates bring private
17:22:22  <caitp>where are you getting confused though my dude
17:22:32  <caitp>like, what is the confusion
17:23:15  <caitp>it's either one way or the other, one is a little bit faster, less memory used, etc, but a little bit less dynamic
17:23:17  <devsnek>that something would be determined from source position
17:23:20  <devsnek>is like
17:23:42  <devsnek>weird for js
17:23:54  <devsnek>add on top of that that privates aren't private anymore
17:24:06  <caitp>weird for js, totally natural for the humans writing the js
17:24:15  <caitp>no, they're still very private
17:24:38  <devsnek>it's not natural for the humans either
17:24:44  <caitp>yes it is
17:25:04  <caitp>it's more declarative
17:25:18  <caitp>"this is a field that can be accessed by code between these squiggly lines, and nothing else"
17:25:40  <caitp>versus "this is a field that can be accessed by code in this particular instance of execution of code between these squiggly lines, and nothing else"
17:26:07  <caitp>humans have an easier time with the former
17:26:12  <caitp>and i you think they don't, you're wrong
17:26:56  <devsnek>well I think you're wrong so there's clearly some misunderstanding
17:27:08  <devsnek>for someone
17:27:20  <devsnek>why don't you open an issue on the repo and get more opinions
17:27:31  <caitp>I did
17:27:47  <devsnek>what did the opinions say
17:27:55  <caitp>nobody has looked at it yet
17:28:13  <devsnek>I guess we wait then
17:31:48  <TabAtkins>I'm definitely on the side of Bakkot's "I wouldn't expect these two examples to work differently".
17:34:28  <caitp>this is similar to some of the discussion about tagged template callsite caching
17:36:24  <Bakkot>the tagged template callsite caching thing is also surprising, but at least there it's a language feature which is actually *about* source text, so the language distinguishing between two identical source texts in different locations is at least not totally insane
17:36:38  <caitp>where "let cache = [cacheMyTag`1`, cacheMyTag`1`]" produces different results from "let cache = []; for (var i = 0; i < 2; ++i) cache.push(cacheMyTag`1`);" now
17:36:50  <caitp>(but historically didn't)
17:38:03  <caitp>well hey, why shouldn't it be about source text? why shouldn't we use statically resolvable private names, instead of runtime-resolvable ones?
17:38:19  <caitp>you get all sorts of benefits from doing it statically
17:38:48  <TabAtkins>I'm sure, but it goes against everything we've ever learned about how properties work.
17:38:48  <caitp>you get a few benefits from doing it at runtime too, sure, but not as many, imo?
17:38:51  <Bakkot>I claim "private fields" is a concept which already exists in the world, and is already not about source text
17:39:00  <Bakkot>we are not in a position to change that
17:39:13  <caitp>in which world?
17:39:17  <caitp>js world?
17:39:25  <Bakkot>the whole world
17:39:56  <caitp>the same concept exists both ways, in the whole world, in different contexts
17:40:52  <caitp>so some people will find one version more appealing, some won't
17:40:55  <caitp>that's ok
17:43:22  <caitp>but if we did go with the "source text caching" thing, is it not true that you could get back the per-class-evaluation semantics in userspace with a 2 line if-throw?
17:44:21  <TabAtkins>The current notion of "private fields" that JS can have (closure variables) will provide the same behavior for Bakkot's two examples if the closure is tightly-wrapped (distinct for each instance); you'll only get a shared variable if you explicitly put the closure at a higher point in the tree.
17:49:16  <TabAtkins>(And I find any analogy with tagged template source-text caching unconvincing, because that's generally a corner feature that very few authors ever need to worry about; if it's confusing (and I don't know if it is), it'll be encountered rarely, so optimizing for perf of *all* template strings over usability can make sense. The calculus is flipped for private fields, I think - particularly when classes are produced by a
17:49:16  <TabAtkins>function, having all of them share the same private state would be *very* confusing.)
17:51:00  <devsnek>what is this confusing behaviour about tagged template literals that people keep mentioning
17:51:23  <caitp>the caching behaviour of the tagged template callsite changed a while ago
17:51:56  <devsnek>i've never encountered anything surprising while using them
17:52:02  <Bakkot>devsnek: one sec
17:52:14  <caitp>see the "cacheMyTag" example above
17:52:42  <caitp>if you try that in chrome 65 vs chrome 55 you get different results
17:52:57  <devsnek>with the literal `1`
17:53:04  <Bakkot>devsnek: ``` let cache = []; let tag = a => cache.push(a); tag``; [0, 1].map(()=>tag``); cache[0] === cache[1] /* false */; cache[1] === cache[2] /* true */; ```
17:54:38  <devsnek>wait its the same array for the second two?
17:54:44  <Bakkot>yes.
17:54:48  <caitp>it is now
17:54:53  <Bakkot>it always was
17:54:59  <devsnek>yes that is definitely surprising
17:55:00  <Bakkot>the new thing is that it is *not* the same array for the first two
17:55:01  <caitp>right, sorry I got that backwards
17:55:04  <caitp>the first one is false now
17:55:15  <caitp>but didn't used to be :p
17:55:19  <devsnek>weird
17:55:33  <devsnek>but lets not use the excuse "confusing stuff happened so we can do more of it now"
17:55:46  <caitp>I still don't really see it as confusing
17:55:55  <caitp>it's similar to most static typed languages
17:56:07  <caitp>not java, afaik?
17:56:09  <caitp>but maybe java too
17:56:34  <devsnek>i would expect tagged literals to always get new arrays
17:56:44  <devsnek>but whatever
17:59:54  <caitp>anyway, fiiiine, I'll not embed constants in code and be sad about that
17:59:55  <caitp>:[
18:10:43  <TabAtkins>,3
18:10:45  <TabAtkins><3 rather
18:11:59  <gsathya>caitp: I was pretty sad about having to do a double lookup too, but I've made my peace with it.
18:13:04  <gsathya>But, I agree with Bakkot, this is not the behavior I expected.
19:06:59  * keith_millerquit (Quit: My MacBook has gone to sleep. ZZZzzz…)
19:13:48  * keith_millerjoined
19:54:39  * AtumTquit (Ping timeout: 260 seconds)
19:54:58  * AtumTjoined
21:43:17  <Bakkot>Question: why does everyone implement function.caller? is it actually required for web reality?
21:43:25  <Bakkot>if so, shouldn't we spec it better?
21:44:10  <devsnek>Domenic: i love love love your blocks proposal
21:44:14  <devsnek>just wanted to say that
21:44:27  <Domenic>Thanks!
21:44:38  <Domenic>Bakkot: I think we should spec function.caller and stack traces better. Hard job.
21:45:15  <devsnek>stack traces need some love
21:46:32  <ljharb>the proposal is happy to accept more cochampions :-)
21:46:41  <ljharb>once the initial one goes in, it opens the door for a lot of followups to improve things
21:58:16  <devsnek>Domenic: did you consider making blOcks create workers instead of requiring a worker to run them?
21:58:32  <Domenic>devsnek: it's really up to the creator of the worker() function what they do.
21:59:07  <devsnek>well they're kinda just parsed code that happens to be transferable right?
21:59:20  <Domenic>Yeah so its up to you what you do with that code
21:59:30  <Domenic>I think that'd be a bad idea though as a whole thread per blöck is very wasteful
21:59:53  <Domenic>Compare to e.g. C# or Android's built-in threadpools
22:00:31  <devsnek>good point
22:01:19  <devsnek>the thing that jumps out at me is blöck-receiver.js though
22:01:22  <ljharb>how can you know the thread is transferable if it references builtins?
22:01:33  <ljharb>would the engine have to keep track of whether builtins were overwritten or not?
22:01:51  <devsnek>i assume thats the reason you have to explicitly capture them
22:01:56  <devsnek>same as like c++ lambdas
22:02:26  <ljharb>i'm not seeing that in the readme
22:02:45  <devsnek>https://github.com/domenic/proposal-blocks#variable-capture
22:02:53  <ljharb>ah k thx
22:03:13  <Domenic>No, it's a fair point, I didn't capture fetch() in the examples
22:03:36  <devsnek>anything in WindowOrWorkerGlobalScope is fair game right?
22:03:40  <ljharb>so when you say "capture" you really mean "mark as something that the worker will provide"?
22:03:50  <ljharb>ah, in .reify
22:04:04  <Domenic>devsnek: I never stated that, and I'm wary of such a rule... opening an issue now.
22:09:03  <Domenic>Ouch https://github.com/domenic/proposal-blocks/issues/1
22:10:56  <ljharb>yeah that's a tough one
22:11:13  <ljharb>based on how you go, it raises questions about polyfills, mutable globals, implicit vs explicit
22:12:29  <bterlson>I'm very invested in some feature like this, but... it's very hard to do right
22:12:43  <bterlson>consider that transpilers may depend on/introduce globals at compile time
22:14:19  <Bakkot>Domenic: also need to worry about prototypes of builtins
22:14:25  <bterlson>but I have a very great scenario to drive this: https://github.com/Azure/azure-documentdb-node/blob/ca337c9ddff136e01d6f589a45e24957d3efe5e3/source/lib/documentclient.js#L437
22:14:46  <Domenic>Bakkot: example?
22:15:24  <Bakkot>Array.prototype.foo = () => 0; await worker({| return [].foo(); |});
22:15:55  <ljharb>oh very true, esp with polyfills
22:15:56  <Domenic>That doesn't seem like an issue; you should be aware that will just runtime error becaues there's no .foo
22:16:09  <ljharb>i suppose you could make "worker" provide a polyfilled Array.prototype
22:16:09  <Bakkot>(This is a bigger deal in light of the protocols proposal.)
22:16:20  <Domenic>The code in the blöck is not running in your realm; it's just syntactically co-located with your source file.
22:16:30  <Bakkot>yeah, that's... weird to me
22:16:48  <bterlson>domenic: that's basically the ocntract that the sdk I linked is built around
22:16:50  <Bakkot>given that it enforces that you don't capture any variables
22:17:00  <bterlson>it's very confusing for people
22:17:06  <Domenic>I mean, the reason it enforces that is so that it can be moved around to another realm...
22:17:28  <Domenic>bterlson: The hope is that the funky syntax makes it less confusing?
22:17:36  <Domenic>And the parse-time restrictions, if we can keep them...
22:18:11  <bterlson>maybe
22:18:25  <bterlson>but identifier lookup semantics are something very... fundamental
22:19:03  <Domenic>Yep. That's the big question for the committee: will it ever be possible to have a source file that contains code that doesn't run in the same scope.
22:19:21  <Domenic>If accepting that is too weird for the committee, I think we're doomed to string-based hacks forever :(
22:19:38  <Domenic>My hope is that with enough syntactic offset it'd be acceptable. But we'll find out!
22:19:56  <Domenic>(String-based hacks: https://github.com/domenic/proposal-blocks#alternatives-considered )
22:21:23  <bterlson>Domenic: there is an alternative: there is a user space package that "just works" for serializing functions across realms using, I think, debugger APIs to chase dependencies and package them in? I'm not sure I can link the code yet, will check
22:22:05  <bterlson>hmm I can't, stay tuned
22:22:11  <Domenic>Hmm, is it different in usage than the ones I linked in https://github.com/domenic/proposal-blocks#alternatives-considered ?
22:22:27  <devsnek>greenlet is really simple
22:22:31  <devsnek>or it was the last time i looked
22:22:46  <devsnek>i've never seen clooney tho
22:24:57  <bterlson>Domenic: at first glance seems much different
22:25:44  <Domenic>Interesting
22:26:31  <devsnek>TabAtkins's point is interesting
22:26:47  <devsnek>is there any precedence for accessing the primordial state of the global scope
22:27:06  <TabAtkins>There's long-standing attempts to provide primordial access, yeah.
22:27:17  <TabAtkins>With one actively under development right now.
22:27:29  <TabAtkins>Championed by domenic, iirc?
22:28:08  <Domenic>Yeah we're working on a web API for that: https://github.com/drufball/layered-apis/issues/6
22:30:09  <devsnek>looks pretty nice
23:25:02  * araiquit (Ping timeout: 255 seconds)
23:37:08  * AtumTquit (Remote host closed the connection)
23:58:14  * keith_millerquit (Quit: My MacBook has gone to sleep. ZZZzzz…)