02:54
<bendtherules>
@ljharb: Thanks for the heads up. Github app notifications are flaky at times.
03:02
<bendtherules>
bradleymeck: Trying to understand the conversation. So what is the potential problem with object[key] with user-provided key?
03:02
<bendtherules>
Accessing __proto__ in the key?
03:03
<bendtherules>
Even then you can only set proto, not change properties within it.
03:15
<devsnek>
littledan: what is finalizationRegistry.[[Realm]] for
12:22
<bendtherules>
ljharb: Done
12:23
<bendtherules>
npm watch script seems broken? https://github.com/tc39/ecma262/issues/2083
15:04
<devsnek>
v8's test262 runner goes past 100% coverage
15:04
<devsnek>
i don't like this
16:25
<ljharb>
bendtherules: thanks
17:17
<bradleymeck>
bendtherules: see https://twitter.com/bradleymeck/status/1280209566907670530 , basically you have this whole security firm audit noise from people accessing things on prototypes with dynamic access
17:18
<bradleymeck>
and it is a lot of noise if you use `npm audit` or github vulnerability checks
17:32
<ljharb>
(both of which are on by default)
17:33
<ljharb>
bradleymeck: is it `constructor-constructor`, and the code?
17:33
<bradleymeck>
yes in that case
17:33
<ljharb>
fun
17:34
<bradleymeck>
but other fun and more esoteric stuff like that does come up while i audit stuff
17:52
<bendtherules>
So, if libraries do Get/hasOwnProperty instead of general accessors - doesn't that fix it?
17:52
<bendtherules>
(if that's what they want Ofcourse)
17:53
<bendtherules>
given these are data manipulation libraries usually, that's a fair limitation
17:59
<devsnek>
bendtherules: the problem is that people write the code
17:59
<devsnek>
there are of course solutions
18:00
<devsnek>
but the solutions are only useful if people use them
18:03
<bendtherules>
Hmm, got it. So, there's no easy syntax for that
18:03
<bradleymeck>
bendtherules: yes it is largely a DX problem
18:03
<bradleymeck>
a variety of things can make this easier
18:03
<bradleymeck>
you can use null prototype objects is my main approach
18:04
<bradleymeck>
there are actually very few reasons to want to have an instance of Object
18:04
<bradleymeck>
so in the example tweet above adding __proto__:null to the literal fixes it
18:04
<bradleymeck>
but no one does that
18:05
<bradleymeck>
and security noise comes from the libraries doing dynamic access, so even if it is fixed you still get alerts
19:13
<bakkot_>
gotta fix `extends null`
19:13
<bakkot_>
should revisit that
19:13
<bakkot_>
one hairbrained idea I had for it was to make `Function.prototype` new-able
19:13
<bakkot_>
though I don't remember enough details to know why I thought that would fix the problem
19:31
<bendtherules>
bradleymeck: does proto null solve it though? You are going to want to call the inherited method, at some point - after all.
19:31
<bendtherules>
Also what about array literals? That will also finally lead to object in proto chain, right?
19:32
<ljharb>
it seems fine to me to make `super()` a no-op when super is null, why is that not workable?
19:35
<bendtherules>
ljharb: just thinking out loud, so should `super.xyz`still throw some error?
19:41
<ljharb>
bendtherules: yes, i'd expect so
19:41
<ljharb>
but i might expect `super?.xyz` to work
19:41
<bradleymeck>
bendtherules: thats fine, the problem is the inherited constructors, calling the method isn't a problem
19:42
<bradleymeck>
so yes, a null prototype on dynamic property access usually fixes the issue
19:42
<bradleymeck>
bakkot_: to my recollection most of the complaints are about minor edge cases
19:43
<bradleymeck>
bendtherules: you can access ([])['constructor'] just fine so idk what the question about them is
19:43
<bakkot_>
bradleymeck yeah mostly
19:44
<bendtherules>
bradleymeck: I meant, if i do `a={__proto__:null}`, then this solves security issue, but now i can't expect `a.toString` to work. What if i want that?
19:44
<bakkot_>
stop wanting that, presumably?
19:44
<bakkot_>
when would you want that to work?
19:44
<bakkot_>
that is, to work how it does by default
19:45
<bakkot_>
ljharb is "when super is null" a at-definition-time property or an at-evaluation-time property? remember that you can dynamically change a class's heritage
19:45
<ljharb>
bakkot_: evaluation time, for that reason
19:46
<ljharb>
bakkot_: definition-time is "it's a derived class, so super is allowed"
19:46
<bakkot_>
ok, so is super _required_ in your extends-null class?
19:46
<bendtherules>
I was thinking - with the same object, sometimes i want "safe mode" (i.e. i am dealing with user-generated value in this line) - so i'll write something like`obj[? prop1][? prop1]`
19:47
<ljharb>
bakkot_: yes, if i want to access `this`
19:47
<bakkot_>
ljharb that seems... very strange
19:47
<ljharb>
why?
19:47
<bendtherules>
but in next line, when i am calling toString, i can call `obj.toString()` directly
19:47
<ljharb>
`this` is only ever available in a derived class after calling super()
19:47
<bakkot_>
lj because there is no super class to call?
19:47
<bakkot_>
ljharb *
19:47
<ljharb>
there's whatever i put in `extends X`
19:47
<bakkot_>
in this case, `null`
19:47
<ljharb>
sure
19:47
<bakkot_>
ljharb right, well, the contention is that a class which extends null is not a derived class
19:48
<ljharb>
i'm basing my model on the syntax
19:48
<ljharb>
if it has "extends" it's a derived class
19:48
<ljharb>
(not that "derived class" is a good name for it, that's just the spec name)
19:48
<bakkot_>
that is a view one could hold, but not one I share or expect to be common
19:49
<ljharb>
i mean, i don't think "common" needs to apply with anything that changes the [[Prototyoe]] later
19:49
<ljharb>
what's common is never monkeying with the prototype at all
19:49
<bakkot_>
I am talking mainly about the mainline case, where you don't do that
19:49
<ljharb>
ok, so like `class extends null` or the equivalent
19:49
<bakkot_>
yup
19:49
<bakkot_>
`new class extends null { constructor(){ console.log(this); } } // works?`
19:50
<bakkot_>
per your above, no
19:50
<ljharb>
i'd assume it throws, right
19:50
<bakkot_>
that would surprise me
19:50
<ljharb>
it would throw with any other value in place of `null` there
19:50
<shu>
ljharb: what would you expect super() to do in that case?
19:50
<ljharb>
and since it's an expression, `null` is the same as `x`
19:50
<ljharb>
shu: nothing
19:50
<ljharb>
shu: just bind the `this` and install fields
19:50
<shu>
why is that less surprising to you than throwing, because the super class is clearly null?
19:50
<ljharb>
shu: iow super does two things right now, "invoke the superclass constructor" and "bind the receiver and install fields"
19:50
<bakkot_>
ljharb well, yes, because the other things you could put there would be expected to be _actual superclasses_, whereas null is, clearly, not
19:51
<ljharb>
because in my `extends null` class what i *want* is to install the fields
19:51
<ljharb>
and i want to control when that happens
19:51
<bakkot_>
... well
19:51
<bakkot_>
stop wanting that?
19:51
<ljharb>
lol
19:51
<bakkot_>
sorry sorry
19:51
<shu>
i see, that's a different line of argument that folks have a model that all expressions in heritage positions are somehow semantically interchangeable
19:51
<bakkot_>
I mean to say, I don't find "I want to control when fields are installed on my extends-null class" to be a compelling use case
19:51
<ljharb>
surprise in an edge case seems less important to avoid than giving people what they want :-p
19:52
<ljharb>
bakkot_: that part's fair
19:52
<bakkot_>
ljharb right, well, the thing that I want is to not have to call `super` in my extends-null class
19:52
<bakkot_>
because, not being a subclass, I don't expect to have to
19:52
<bakkot_>
there is no superclass to call
19:52
<ljharb>
i see the difference in our thinking here: to me, if it has `extends`, it *is* a subclass
19:52
<bakkot_>
... of... what?
19:52
<ljharb>
of whatever's on the RHS of `extends`
19:52
<bakkot_>
... null?
19:52
<bakkot_>
null is not a class
19:53
<bakkot_>
this I believe
19:53
<ljharb>
sure
19:53
<ljharb>
`new class extends {} { constructor() { this; } }` throws too tho
19:53
<ljharb>
iow, "the RHS is a class" is already not a guarantee
19:53
<ljharb>
and the subclass rules still apply to everything simply because `extends` is present
19:53
<bakkot_>
wait, I don't understand this example
19:54
<ljharb>
why is null the one deviation?
19:54
<ljharb>
it extends an empty object
19:54
<bakkot_>
ljharb `{}` is not a class, which is why `super()` would throw
19:54
<ljharb>
sure
19:54
<ljharb>
but i didn't use super there
19:54
<ljharb>
i used `this`
19:54
<bakkot_>
sure, but
19:54
<ljharb>
and it threw because i never called super
19:54
<ljharb>
because it's a subclass, *because `extends` is there*
19:54
<bakkot_>
ok but it would also throw if you did call `super()`
19:54
<ljharb>
sure
19:54
<bakkot_>
I don't understand the relevance of this example
19:54
<ljharb>
so the part that i'm suggesting would be special for null is "what happens when you call super()"
19:54
<ljharb>
there is no scenario where `extends` is present and you'd not have to call super to access `this`
19:55
<bakkot_>
right, yes, I get that this is what you are proposing
19:55
<ljharb>
and i don't think it makes any sense to make `null` special for the latter case
19:55
<bakkot_>
our difference is, I don't think `null` is a class
19:55
<bakkot_>
meaning, I don't think one can have a subclass of `null`
19:55
<ljharb>
neither is most of the other things i can put on the RHS of `extends`
19:55
<bakkot_>
right, you can't have subclasses of those eitherr
19:55
<ljharb>
because "the thing it extends is a class" is currently irrelevant to whether `super` is required.
19:55
<shu>
ljharb: i also do not understand why your opinion is that "there is no scenario where `extends` is present and you'd not have to call super to access `this`" is such a desirable property to preserve
19:55
<ljharb>
shu: because the syntax is what developers see
19:56
<ljharb>
shu: they see extends, they know they need to call super
19:56
<ljharb>
shu: that's my argument anyways
19:56
<bakkot_>
that is not my intuition
19:56
<shu>
i simply disagree with that intuition
19:56
<bakkot_>
I see `extends null`, I do not expect to have to call `super`, because I do not think I am writing a derived class
19:56
<ljharb>
right but you might not see null there
19:56
<shu>
...how?
19:56
<bakkot_>
this is technically true but I think not super relevant
19:57
<ljharb>
you might have a function that takes a superclass and returns a new class, and the user could pass null or a constructor
19:57
<bakkot_>
I expect the overwhelming majority of cases to put `null` there explicitly
19:57
<ljharb>
that's fair, i also expect that
19:57
<shu>
ah, i see, that would be a strange kind of class factory
19:57
<shu>
that sometimes generates a subclass and sometimes not?
19:57
<ljharb>
shu: i maintain it's a subclass if it has `extends` :-)
19:57
<bakkot_>
ljharb I still want to know what you think it is a subclass _of_
19:57
<ljharb>
nothing, if it's null
19:57
<bakkot_>
what... does that mean
19:57
<ljharb>
its subclass-ness is not contingent on what it's subclassing
19:58
<bakkot_>
you and I do not share an understanding of the word "subclass", I see
19:58
<ljharb>
right
19:58
<shu>
ljharb: when you are writing new classes, do you think about the class hierarchy?
19:58
<ljharb>
another way to put it, is that to me a subclass is only a class body in which `super` can be used
19:58
<shu>
or do you only think about a stream of tokens?
19:58
<ljharb>
shu: sure
19:58
<ljharb>
i don't think about tokens at all, but i do think about what i'm typing, which includes the syntax
19:59
<shu>
okay, when is the point in time when you switch from thinking about your syntactic notion of "subclass" to the other one, where a subclass is a class in a hierarchy with a superclass?
19:59
<ljharb>
i either typed a base class, or a subclass, by virtue of whether i chose `extends` or not
19:59
<ljharb>
when i `new` it, i suppose?
19:59
<ljharb>
you're describing a runtime characteristic, not a definition time one
19:59
<shu>
is there any linkage between your two notions?
19:59
<shu>
this is just all very bizarre to me
20:00
<ljharb>
so what would be an alternative to "making extends null work" besides "super() where super is null is a runtime noop"?
20:00
<ljharb>
the constructor checks the Prototype before it evaluates, and if it's null it binds it like a base class?
20:01
<bradleymeck>
i'd rather lean towards the presence of `extends` at all makes it an explicit subclass of some kind
20:01
<rkirsling>
it doesn't make sense to call `extends null` subclassing; what you'd be saying is that there is _implicit_ subclassing when `extends` is omitted such that you'd like to be able to explicitly cancel it out
20:01
<ljharb>
rkirsling: not sure what that means
20:01
<shu>
ljharb: what is the difference in your mental model between `class C extends Object` and `class C`
20:02
<bradleymeck>
shu: to me, it is that you have used a different construct
20:02
<ljharb>
shu: the former is an explicit subclass and the latter isn't?
20:02
<bradleymeck>
i don't understand the idea that they are equivalent
20:02
<ljharb>
shu: i don't actually care that they both inherit from Object.prototype
20:02
<bradleymeck>
one requires super()
20:02
<ljharb>
^
20:03
<bradleymeck>
you made a choice by using 1 of the 2 constructs, and it causes a variation in how they need to be used
20:03
<rkirsling>
oof. it sucks that those are the same insofar as the word "subclass" is concerned and yet we have a thing about them that isn't the same :(
20:04
<bradleymeck>
rkirsling: they aren't the same is exactly the point of why i don't think they should be equated
20:04
<ljharb>
they do result in the same prototype chain. but they're not the same.
20:04
<bradleymeck>
to put it a different way, a non-subclass can simply be thought of as something that cannot call super() during coonstruction
20:04
<shu>
i see, interesting
20:05
<shu>
so you really have a purely syntactic notion of "subclass"
20:05
<ljharb>
yep
20:05
<rkirsling>
ouch
20:05
<bradleymeck>
shu: i wouldn't say syntactic
20:05
<ljharb>
that's the expectation the ES6 syntax sets up imo
20:05
<shu>
bradleymeck: no?
20:05
<bradleymeck>
but the equality of output doesn't tie me to the syntax being equal in intent
20:05
<shu>
bradleymeck: if the difference is not a ontological one in terms of hierarchical relationships but in whether super() throws or not
20:06
<shu>
bradleymeck: that seems purely syntactic to me
20:06
<rkirsling>
it's really unfortunate that we made `extends Object` require `super` :(
20:06
<ljharb>
if we hadn't, then we'd already know what to do with null
20:06
<ljharb>
and with `extends {}` or `extends false`
20:07
<bradleymeck>
shu: I'd agree that it is related to what the programmer wrote, but not necessarily that it is purely syntactic
20:07
bradleymeck
has to dig up a spec sadness
20:07
<shu>
ljharb: so by this logic, you would not be opposed (syntax budget concerns aside) to another syntax that isn't "extends null"
20:08
<shu>
presumably because there is no extant property about super throwing or not tied to any new syntax
20:08
<ljharb>
shu: absolutely correvt
20:08
<ljharb>
`null class` or something
20:08
<shu>
in general i feel like that's a dangerous constraint on design
20:08
<bradleymeck>
shu: i'd probably be in the same camp as being fine with other syntax but I would prefer `null` to be in the syntax
20:09
<ljharb>
shu: how so?
20:09
<shu>
i don't really care enough about this particular use case
20:10
<bradleymeck>
shu: I think trying to state that the current programmer divergence must preserve the behavior of being purely syntactical is odd is my main complaint about the argument
20:10
<shu>
ljharb: because to me, i would almost always prefer to make tradeoffs that favor ontological symmetry (what is or isn't a subclass in the class hierarchy sense, in this case) over syntactic symmetry, unless doing so prohibits performant implementations, say
20:10
<bradleymeck>
extends is just a signal that the class is different from a non-subclass
20:10
<shu>
ljharb: see our repeated semi-serious attempts to reclaim 'with' to be useful
20:10
<bradleymeck>
is there a real issue with not throwing for null as a class heritage?
20:10
<ljharb>
shu: i think i agree with you in principle, but class syntax failed that rubric at inception, because `extends false` eg is permitted
20:11
<ljharb>
shu: and similarly, `super` isn't permitted in a class without `extends`, even if that class changes its Prototype later
20:11
<shu>
bradleymeck: no, i think the issue is not requiring typing super()
20:12
<bradleymeck>
shu: i don't think there is a problem requiring super() when extending null personally
20:12
<rkirsling>
there's no way that shouldn't throw though :-/
20:12
<bradleymeck>
i'd be kind of terrified if it wasn't required for null, since it is required for everything else
20:12
<ljharb>
tbh it's annoying to me to require `super()` in *every* case to me where i'm not changing the argument signature
20:12
<bradleymeck>
rkirsling: why
20:12
<ljharb>
rkirsling: yes, why
20:12
<ljharb>
rkirsling: "super" just means "make sure my instance is set up, somehow"
20:13
<ljharb>
that it invokes a constructor is an implementation detail
20:13
<shu>
i don't think that's how most people think of it...
20:13
<rkirsling>
it's a function call
20:13
<shu>
it is called "super"
20:13
<shu>
not "init"
20:13
<ljharb>
rkirsling: it's function-like, like import(). you can't pass it anywhere else.
20:13
<bradleymeck>
rkirsling: a function call to what? directly to the class heritage? it doesn't do a normal behavior when you call it
20:13
<bradleymeck>
I'd agree it has to be a call to *something*
20:13
<bradleymeck>
but not to the class heritage itself
20:13
<rkirsling>
yeah I mean I know it's a keyword but the idea is that you're calling a function, namely the constructor of the superclass
20:14
<devsnek>
auto call super if you don't call super seems too magical to me
20:14
<bradleymeck>
rkirsling: but it doesn't do that exactly
20:14
<ljharb>
devsnek: i agree
20:14
<rkirsling>
it would need to throw just as `x = null; x.y()` does
20:14
<devsnek>
and that's not just
20:14
<devsnek>
you don't call super
20:14
<ljharb>
rkirsling: sure. and in `extends null` the implied constructor is `function () { return { __proto__: null } }`
20:14
<devsnek>
it has to be, super isn't used
20:14
<devsnek>
or smth `if (false) { super() }`
20:14
<devsnek>
extends null has no useful behavior atm
20:15
<bradleymeck>
rkirsling: I don't understand the last comment
20:15
<bradleymeck>
you aren't calling a method of the heritage nor the heritage directly
20:15
<ljharb>
rkirsling: i'd be very surprised if people found that `class C extends null { constructor() { super(); this.a = 1; } } new C()` produces an instance of C, with an own `a` property set to 1, with a [[Prototype]] set to null, was confusing
20:15
<devsnek>
clearly what we need is
20:16
<devsnek>
a global `NullProtoObject` constructor
20:16
<rkirsling>
ljharb: it's not the produced thing that's confusing, it's the fact that `super()` doesn't throw
20:16
<rkirsling>
I find that wildly objectionable
20:16
<bradleymeck>
rkirsling: I still don't understand the claim it should throw
20:17
<bradleymeck>
cause super() clearly doesn't just do a function call
20:17
<rkirsling>
you can't call a method on null, I don't know what else to say
20:17
<ljharb>
rkirsling: that's not what you're doing
20:17
<ljharb>
rkirsling: `super()` isn't calling a method on null, it's not calling a method on anything - it's asking the engine to set up the class hierarchy
20:17
<bradleymeck>
rkirsling: you don't call methods on class heritage though, see `extends Object.setPrototype(function () {}, null)`
20:17
<ljharb>
that *may* involve invoking a constructor
20:18
<bakkot_>
ljharb I don't really agree with the claim that `extends false` is permitted
20:18
<ljharb>
bakkot_: it's syntactically permitted. obv `super()` would throw there
20:18
<bakkot_>
and therefore do not want to reason from that claim
20:18
<bakkot_>
ljharb sure but why does that... matter?
20:18
<ljharb>
bakkot_: if i don't use `this`, i can still return an object from the constructor and everything works fine
20:18
<ljharb>
because that's how classes work?
20:19
<ljharb>
i don't see how it makes sense to just pretend to ignore a bunch of existing semantics until it matches the mental model you want
20:19
<bakkot_>
my mental model is that JS is not typechecked, which means a lot of nonsense is allowed statically and throws only at runtime
20:20
<bakkot_>
you brought up this example in response to Shu's "i would almost always prefer to make tradeoffs that favor ontological symmetry over syntactic symmetry"
20:20
<bakkot_>
in particular, you claimed JS has failed at this because the syntax is allowed
20:20
<ljharb>
hm
20:20
<bakkot_>
I dispute this; I claim it also matters what is allowed _at runtime_
20:20
<bradleymeck>
i'm just not clear that there is an ontological symmetry
20:20
<bakkot_>
and `extends false` is not
20:20
<bakkot_>
(unless you do weird things JS programmers are unlikely to need to think about)
20:21
<devsnek>
i just think of every feature in terms of abstracting all operands to variables
20:21
<bradleymeck>
super() does *something* and not a direct call of some kind to the class heritage
20:21
<ljharb>
bakkot_: ok, so then you'd prefer `this` to Just Work whenever the superclass either doesn't exist, or it's a base class, or is null?
20:21
<bakkot_>
ljharb concretely, I think `this` should work without calling `super()` first when you have `class {}` or `class extends null {}`
20:21
<ljharb>
bakkot_: meaning that the impl would have to check the prototype chain the first time `this` is referenced in any class, instead of only when super is called in a derived/never in a base?
20:21
<ljharb>
right
20:21
<devsnek>
i wouldn't mind `extends null` meaning you don't call super
20:21
<bakkot_>
ljharb I am not making claims about what should happen outside of those two cases
20:21
<devsnek>
but others would
20:22
<bradleymeck>
bakkot_: should or must?
20:22
<ljharb>
bakkot_: ok, seems like we'd need to figure that out tho
20:22
<bakkot_>
ljharb of course, yes
20:22
<bakkot_>
I just want agreement on the core, happy-path semantics first
20:22
<bakkot_>
before we worry about the edge cases
20:22
<ljharb>
i don't think the one can come without the other
20:22
<bakkot_>
uh
20:22
<bakkot_>
why?
20:23
<ljharb>
so with changing the proto later, even in `class {}`, what happens the first time `this` is referenced?
20:23
<devsnek>
people will argue about `function factory(S) { return class extends S { constructor() { super(); } } }`
20:23
<bakkot_>
hang on, before I respond to that, I want to know why we need to talk about the edge cases before agreeing on the core semantics
20:23
<ljharb>
bakkot_: because in classes, i don't think they're separable
20:24
<ljharb>
the edge cases are part of the core semantics to me
20:24
<bakkot_>
ljharb well
20:24
<bakkot_>
I do not agree with that
20:24
<bakkot_>
that's... pretty much a contradiction in terms
20:24
<ljharb>
for classes specifically, i think everything's so entangled and intertwined that it can't really be separated
20:24
<ljharb>
to me, class fields discussions bore that out as well
20:24
<bradleymeck>
bakkot_: i think the presence of extends forcing super() is really brutal to try and claim is decoupled
20:25
<bakkot_>
bradleymeck I'm not claiming it's decoupled?
20:25
<bakkot_>
that's the case I'm talking about
20:25
<bradleymeck>
i'm losing something via reading this
20:26
<bakkot_>
bradleymeck my core claims are "`this` should work without calling `super()` first when you have `class {}` or `class extends null {}`" and "I want to get agreement on these cases before trying to reason about what happens when you change the prototype dynamically"
20:27
<devsnek>
bakkot_: there were people who objected to a super call not aligning exactly with the existence of `extends` regardless of what the value actually is
20:28
<bakkot_>
devsnek yes, ljharb has expressed that opinion
20:28
<ljharb>
(and while i'm one of them i don't think i ever said as much in plenary)
20:28
<bakkot_>
which is why I'm here contesting it
20:28
<ljharb>
so there's a number of folks who think that way
20:29
<bakkot_>
ljharb anyway, would you be willing to talk about my claims without trying to get into what happens in edge cases?
20:29
<bradleymeck>
bakkot_: can you explain why it must not require calling super, while extends Object must?
20:29
<bakkot_>
because if you agree those are good goals but don't think there's a good way to achieve them, that's different from disagreeing with the goals
20:30
<ljharb>
bakkot_: i'm not sure what more we'd talk about :-) i hear your suggestion, and altho it conflicts with my intuition about extends+super, to me the only way i'd be able to consider ignoring that conflict is if i understand the edge cases too
20:30
<bakkot_>
bradleymeck I don't think anyone writes `extends Object` and I don't actually care if that particular case requires calling `super` or not
20:30
<ljharb>
i do disagree with the goal, but not in a forever-intractable kind of way
20:30
<ljharb>
i think "avoiding writing super()" is a non-goal
20:30
<ljharb>
(if that were a goal, then most of my "actual" subclasses wouldn't have required it either, including every react component)
20:31
<bradleymeck>
i'd agree that avoiding verbosity isn't a real goal, as long as usability isn't too impacted, you already are doing additional burden by requiring `extends null`
20:32
<bakkot_>
yes, my objection is not "this is a burden", it's "this requirement breaks my ontology by suggesting that an extends-null class is a subclass when it is not"
20:32
<bradleymeck>
bakkot_: why is it not i guess is the follow up
20:32
<ljharb>
to paraphrase a smart person, "stop wanting that ontology?"
20:32
<bakkot_>
bradleymeck of what?
20:32
<ljharb>
:-p
20:32
<bradleymeck>
bakkot_: why is extending null not a subclass if it has a heritage
20:32
<bakkot_>
ljharb I am willing to give it up if there is reason to believe it is unusual!
20:33
<bakkot_>
ljharb but fwiw I cannot find any definition of "subclass" anywhere for which "it is a subclass, but not of anything" would make sense
20:33
<ljharb>
i would be very surprised if muggle devs thought about the definition of subclass at all
20:33
<bradleymeck>
lack of heritage does affect things like replacements of heritage causing errors
20:33
<bakkot_>
strong disagree
20:33
<shu>
everyday practitioners for sure think about class hierarchy?
20:33
<ljharb>
i believe they think their class either extends something or doesn't, and if it does, it requires super
20:34
<ljharb>
shu: yes but not in a strict "this is a subclass" sense
20:34
<rkirsling>
I don't think devs think that
20:34
<bradleymeck>
i'm fine with the idea that people think about some hierarchy but i don't understand that having a heritage is seen as not being a subclass
20:34
<rkirsling>
if anything they forget that it's not simple OOP
20:34
<bakkot_>
bradleymeck having a heritage _specifically from null_ is not being a subclass
20:34
<bakkot_>
you cannot be a subclass without having a superclass
20:34
<ljharb>
rkirsling: sure but those people also aren't going to be extending null
20:34
<bakkot_>
`null` is not a superclass
20:35
<rkirsling>
true
20:35
<ljharb>
ok so then maybe "subclass" is the wrong term
20:35
<ljharb>
if it has `extends`, the spec calls it a derived class
20:35
<shu>
ljharb: this past hour of argumentation has kinda hinged on your using a spec author's understanding of "subclass" and say that people don't understand it that way
20:35
<ljharb>
derived classes require use of super
20:35
<bradleymeck>
bakkot_: can you explain why it isn't something that can be derived from?
20:35
<ljharb>
shu: hm, let me think about that one
20:35
<bradleymeck>
shu: to be clear I do think there is variation on the presence of a heritage that is attempting to be ignored, ljharb isn't purely alone
20:36
<bakkot_>
bradleymeck I didn't say "cannot be derived from", I said "is not a class"
20:36
<bakkot_>
*is not a superclass
20:36
<bakkot_>
the reason there being, it is not a class
20:36
<bakkot_>
the reason for that being that it... isn't... a class? I don't know how to justify that onoe
20:36
<shu>
ljharb: all i'm saying is almost all practitioners, by my observation and experience, certainly think about subclassing. what they don't think about is what you defined "subclass" to mean, so yes i agree we should stop using "subclass" in this argument at least.
20:37
<bradleymeck>
bakkot_: if a class is derived from something it isn't a subclass?
20:37
<rkirsling>
maybe like "explicit extension"
20:37
<bradleymeck>
in particular i think the claim that `extends` is purely syntactic is false
20:37
<bradleymeck>
`class X { constructor() { this.x = 'x'; } }; X.__proto__ = function Y() {this.y = 'y';}; new X();`
20:38
<bradleymeck>
vs
20:38
<bradleymeck>
`class X extends Object { constructor() { super(); this.x = 'x'; } }; X.__proto__ = function Y() {this.y = 'y';}; new X();`
20:39
<ljharb>
that feels like a pretty compelling example to me
20:39
<shu>
bradleymeck: what is that trying to show
20:39
<bradleymeck>
shu: one of those does add this.y, one does not
20:40
<bradleymeck>
both have a class heritage value of Object
20:40
<bakkot_>
bradleymeck: in the spirit of avoiding the term "subclass", I would regard a class which `extends null` as sitting at the top of its class hierarchy, the same way that a class which does not `extend` anything does
20:40
<bakkot_>
since it sits at the top of its class hierarchy, I would be surprised if I had to call `super()`
20:41
<bradleymeck>
bakkot_: i am unclear on *why* it is at the top
20:41
<bakkot_>
bradleymeck uh
20:41
<bakkot_>
there is no possible thing which could be above it?
20:41
<bradleymeck>
null seems to be at the top
20:41
<bakkot_>
null is not a class
20:41
<bakkot_>
null cannot be in a class heirarchy
20:42
<ljharb>
i mean, the hierarchy is between [[Prototype]] values, which are all objects or null
20:42
<bakkot_>
LSV cannot hold for `null` and any `class` under almost any circumstances
20:42
<ljharb>
so null is part of a prototype hierarchy - at the top
20:42
<bakkot_>
*LSP
20:42
<bradleymeck>
i mean, we can argue it isn't a class but that class does have a heritage
20:42
<ljharb>
a "class hierarchy" is just a combination of the constructor's prototype hierarchy, and its prototype object (and its prototype hierarchy)
20:42
<shu>
no! that's the core confusion i think
20:42
<bakkot_>
ljharb strong disagree
20:43
<ljharb>
do ES5 constructors have a class hierarchy?
20:43
<bakkot_>
class hierarchy is a concept which exists independent of language
20:43
<bakkot_>
it is a concept prior to concrete programming languages
20:43
<rkirsling>
^
20:43
<shu>
there's a programmer's "theory" of a class hierarchy, it's the mental map of building blocks of a program and how things are divided up
20:43
<shu>
ljharb and bradleymeck instead are arguing from purely mechanical grounds of what JS happens to do and how that is inconsistent
20:44
<shu>
because JS is a dynamic language, inconsistencies and weirdness will invariably be possible
20:44
<bradleymeck>
shu: i don't care what you call it, it can be a base class but it has a heritage
20:44
<shu>
...
20:44
<ljharb>
bakkot_: then "extends null" as a concept just doesn't fit into your concept of class hierarchy imo
20:44
<bradleymeck>
shu: to be clear i don't care if null is considered outside the heirarchy
20:44
<bakkot_>
ljharb yes, that is my claim
20:44
<ljharb>
even the existence of a null [[Prototype]] doesn't seem like it's part of this class hierarchy concept
20:44
<ljharb>
which would mean it's not an appropriate concept to apply to JS
20:45
<bakkot_>
ljharb disagree
20:45
<bradleymeck>
i simply don't understand this logic that heritage must match a class hierarchy and be a direct call of some kind to it
20:45
<bakkot_>
I don't think every feature in a language needs to be represented perfectly by some concept for that concept to be useful
20:45
<bradleymeck>
both of those seem to be invalidated both at runtime and to some extent at parse time
20:45
<shu>
bradleymeck: i don't think that's the logic
20:45
<ljharb>
bakkot_: nor does a useful concept need to constrain every aspect of the language's design
20:45
<bakkot_>
ljharb right
20:45
<bakkot_>
but
20:46
<bradleymeck>
shu: what is the logic? avoid using subclass
20:46
<bakkot_>
that does not mean you are free to ignore it
20:46
<bakkot_>
bradleymeck I don't know what you mean by "heritage", I am realizing
20:46
<shu>
bradleymeck: the logic is that *unless* you do edge casey things like runtime __proto__ mutation, not requiring super() in `extends null` classes matches up with the general OOP model of class hierarchies
20:46
<shu>
bradleymeck: and that is a valuable property
20:47
<ljharb>
bakkot_: “not constrain” definitely means “free to ignore” :-p
20:47
<bradleymeck>
shu: can you explain what we get for that property?
20:47
<shu>
ljharb: that is also just not true!
20:47
<shu>
bradleymeck: we get a happy path for common usage? the normal 80-20 thing
20:47
<ljharb>
it doesn’t mean it’s wise to ignore it ofc
20:47
<bradleymeck>
shu: can you explain that more
20:47
<bradleymeck>
why is not using super() the explicit happy path
20:47
<bradleymeck>
like, what does that mean
20:48
<bakkot_>
bradleymeck the happy class is not that you are avoiding `super`
20:48
<bakkot_>
its that your usage of the language matches the concepts in your head
20:48
<bakkot_>
in particular, that a class which extends `null` is at the top of its class heirarchy
20:48
<shu>
bradleymeck: like, when i'm programming in an OO way, i think about classes and how i organize my code, right?
20:48
<shu>
bradleymeck: i don't start by first saying "let me think of the full semantics of JS's classes"
20:48
<shu>
i think about organization
20:48
<bradleymeck>
bakkot_: i don't understand that claim, i don't have a concrete mental model i try to force all my programming into
20:49
<shu>
this isn't concrete
20:49
<bakkot_>
I will let shu say the thing I would say
20:49
<shu>
it's like a rough organizational idea?
20:49
<bradleymeck>
shu: ok, and having super() would break it, and in a way that isn't easily learned/debugged?
20:49
<bradleymeck>
what about it breaks the model
20:49
<shu>
bradleymeck: oh i don't know about the ease claim, it seems easy enough to fix
20:50
<shu>
bradleymeck: it's not a binary break-or-not-break
20:50
<shu>
it makes the mapping more difficult
20:50
<bradleymeck>
what would it mean if a base class is calling super()
20:50
<bradleymeck>
from your model
20:50
<shu>
i don't know what it would mean
20:50
<bakkot_>
bradleymeck it would not mean anything
20:50
<bakkot_>
`super()` is a thing for subclasses to do
20:51
<bradleymeck>
bakkot_: it would be non-sensical or something else?
20:51
<bakkot_>
in particular, for them to invoke their superclass
20:51
<bradleymeck>
bakkot_: and if they don't have superclass?
20:51
<bakkot_>
bradleymeck this question is like "what would it mean if orange was a sound"
20:51
<bradleymeck>
to my recollection abstract superclasses etc might be ellided in ye olden days
20:51
<bradleymeck>
bakkot_: i'd disagree
20:51
<shu>
bradleymeck: hold on, the "if they don't have a superclass" is the turning point where we diverge, i think
20:52
<shu>
bradleymeck: *you*, as a JS expert, know that the congruence i want for `super` in my hypothetical is _already not true_
20:52
<shu>
bradleymeck: the contention is that the cases where it is already not true are, i think, rare, like runtime prototype mutation
20:52
<bradleymeck>
shu: i'm thinking of me in 11th grade learning Java
20:52
<ljharb>
bakkot_: ok what about this: what if `super()` always throws when the superclassish is null, but `super?.()` doesn't throw when it's null (but does set up the `this`)
20:52
<ljharb>
bakkot_: would that break you and shu's mental model?
20:53
<bradleymeck>
shu: a lot of what you are stating is really confusing, just like polymorphism is confusing, let alone things like multiple inheritance
20:53
<bradleymeck>
but we are familiar with topics here
20:53
<rkirsling>
wait `super?.()` doesn't (and shouldn't) exist since it's a keyword though
20:53
<bakkot_>
ljharb again, my core claim is that a class which extends `null` should not need to call `super`; I am somewhat less worried about what happens in other cases
20:53
<devsnek>
i was not ready for `super?.()`
20:54
<devsnek>
but i don't hate it
20:54
<ljharb>
rkirsling: it could certainly be permitted
20:54
<shu>
bradleymeck: single inheritance is confusing?
20:54
<bradleymeck>
shu: for me it was
20:54
<bradleymeck>
to some extent... is?
20:54
<bakkot_>
bradleymeck in 11th grade Java, you would not call `super()` in a class which does not have a superclass
20:54
<bakkot_>
`super()` is a thing for subclasses to do, and a subclass cannot not-have a superclass
20:55
<bradleymeck>
bakkot_: that was learned and still seems to be completely fine if i had been forced to call it
20:55
<rkirsling>
why would it be completely fine though?
20:55
<bradleymeck>
it wasn't inherent in the fact that a class inherited
20:55
<rkirsling>
it refers to something nonexistent
20:55
<devsnek>
couldn't you argue if you don't specify `extends` it defaults to Objcet
20:55
<bradleymeck>
rkirsling: why does super() have to do something?
20:55
<bradleymeck>
thats really a crux of the argument
20:56
<bradleymeck>
if super() doesn't need to do anything... why do you have to avoid calling it
20:56
<bakkot_>
bradleymeck here we are arguing about whether `super` should be required; if it is required. it definitely does do something
20:56
<rkirsling>
devsnek: yeah that's my view but I guess it's messy since extends syntactically requires super
20:56
<bradleymeck>
bakkot_: why
20:56
<bradleymeck>
what might be better a question
20:56
<rkirsling>
bradleymeck: a no-op is still a something
20:56
<bakkot_>
bradleymeck um. how can it be required and also not do something? at the very least "make the class work"? that is a thing that it has done.
20:58
<shu>
i think i am going to do some reviews
20:58
<rkirsling>
I understand that super is _more_ than just a function call but I don't buy that it's ever not one, just because the function is an implicit no-op
20:58
<bradleymeck>
bakkot_: to clarify a bit, i don't see a no-op as being a claim that it does something and equating lack of extends and extends Object to be very consistent
20:59
<bakkot_>
bradleymeck I am not dead-set against it being a no-op. what I am against is it being required.
20:59
<bakkot_>
also, like I said, I don't really care about the `extends Object` case and am happy to explore whatever semantics there would make people happy
20:59
<devsnek>
are there use counters for `extends null`
20:59
<bradleymeck>
bakkot_: I'm very unclear on why it must not be required
21:00
<bradleymeck>
devsnek: there was at one point, but it is so error ridden it was terribly low when aklein did it
21:00
<bakkot_>
devsnek `extends null` currently results in a thing which is impossible to instantiate, which is almost certainly not the thing anyone wants
21:00
<bakkot_>
bradleymeck well, I don't know if I can say it any more clearly than it was said above, but I'll give it one more shot.
21:00
<devsnek>
i'm thinking we might just throw if its null
21:01
<devsnek>
remove the special case for null rather
21:01
<ljharb>
devsnek: it's not really a special case right now tho, is it?
21:01
<devsnek>
it is
21:01
<bradleymeck>
bakkot_: thanks, I'm just very confused on what this class heir-achy idea in some circle i've not participated in is being broken by :(
21:02
<devsnek>
ljharb: ClassDefinitionEvaluation 5.e
21:02
<bradleymeck>
i never had this mandate in my college that base classes must never do some sort of super, just that they don't
21:03
<bradleymeck>
they must not inherit
21:03
<devsnek>
the easy way out here would be to specify that Function.prototype returns Object.create(null) when [[Construct]]ed
21:03
<bradleymeck>
definitely cannot do that
21:04
<devsnek>
why not
21:04
<bradleymeck>
i've certainly seen code that uses new Function
21:04
<bradleymeck>
oh you mean the proto itself
21:04
<devsnek>
yes
21:04
<bradleymeck>
mmm
21:04
<bradleymeck>
odd
21:04
<devsnek>
when you do `class extends null`
21:04
<devsnek>
the parent is `Function.prototype`
21:04
<devsnek>
which is why it can't be instantiated
21:06
<ljharb>
if you did that "easy way out", wouldn't one expect `instanceof Function.prototype` to be true?
21:06
<devsnek>
not inherently?
21:06
<devsnek>
the result of `new X` doesn't have to fulfill `instanceof X`
21:07
<bradleymeck>
ljharb: i mean builtin protos are weird already, some are instances of their function, some are regular objects, some have the brand of their function
21:07
<ljharb>
i feel like that's still an expectation people would hold in the common case
21:07
<ljharb>
if we're going with "null isn't part of the class hierarchy" as an expectation
21:08
<devsnek>
i think all the reasonable alternatives are listed in the pr somewhere
22:26
<bakkot_>
bradleymeck sorry, I had a meeting before I could finish writing this up
22:27
<bakkot_>
anywhere here: https://gist.github.com/bakkot/d931e4d67a4d44ff79261d3699621cc8
22:28
<bakkot_>
these are all things that I think, but also concretely I think they correctly describe the intuitions of most people who use `class` in languages like JS, Java, python, C++, etc
22:37
<rkirsling>
bakkot_: 💯