00:48
<bakkot>
For me it’s also that the bot is slower than the stenographer. For me to be of any use taking notes I have to add a 5-10 second delay to my audio. Which further means I can’t interact with what is happening.
I will tweak the bot a little for this evening to make it more eager at the cost of being a little less accurate, which be a good tradeoff; we'll have to see.
00:57
<Michael Ficarra>
no host again?
00:58
<msaboff>
Appears not
00:59
<nicolo-ribaudo>
I can start it,give me a few seconds
01:00
<Mathieu Hofman>
I don't think it asked for the code
01:01
<littledan>
You may need to refresh now to get in
01:01
<littledan>
The meeting is started
01:13
<rbuckton>
one second
01:13
<rbuckton>
audio issue, will be sorted in a moment
01:28
<shu>
Michael Ficarra: yes to your queue question, afaict
01:28
<shu>
it is basically a const with some extra semantics
01:28
<Michael Ficarra>
I figured, but want to confirm
01:29
<Michael Ficarra>
const bindings can be closed over and don't ever become TDZ once they are bound, this is kind of the opposite of consts
01:29
<shu>
wait what
01:29
<shu>
how can using become TDZ?
01:30
<Michael Ficarra>
after disposal I would assume that's what happens
01:30
<shu>
what that would be crazy
01:30
<Michael Ficarra>
does it just keep its current value?
01:30
<shu>
i hope that doesn't happen, let me read spec draft again
01:30
<shu>
it just calls @@dispose i thought
01:30
<shu>
it doesn't do any binding magic
01:31
<Michael Ficarra>
it's probably more useful to developers to TDZ the binding
01:32
<littledan>
doesn't let have this same syntactic quirk?
01:32
<shu>
i am strongly opposed uninitializing the binding after disposing
01:32
<shu>
i'm also pretty sure that's not what the current proposal does
01:32
<littledan>
this would be the first time we would move to a TDZ
01:32
<littledan>
we're still a memory-safe language so I don't really see why this is an important thing to do
01:33
<Michael Ficarra>
it would just discourage accidental use of something that's been disposed
01:33
<Michael Ficarra>
it would be easier to identify bugs
01:33
<nicolo-ribaudo>
I think it's not unreasoable to move it to TDZ, it would prevent you from using a variable which is already meant to not be used anymore
01:33
<shu>
it would complicate what little more hope we have of optimizing away TDZ
01:33
<Michael Ficarra>
unless the disposal protocol is meant to be used on things that remain useful after disposal?
01:34
<littledan>
the accidental use case would need to be from someone's disposal callback, so it'd only come up if you did using x = {[Symbol.dispose]() { /* variable which is closed over that is also a using binding \*/ } };
01:34
<shu>
the bar for messing with bindings should be very high
01:34
<littledan>
the accidental use case would need to be from someone's disposal callback, so it'd only come up if you did using x = {[Symbol.dispose]() { /* variable which is closed over that is also a using binding */ } };
IMO this case is just not worth our design energy
01:34
<Michael Ficarra>
littledan: no? a closure could escape
01:34
<littledan>
oh, oops
01:35
<nicolo-ribaudo>
{
  using db = getConnection()
  setTimeout(() => db.query("..."), 100);
} // disposes the connection  
01:36
<ljharb>
why do we allow const there?
01:36
<ljharb>
it kind of seems like something that'd be web compatible to disallow, since it'd be unlikely someone ships that code
01:36
<shu>
moving it back into tdz is super weird
01:36
<nicolo-ribaudo>
moving it back into tdz is super weird
pdz, "permanent dead zone"
01:37
<littledan>
I guess implementers of the disposal protocol need to handle use-after-dispose anyway; moving to TDZ could help if it removed that need for them but it wouldn't
01:37
<nicolo-ribaudo>
Uhm right, it wouldn't because the disposable value can still escape even if the binding doesn't
01:38
<nicolo-ribaudo>
You just have to reassign it to a variable
01:38
<Michael Ficarra>
poison pill the value πŸ‘Ώ
01:39
<littledan>
who? what?
01:39
<ljharb>
you can only using a revocable proxy, and disposal revokes it :-p
01:40
<shu>
michael it sounds like you want a language that doesn't start with java and end with script
01:41
<Michael Ficarra>
I really want to know whether it is ever reasonable to use a value after disposal
01:41
<Michael Ficarra>
or if our intent is that that is never appropriate
01:41
<ljharb>
a file handle could be reopened, or a DB connection?
01:41
<shu>
i think it is reasonable
01:41
<Michael Ficarra>
if it is not reasonable, we should do what we can to surface an error when that reuse happens
01:42
<shu>
RAII doesn't always mean "irrevocably unusable"
01:42
<shu>
sometimes you just piggyback on RAII scope to do things like locking mutexes
01:42
<shu>
or temporarily unlocking
01:42
<littledan>
I think it's OK that this is the responsibility of the implementer of the disposal protocol
01:42
<shu>
those values remain usable
01:42
<Kris Kowal>
objects with casual state machines are normal. double return on an iterator, double close, &c
01:42
<Michael Ficarra>
hey, I must have missed this slide!
01:49
<snek>
hot take: we should take everything in contextlib and explicitly avoid it
01:53
<bakkot>
we should normalize larger timeboxes for larger proposals
01:56
<Michael Ficarra>
I would be opposed to a timebox extension to discuss things we are not even considering at the moment
02:03
<littledan>
yeah error cause seems weird since you'd need to mutate the other exception. AggregateError seems correct
02:04
<bakkot>
love me some AV issues
02:04
<littledan>
I would be opposed to a timebox extension to discuss things we are not even considering at the moment
IMO it is important that we give adequate time to this discussion, and most of that is on the proposal
02:05
<littledan>
(especially since this is for stage advancement, so it should be especially prioritized)
02:06
<littledan>
I don't understand ljharb's comment about concurrent errors--is that explaining why this model does or doesn't make sense?
02:07
<littledan>
also +1 to avoiding processing nested AggregateErrors by default
02:07
<ljharb>
it's replying to kevin's statement about using cause instead of AE. kevin claimed something akin to "AE is for errors that occur at roughly the same time", and i was adding that i think "at the same time" isn't part of the mental model for me.
02:07
<Michael Ficarra>
btw I still feel using [x] = iterable being valid is quite the footgun. Can someone explain why it is not?
02:07
<bakkot>
that will usually throw immediately, no?
02:07
<bakkot>
because using will not be in scope?
02:08
<Michael Ficarra>
πŸ€” hmm, okay
02:08
<ljharb>
Array.prototype[Symbol.dispose] = () => {}
02:08
<Michael Ficarra>
I guess a linter would help catch the unbound reference as well
02:11
<littledan>
Don't we have plenty of experience in other GC'd languages which do something like using but don't introduce such a new form of TDZ?
02:11
<bakkot>
those languages don't create closures nearly as often
02:11
<snek>
what is the new tdz, i missed it
02:12
<Michael Ficarra>
snek: scroll up, search for TDZ
02:12
<littledan>
those languages don't create closures nearly as often
huh, maybe we should go through this case-by-case
02:12
<littledan>
what is the new tdz, i missed it
it's that the using binding should enter a TDZ when the scope ends
02:13
<snek>
like after the block executes it sets the binding to ~uninitialized~?
02:13
<rbuckton>
snek: current slide
02:13
<snek>
i don't see slides
02:13
<snek>
i wlll reload
02:13
<snek>
ok i see
02:14
<ptomato>
console.group()/console.groupEnd()?
02:14
<ljharb>
it's a fair point that it's weird to "dispose" of something and have it still be around - disposal implies chucking it into the trash forever
02:14
<Michael Ficarra>
to be fair, the points on the slide are being made about the object reference, not the binding
02:14
<littledan>
I agree with shu here; this isn't about freeing the memory
02:14
<Anthony Bullard>
This isn't C
02:15
<Anthony Bullard>
This is about the release of resources associated with an object
02:15
<snek>
this would be valid right? using x = y(); let xEscape = x; setTimeout(() => consume(xEscape), 100)
02:15
<Justin Ridgewell>
What if I need disableable is a key in a map?
02:15
<Justin Ridgewell>
I may need to clean the entry at a later point.
02:15
<Michael Ficarra>
snek: that is the point about introducing a const alias on the slide
02:15
<ljharb>
Justin Ridgewell: would const x = <expr>; using x; work? or would you need like using y = x; and just not use the y
02:15
<snek>
ah i see yeah
02:16
<littledan>
IMO the burden of proof for use cases is on the other side; bakkot should explain what important errors are prevented by this TDZ (since it's a lot of extra work at multiple levels to provide this)
02:17
<bakkot>
attempting to use a file handle after closing it
02:17
<Michael Ficarra>
ljharb: just change the const to using, right?
02:17
<bakkot>
or literally anything else
02:17
<littledan>
well, IMO this error should be given to you by the file implementation
02:17
<littledan>
(as we've been discussing)
02:17
<ljharb>
i mean like if you need to use it as a key in the map, you'd just save it in a separate variable
02:18
<littledan>
so I'm very confused about what I've been missing. Maybe there could be a utility class bulit-in which implements this "casual state machine" as Kris put it
02:18
<Michael Ficarra>
ljharb: then yes, that or the other way around using x = <expr>; const y = x;
02:19
<snek>
i think i am convinced that we should do TDZ
02:20
<littledan>
maybe we should take a break from this topic and come back to using afterwards
02:20
<snek>
while also knowing that this requires prayers for implementors
02:20
<Michael Ficarra>
yeah I'm leaning toward having TDZ as well, since the arguments of re-using the disposed object are about the object, and I don't see a good reason to re-use a disposed binding
02:21
<snek>
it forces you to explicitly ref it if you want to reuse it which seems good
02:21
<HE Shi-Jun>
bakkot: I agree it's better to avoid reference the disposed thing, but could linter solve the problem?
02:21
<snek>
i don't think a linter can solve that, it requires knowledge of the runtime behavior of the program
02:22
<shu>
the linter can just say "don't close over using"
02:22
<bakkot>
well, IMO this error should be given to you by the file implementation
it would be nice if it did but I don't see why we want the language to do that for you?
02:22
<shu>
it doesn't need to be smart
02:22
<snek>
for example { using x = y(); myArray.forEach(() => thingWith(x) }) } this is valid
02:22
<littledan>
yeah all of this "TDZ" stuff could be added to any version of this proposal that we've discussed over the past several years
02:22
<Michael Ficarra>
shu: but we have higher-order functions on arrays and iterators and such
02:22
<HE Shi-Jun>
for example { using x = y(); myArray.forEach(() => thingWith(x) }) } this is valid
TS may have "call immediate" concept later?
02:22
<shu>
then your linter needs to do escape analysis
02:23
<snek>
every linter is now also a symbolic interpreter
02:24
<littledan>
it would be nice if it did but I don't see why we want the language to do that for you?
so, in general, no? I don't want the language to have half-working state tracking mechanisms that libraries will need to duplicate.
02:24
<littledan>
the duplication of state is necessary anyway for soundness
02:24
<ljharb>
what concept is this, and why would a possible future TS concept block a JS language feature?
02:25
<shu>
there are two competing mental models here: 1) using being about single-use, and 2) using being about registering functions to call on lexical scope exit
02:25
<snek>
i don't see those are competing
02:25
<snek>
one of those is the use case and the other is the mechanism
02:25
<HE Shi-Jun>
there are two competing mental models here: 1) using being about single-use, and 2) using being about registering functions to call on lexical scope exit
I think current proposal more close the 1, not 2, if 2, we should choose "defer" syntax like Go.
02:25
<shu>
is this about naming?
02:25
<shu>
is this because it's called @@dispose?
02:26
<littledan>
I'm in 100% agreement with all the arguments that Ron is making
02:26
<snek>
not for me
02:26
<shu>
yes, +1 to Ron
02:26
<Bradford Smith>
function f() { let x = 3; return () => x; }; f()(); is 3 == why would "using x" be different? I'm missing something somewhere. Is KG saying "x" should become undefined after its dispose is called?
02:26
<waldemar>
I don't think "we have lamented TDZs" speaks for all of us.
02:26
<ljharb>
function f() { let x = 3; return () => x; }; f()(); is 3 == why would "using x" be different? I'm missing something somewhere. Is KG saying "x" should become undefined after its dispose is called?
it'd be a TDZ error, like if you did x; let x;
02:27
<Bradford Smith>
so it would throw an error
02:27
<bakkot>
function f() { let x = 3; return () => x; }; f()(); is 3 == why would "using x" be different? I'm missing something somewhere. Is KG saying "x" should become undefined after its dispose is called?
I am saying it should be an error, not undefined
02:27
<bakkot>
and it should be an error because you are attempting to access a binding after the binding has been disposed
02:28
<bakkot>
I am confused by the "not discoverable" claim
02:28
<bakkot>
that seems... super discoverable?
02:29
<snek>
the use case here (at least the use case i am interested in) is resource management. i think the tdz provides an important guard against misuse when taking advantage of that use case. (i would also not be interested in advancing a "golang defer" proposal)
02:29
<snek>
i'm really not sure when i would want to use this without the tdz
02:30
<snek>
if anyone has an example of when you would want to use this without a tdz i would be interested
02:30
<HE Shi-Jun>
To be honest, I like TDZ, but I can also live without TDZ.
02:32
<HE Shi-Jun>
If there was an "use stricter", I would like it have TDZ πŸ˜‚
02:33
<Michael Ficarra>
i'm really not sure when i would want to use this without the tdz
same, and the only response given was "aliasing bindings is hard", which I do not buy in the slightest
02:34
<Anthony Bullard>
This is a naive question:. Would it not be possible to throw on capturing the using binding instead of a TDZ?
02:34
<snek>
i think throwing on capture would be too strict
02:34
<snek>
for example if you use array.forEach
02:34
<snek>
that fulfills the requirement of completing within the block
02:35
<snek>
and actually
02:35
<snek>
if you had to alias for all captures
02:35
<snek>
you'd lose the ability to catch aliases that were too lenient
02:35
<snek>
unless you manually undefined all of them at the end of the scope
02:35
<Anthony Bullard>
True
02:35
<snek>
which is sort of a recursive loop back to "i want to clean things up at the end of a scope"
02:36
<Anthony Bullard>
Just running through my head other ways to solve the issues involved here
02:37
<snek>
yeah i think we should probably not advance the proposal today
02:37
<snek>
lots of things for people to think about
02:38
<HE Shi-Jun>
the concept is TS could have the type info that the function would be call immediately, so will not extend the lifetime of the bindings captured by the closure. I don't mean this should block something. Just want to discuss whether the argument could be solved by tools (not by language).
02:38
<snek>
i don't use TS though
02:38
<snek>
except for at work
02:40
<snek>
bakkot: what if theoretically it was written as use(resource, onDispose = undefined)
02:40
<Anthony Bullard>
Isn't the second form for passing resources that may not have a dispose method itself, and there is not a desire to wrap it?
02:41
<snek>
personally i'd be fine with the proposal not having the disposablestack api
02:41
<snek>
it seems trivially implementable in userland it think?
02:41
<HE Shi-Jun>
This is a naive question:. Would it not be possible to throw on capturing the using binding instead of a TDZ?
I think it's too strict, unless we have block param proposal advanced which might support syntactical call immediate semantic.
02:44
<Anthony Bullard>
Second one could be `useWith`?
02:57
<littledan>
the "web platform collaboration" queue item is also sort of a blocking issue that I want to get on the record
03:00
<littledan>
I already raised this issue in GitHub https://github.com/tc39/proposal-explicit-resource-management/issues/95 ; I hope this can be followed up on before Stage 3
03:03
<littledan>
dminor: I'd like to understand better why Mozilla is unconvinced by the need for syntax; I think this has been explained well since the beginning.
03:06
<dminor>
The discussion we had is that we would like to see more evidence as to why try/finally is not adequate to solve these use cases
03:06
<bakkot>
you can only using a revocable proxy, and disposal revokes it :-p
... honestly I don't hate that
03:06
<bakkot>
I guess maybe I do hate that
03:06
<bakkot>
it's a fun idea though
03:06
<bakkot>
bad, but fun
03:07
<ljharb>
if anyone doesn't hate it, i made a terrible terrible mistake
03:07
<bakkot>
if it weren't for the identity discontinuity I would not hate that
03:07
<rbuckton>
The discussion we had is that we would like to see more evidence as to why try/finally is not adequate to solve these use cases
One of my main motivations is related to how try..finally is not adequate to solve these cases given the complex nesting that needs to occur when working with multiple resources.
03:08
<ljharb>
since not everyone even uses a linter, which is an uncontroversial best practice, let alone a type system, which is not an uncontroversial best practice, i don't think solving it with tooling is sufficient
03:08
<littledan>
The discussion we had is that we would like to see more evidence as to why try/finally is not adequate to solve these use cases
I believe developers don't tend to make good enough use of try/finally since it's too wordy, and just leak things instead--I think this is why many other languages have added this construct despite also having try/finally (itself a validation)
03:08
<littledan>
it will be good to collate evidence here, I agree, but I think we can do that
03:09
<littledan>
dminor: Is there an issue tracking your feedback, so we can collect this information there?
03:09
<bakkot>
it kind of seems like something that'd be web compatible to disallow, since it'd be unlikely someone ships that code
re the "why do we allow const in for-loop heads", https://github.com/tc39/proposal-explicit-resource-management/issues/96#issuecomment-1239940758
03:09
<HE Shi-Jun>
The discussion we had is that we would like to see more evidence as to why try/finally is not adequate to solve these use cases
I believe the README already give the solid motivations? For example, It's too hard to manage multiple resource correctly...
03:09
<rbuckton>
try..finally also has the limitation of suppressing exceptions from the body, which are valuable for both logging and error recovery purposes.
03:10
<shu>

so here's a use case where i don't want tdz: recursive mutexes. to be sure, it's expressible even in the case of a TDZ via additional aliasing but that is not something i want to type.

my intuition here is that the usual extreme badness about closing over stack-lifetimed variables is memory safety, which is not the footgun here. so my expectation is that most objects would throw at runtime in some way after being disposed of anyways, instead, for example, accessing random memory. if TDZ throws a runtime error, i expect that to be net the same effect, except with extra implementation burden (at least the investigative portion, if not also the implementation complexity portion)

03:12
<rbuckton>

so here's a use case where i don't want tdz: recursive mutexes. to be sure, it's expressible even in the case of a TDZ via additional aliasing but that is not something i want to type.

my intuition here is that the usual extreme badness about closing over stack-lifetimed variables is memory safety, which is not the footgun here. so my expectation is that most objects would throw at runtime in some way after being disposed of anyways, instead, for example, accessing random memory. if TDZ throws a runtime error, i expect that to be net the same effect, except with extra implementation burden (at least the investigative portion, if not also the implementation complexity portion)

This matches my intuition as well. Disposable objects should guard against use that is inconsistent with their state regardless as to whether you used using or let/const. However, such a guard is not mandatory as there are valid use cases for disposables that can be reused, such as re-opening a connection.
03:13
<rbuckton>
Enforcing this both in the object itself and in the using binding (by introducing a new TDZ) seems excessive.
03:15
<HE Shi-Jun>
Could we have an example of reusing so I can understand it more easy? thank u!
03:16
<bakkot>

so here's a use case where i don't want tdz: recursive mutexes. to be sure, it's expressible even in the case of a TDZ via additional aliasing but that is not something i want to type.

Can you write out the example there?

03:19
<ljharb>
meh, i don't find that compelling at all
03:19
<rbuckton>
Could we have an example of reusing so I can understand it more easy? thank u!

Since we don't have using void, essentially:

const connection = new Connection();
connection.open();
{
  using _ = connection;
  ...
} // connection is closed
connection.open();
{
  using _ = connection;
  ...
} // connection is closed
03:20
<rbuckton>
We want to guarantee the connection is closed in both the normal and exceptional cases, so that we can reopen it following the block.
03:20
<rbuckton>
This is a bit contrived, for a more real world example I'll have to spend more time putting something together.
03:22
<bakkot>
so in those case you aren't... reusing the binding
03:25
<HE Shi-Jun>
This example seems weird to me...
03:27
<HE Shi-Jun>
Why we can't write using conn = Connection.open() twice and let Connection class do the reusing of underlying connections?
03:28
<shu>
bakkot: something like https://gist.github.com/syg/26e303748b5ebc3ed5206a2b875293fb
03:28
<shu>
err there's a typo there
03:29
<rbuckton>
so in those case you aren't... reusing the binding
No, sorry. That example relates to general reuse of a disposable.
03:30
<shu>
i'd also like to better understand what the badness is with closing over a disposed thing
03:31
<shu>
like, what do you expect to happen, if it's not a runtime error, since that's what the TDZ gives you
03:31
<shu>
is it "it might do some other stuff before getting to the error point"?
03:31
<shu>
are your errors generally recoverable? is it bad that it did some other stuff?
03:32
<bakkot>
it is potentially bad that it did some other stuff, certainly
03:32
<rbuckton>

As far as binding reuse, the example is not much different than the example in your issue:

{
  using x = foo();
  cache.set(x.url, data);
  ...
  setTimeout(() => cache.delete(x.url), 100);
}
03:32
<bakkot>
but more generally, the language should give you an error at the point at which it is obvious you have made a mistake
03:33
<shu>
my contention is that in practice, it does, because most objects would throw in some fashion at runtime after having its internal resources disposed
03:33
<shu>
if we had a static TDZ then i would not be of this opinion
03:33
<bakkot>

As far as binding reuse, the example is not much different than the example in your issue:

{
  using x = foo();
  cache.set(x.url, data);
  ...
  setTimeout(() => cache.delete(x.url), 100);
}
that seems like it is a mistake - you're using x after it's been disposed. it seems like the language should prevent you from doing that.
03:34
<shu>
that's the philosophical difference
03:34
<bakkot>
if you explicitly want to use the object after it's been disposed, you should put it in a binding other than the one which is going to get disposed.
03:34
<rbuckton>
Yes, you can create another const to capture x.url to close over it, but that seems awkward just to enforce something via the binding that the disposable itself also needs to enforce internally.
03:34
<shu>
i do not think it is a mistake to reuse x after it's been "disposed", which i take to mean no more than "had some scope exit function ran"
03:34
<bakkot>
a scope exit function named Symbol.dispose, like from DisposableStack
03:34
<bakkot>
it's not like I am making up the word "dispose" here
03:35
<shu>
that's why i asked is it a naming issue earlier
03:35
<shu>
i thought ron named it thus cribbing from C# terminology
03:35
<bakkot>
also the proposal is about resource management
03:35
<rbuckton>
that seems like it is a mistake - you're using x after it's been disposed. it seems like the language should prevent you from doing that.
Not if x.url isn't related to the memory/native resource/etc. that x is holding.
03:35
<shu>
i am sorry to say i'm not sure how either side can be swayed here
03:36
<shu>
if seems to boil down to i think this pattern isn't a mistake, while you do
03:36
<bakkot>
Not if x.url isn't related to the memory/native resource/etc. that x is holding.
I mean that if I saw that code snippet in any codebase, I would assume it was a mistake and ask the author to rephrase it
03:36
<shu>
if i saw that in C++ or Java i would!
03:36
<shu>
but memory safety isn't an issue here!
03:37
<rbuckton>
I'm concerned that introducing a new TDZ to enforce this is overkill. It will impact host implementations and introduce complexity for valid cases (by requiring a const alias), for almost no added benefit when disposables also need to guard themselves.
03:38
<bakkot>
memory safety isn't an issue, but the whole concept of this proposal is that you are tying the lifetime of a resource to a specific binding. like, that's why it makes sense to have a thing happen at "scope exit" - because once you leave the scope, you are no longer able to use the binding.
03:38
<bakkot>
except that you are, through closures which outlive the binding.
03:38
<shu>
i hear you, but i have it layered the opposite way in mind
03:38
<bakkot>
but if you are able to reuse the binding after the scope has exited, it does not make sense to tie the lifetime of the resource to the scope
03:38
<rbuckton>
Calling x[Symbol.dispose]() doesn't explicitly free memory. If you didn't have using the object would still be resident in memory. It merely informs the object it should dispose of any relevant resources it holds.
03:39
<shu>
the useful thing of this proposal, regardless of its name and the name of Symbol.dispose, is to register a thing to happen at "scope exit", which IME extends beyond resource management
03:39
<bakkot>
that would be Go's defer, which is a reasonable alternative to this proposal
03:39
<shu>
so i see no compelling reason to build in a hairshirt for resource management, because that's an enabled use case, not the sole value to me
03:39
<bakkot>
however, that is not this proposal
03:39
<shu>
it is this proposal mechanically via an object method?
03:40
<rbuckton>
Unfortunately, go's defer doesn't help with inconsistent resource management APIs in the web and NodeJS platforms. defer isn't composable in the same way that [Symbol.dispose] is.
03:41
<littledan>
Yeah I see making a symbol-based protocol for disposal as an extremely useful part of this proposal
03:41
<rbuckton>
Also, Go's defer is function-scoped, not block-scoped, which is a very coarse grained lifecycle.
03:41
<bakkot>
it is this proposal mechanically via an object method?
you can use it for the same effect, but you can also use try finally for the same effect. everything about the shape of the API - the introduction of a binding, the name of the method, the fact that it is called using - implies it is for resource management. that is the thing it is for, that is the problem it is solving.
03:42
<bakkot>
it is nice that you can also use it for registering a thing to run at scope exit
03:42
<bakkot>
but if we wanted to introduce specifically "register a function to run at scope exit", that would not look like this proposal
03:43
<rbuckton>
But that isn't the only problem it solves, even if its the most common case. You're suggesting we introduce a new TDZ mechanism, and that feels very heavy handed.
03:43
<shu>
i find having an object registered with it valuable
03:44
<bakkot>
it genuinely feels more surprising to me to have the binding outlive the scope than not.
03:44
<bakkot>
since the whole design of the proposal assumes that the end of the scope is when you are done with the thing
03:44
<rbuckton>
using is essentially a special form of const, and const values remain immutable. Introducing TDZ makes it mutable since its value (or whether you can even inspect that value) can change.
03:45
<bakkot>
const also has tdz.
03:45
<bakkot>
it is no more mutable than const is.
03:45
<shu>
again, i contend, the existing intuition that we need TDZ and the footgun is bad is because RAII is mostly used in languages in conjunction with stack-lifetimed bindings, so memory safety becomes an issue
03:45
<rbuckton>
only until it is initialized.
03:45
<shu>
that intuition doesn't really apply here
03:45
<rbuckton>
it is no more mutable than const is.
It's definitely more mutable than const is.
03:46
<bakkot>
in that it has two points at which it changes rather than one?
03:46
<bakkot>
I mean
03:46
<bakkot>
that is technically "more" but like
03:46
<bakkot>
not fundamentally different.
03:47
<snek>

so here's a use case where i don't want tdz: recursive mutexes. to be sure, it's expressible even in the case of a TDZ via additional aliasing but that is not something i want to type.

my intuition here is that the usual extreme badness about closing over stack-lifetimed variables is memory safety, which is not the footgun here. so my expectation is that most objects would throw at runtime in some way after being disposed of anyways, instead, for example, accessing random memory. if TDZ throws a runtime error, i expect that to be net the same effect, except with extra implementation burden (at least the investigative portion, if not also the implementation complexity portion)

wouldn't this be using mutex.lock() not using mutex?
03:47
<snek>
like how it works in other languages
03:48
<rbuckton>
I prefer not to introduce the TDZ unless it is a major blocking concern as I'm not convinced its warranted. That said, I have already put together a PR to implement it if it becomes necessary for advancement.
03:49
<rbuckton>
wouldn't this be using mutex.lock() not using mutex?
More like using lck = mutex.lock() (or previously using void = mutex.lock() if you didn't need the binding).
03:49
<snek>
yeah, but that gets rid of the problem with needing an alias
03:49
<shu>
did you see the example?
03:49
<bakkot>
again, i contend, the existing intuition that we need TDZ and the footgun is bad is because RAII is mostly used in languages in conjunction with stack-lifetimed bindings, so memory safety becomes an issue
I don't really write that much C++ these days; I don't think that's where my intuition is coming from. my intuition is coming from the fact that the end of the scope is when we call the dispose method. the only reason that "end of scope" is a sensible place to call this method is because the binding is going out of scope, so that you can't using it in the code after the scope. so we should throw an error if, through a closure, you use the binding after that point.
03:50
<snek>
uhhh i guess not?
03:50
<snek>
i might've missed stuff i scrolled way up
03:50
<shu>
snek: https://gist.github.com/syg/26e303748b5ebc3ed5206a2b875293fb
03:50
<snek>
hmmm ic
03:50
<snek>
i guess that is a way to do that
03:51
<shu>
bakkot: i understand your viewpoint, i think, but it is not my viewpoint
03:51
<bakkot>
shu: in your viewpoint, why is the end of the scope a sensible place to be running code associated with this binding, then?
03:52
<shu>
because that's the syntactic affordance to lump together a transaction-like operation
03:53
<bakkot>
that's a reasonable reason to run code at the end of a scope, like defer (except for the function-vs-block thing).
03:53
<bakkot>
but why is that a reason to run code associated with a particular binding?
03:53
<shu>
because JS is OO and i also prefer to associate state with my code instead of making new closures, since chances are it's already structured with objects with state?
03:53
<littledan>
but why is that a reason to run code associated with a particular binding?
well, because empirically, you often want to both bind to something and dispose of it
03:54
<littledan>
and C++ experience shows this is good enough
03:54
<bakkot>
littledan: if you are thinking of "dispose", you are not thinking of "lumping together a transaction-like operation"
03:54
<snek>
i guess shu i would say if you want something with that exact api you should probably be making an alias to signal your intent for it to escape the scope
03:54
<snek>
i get how that's annoying though
03:54
<bakkot>
because JS is OO and i also prefer to associate state with my code instead of making new closures, since chances are it's already structured with objects with state?
I do not really understand how that's responsive to my question?
03:55
<bakkot>
that's a reason to run code associated with a particular object, but not a particular binding
03:55
<bakkot>
the thing which registers code to be run is the introduction of the binding
03:56
<shu>
snek: and i would say that's not something i want to type, given that in practice my hypothesis is most objects who have had their internal state disposed would throw anyway, and errors are not recoverable IME in practice so i'm also not too worried about "doing bad things before the object got to the point of throwing the error"
03:56
<snek>
yeah i guess
03:57
<bakkot>
the sole existing example of a thing which is disposable in the language, iterators-as-created-by-generators, do not throw when you use them after calling their disposal method.
03:57
<snek>
what if the api of your mutex was just different
03:57
<shu>
let's take a step back. i don't see either side being swayed by these lines of argument
03:57
<shu>
how do we move forward?
03:58
<Rob Palmer>
We are resuming plenary in 2 mins! Is rbuckton around?
03:58
<bakkot>
well, it might be worth thinking about the problem more than one day
03:58
<snek>
using(with-tdz) x = y()
03:58
<Rob Palmer>
nicolo-ribaudo: are you around to present if Ron is not?
03:58
<bakkot>
but if we are permanently stuck, I dunno, vote? unless you want to use your implementor veto to say you are unwilling to implement it, in which case, I guess it goes forward without tdz.
03:58
<shu>
bakkot: well yes, agreed to that
03:59
<HE Shi-Jun>
I agree most opinions of bakkot , but I don't think this should be a block issue. I still think it could be solved by static analysis tools.
03:59
<littledan>
It seems extremely soon to declare ourselves permanently stuck. We made a ton of progress today, I think
03:59
<bakkot>
static analysis can't solve this unless it gets a lot better
03:59
<shu>
i would need to do what mark asked and actually investigate the implementation difficulty before saying i am unwilling to implement
03:59
<littledan>
let's keep bringing this topic back to committee to ensure we get broad review and keep making progress
03:59
<bakkot>
the whole reason it's called temporal dead zone is because it's temporal, not lexical; it's quite hard to analyze statically
04:00
<shu>
but it has not risen to that level of time commitment for me yet
04:00
<HE Shi-Jun>
static analysis can't solve this unless it gets a lot better
yeah we need rust compiler -like tool... hope it won't as slow as rust compiler πŸ€ͺ
04:01
<bakkot>
linear-typescript
04:01
<snek>
we could modify prepack
04:01
<bakkot>
did prepack ever start working?
04:01
<snek>
prepack sort of works
04:01
<snek>
if you only give it simple es5
04:01
<bakkot>
everything I ever ran it on it either did nothing or ran forever
04:02
<snek>
shu i do wonder like
04:02
<snek>
your example requires a pretty good understanding of some complex programming concepts
04:03
<ljharb>
* Rob Palmer: tcq needs advancing
04:04
<shu>
snek: if the problem to solve is common footguns that befall beginner programmers, a lint that prevents closing over using bindings at all but allowlists Array combinators is probably sufficient
04:04
<snek>
πŸ˜”
04:11
<HE Shi-Jun>
current pattern match when (${xxx} with {...}) is too tedious...😭
04:13
<ljharb>
bindings must be explicit; how could that be less tedious?
04:14
<HE Shi-Jun>
Yeah syntax is hard.
04:14
<bakkot>
scala is not a language I would hold up as an example of a mature and cohesive programming language
04:14
<Michael Ficarra>
I don't even think the people who work on Scala would claim that
04:14
<Michael Ficarra>
it's not even their goal
04:15
<HE Shi-Jun>
scala is not a language I would hold up as an example of a mature and cohesive programming language
There are many languages have similar feature, not only scala
04:16
<bakkot>
yeah I just thought it was funny to introduce the feature by talking about scala and then say the goal is cohesiveness
04:17
<Kris Kowal>
All those languages have something in common: you have to be at least this tall to ride.
04:17
<ljharb>
oh look, a 10th meaning of this
04:17
<Kris Kowal>
πŸ‘‰
04:18
<shu>
Kris Kowal: are you saying JS is a language for short kings
04:18
<HE Shi-Jun>
yeah I just thought it was funny to introduce the feature by talking about scala and then say the goal is cohesiveness
I think the cohesiveness is about destructuring and pattern match?
04:19
<Michael Ficarra>
wait who says calls can't be the target of an assignment?
04:19
<bakkot>
"cannot be the target of an assignment" ehhhh that is not as true as it should be but almost is true
04:20
<Kris Kowal>
Kris Kowal: are you saying JS is a language for short kings
No, but valid
04:20
<Jack Works>
IIRC IE can do that in the past but it is a Reference Error?
04:20
<HE Shi-Jun>
wait who says calls can't be the target of an assignment?
it could, but give u referenceerror
04:21
<Michael Ficarra>
HE Shi-Jun: depends on the reference returned by the call
04:21
<Michael Ficarra>
I can't write an ECMAScript function that returns such a reference but there can be host-provided built-ins that do
04:22
<HE Shi-Jun>
HE Shi-Jun: depends on the reference returned by the call
so how can function return something won't cause ref error as assigment target?
04:22
<HE Shi-Jun>
I can't write an ECMAScript function that returns such a reference but there can be host-provided built-ins that do
Oh, that would be cool! 🀩
04:23
<Michael Ficarra>
HE Shi-Jun: IE used to provide built-ins that did this for interacting with VB scripts
04:25
<HE Shi-Jun>
Maybe this proposal should only allow const/let declaration, not assigment.
04:29
<bakkot>
I can't write an ECMAScript function that returns such a reference but there can be host-provided built-ins that do
no we made that illegal I think?
04:29
<snek>
correct
04:31
<Michael Ficarra>
when did we do that?
04:31
<littledan>
I really like this proposal, but I think the InstanceExtractor is a kind of odd case--I hope this kind of feature is used mostly for cases where, what you have on the LHS could've done the inverse on the RHS
04:31
<littledan>
similarly, IMO we should add the object extractor syntax only in conjunction with a corresponding expression syntax (and filed https://github.com/rbuckton/proposal-extractors/issues/2 about this). I'm fine with it being included for Stage 1 though.
04:39
<Jack Works>
I like ThisOne(a, b), but I'm a little hesitate on ThisOne { a, b }
04:39
<rickbutton>
const { Map(foo): { bar, baz: { quzzz } } } = obj; ?
04:40
<rickbutton>
i.e. move the function name to the property being extracted, rather than the RHS
04:40
<littledan>
no?
04:41
<rickbutton>
heh, i mean in terms of getting away from Thing{ but yeah
04:54
<littledan>
There's ambiguity when people say "Stage $n concern"--sometimes people mean, an entry requirement, sometimes people mean something to do within that stage
04:54
<littledan>
I think this has caused miscommunication in the past; maybe we need other ways to refer to this
04:54
<Michael Ficarra>
I think it means the latter
04:55
<snek>
was yulia's big document about pattern syntax mentioned at all
04:56
<shu>
the epic?
04:56
<shu>
someone mentioned the epic
04:56
<littledan>
I think this has caused miscommunication in the past; maybe we need other ways to refer to this
For example, "Stage $n entry requirement", "Stage $n exit requirement", "Topic for investigation during Stage $n"
04:57
<littledan>
someone mentioned the epic
I think they mentioned epoch (which can be a homonym for epic in some kinds of English)
04:57
<littledan>
but yeah it's just been allusions
04:57
<bakkot>
tersness is less important than symmetry for me
04:57
<bakkot>
symmetry is the compelling thing that this has
04:58
<snek>
https://github.com/codehag/pattern-matching-epic
04:58
<littledan>
oh! oops
04:58
<snek>
if anyone hasn't seen this
04:58
<snek>
it is a good read
04:58
<snek>
there was also a google doc somewhere
04:58
<snek>
oh its linked as "my analysis" in the readme cool
04:58
<Jack Works>
I'm a little agree this is "just more clever" in most cases without pattern matching, but pattern matching won't add it unless it is in the destructing
04:59
<littledan>
rbuckton: Revisiting examples could also be good; I don't think the InstantExtractor is a very intuitive use case
04:59
<HE Shi-Jun>
It's just symmetry to constructor/factory.
04:59
<HE Shi-Jun>
So if u need to look inside to understand what factory do, u also may need do the similar thing. Abstraction always have cost.
05:00
<ljharb>
bakkot: pattern matching subsumes switch and i would argue is way more intuitive at first glance than extractors/unapply/etc
05:00
<bakkot>
depends on whether pattern matching has Symbol.matcher
05:01
<ljharb>
i see what you're saying there, yes
05:01
<ljharb>
but i don't think the issue mark is talking about with extractors is "it invokes a protocol", since we have lots of examples of that
05:05
<bakkot>
mark was saying match has a lot of syntax, which I definitely agree with
05:05
<shu>
i think that is a matter of fact, yes
05:07
<snek>
tc39 hats for note takers
05:07
<Michael Ficarra>
"I took notes at a TC39 meeting and all I got was this tshirt" tshirts
05:08
<rbuckton>
I agree it has a lot of syntax, but I also find pattern matching so valuable that its more than worth it.
05:08
<Rob Palmer>
tc39 hats for note takers
I was about to announce I have a hat to give away, but I only have one and have become attached to it.
05:08
<Michael Ficarra>
Rob Palmer: sacrifices must be made
05:09
<rbuckton>
I have two, but they are both well loved.
05:25
<Jack Works>
thanks nicolo-ribaudo for this pr. this makes my implementation of compartment proposal much easier.
05:25
<snek>
this is turning into kingdom hearts
05:26
<shu>
are you ready for me to show up dressed up as goofy with a keyblade
05:27
<snek>
always
05:27
<Kris Kowal>
yes
05:27
<Kris Kowal>
Thanks nicolo-ribaudo!
05:27
<littledan>
yay congrats nicolo-ribaudo !
05:32
<HE Shi-Jun>
if Object.freeze(), does that mean it can't be do brand check?
05:33
<shu>
there's no brand to check, you get a plain object back (but frozen)
05:33
<Robin Ricard>
yes in that case there is no Record exotic object
05:33
<HE Shi-Jun>
if Object.freeze(), does that mean it can't be do brand check?
ok i hear that it's just ordinary objects. fine.
05:49
<snek>
wait do boxed records exist or not
05:49
<snek>
i thought this gets rid of them
05:49
<rickbutton>
this would get rid of them
05:49
<snek>
what is all this about debugging
05:49
<rickbutton>
i.e. passing a record to Object just makes a Object.create(null)
05:49
<rickbutton>
that if you ran into one of these it should be debuggable
05:49
<snek>
hmmm
05:50
<snek>
so like
05:50
<snek>
there would be a differenec between Object.create(null, { a: { value: 1 } }) and Object(#{ a: 1 })?
05:50
<snek>
even though the point is that they emit identical things?
05:50
<rickbutton>
also freeze it
05:50
<rickbutton>
but no difference between those two, other than that
05:51
<shu>
there isn't, i think jordan is saying boxed primitives are important from his experience, so even if this isn't a boxed primitive, he wants the provenance to be programmatically discoverable
05:51
<snek>
right with freezing
05:51
<rickbutton>
but we don't do that for any other thing
05:51
<shu>
well, we have a boxed primitive for the other things
05:51
<snek>
if provenance is the problem obviously we need PNVI-ae-udi
05:51
<rickbutton>
well but this isn't a boxed primitive (and around we go :) )
05:51
<littledan>
there isn't, i think jordan is saying boxed primitives are important from his experience, so even if this isn't a boxed primitive, he wants the provenance to be programmatically discoverable
maybe replay.io is a good solution for such debugging flows
05:51
<shu>
to be clear i don't endorse his view
05:53
<shu>
i don't think "someone had a feature request on a library of mine" is a signal
05:53
<rickbutton>
waldemar: typeof thing === "record"
05:53
<snek>
provenance of objects sounds like a fun devtools exercise
05:53
<snek>
not a language feature
05:54
<rbuckton>
Is there any other built-in (primitive or otherwise) where (without modifying prototypes) a == b throws? That seems kind of bad, especially since people frequently do a == null tests.
05:54
<shu>
hm that's interesting
05:55
<snek>
can == throw?
05:55
<littledan>
To jump ahead and answer Waldemar's question: typeof!
05:55
<bakkot>
yes == can throw
05:55
<littledan>
oops rickbutton sniped me
05:55
<bakkot>
it can invoke arbitrary code
05:55
<rbuckton>
Yeah, if you have a bad Symbol.toPrimtive or valueOf, etc. But not for any existing built-ins.
05:55
<snek>
this is news to me
05:55
<shu>
but as far as default behavior, the null check is a common pattern
05:56
<bakkot>
'a' == { valueOf: () => null[0] }
05:57
<snek>
oh i see i was confused by the recursive calls using !
05:57
<snek>
there is a little ? ToPrimitive
05:57
<bakkot>
also groupBy results
05:57
<bakkot>
which have null proto
05:58
<snek>
how have i never noticed that == fails on null proto objects
05:58
<snek>
that is nuts
05:58
<rbuckton>
More and more reason to never use ==.
05:59
<bakkot>
it's fine as long as the RHS is null
05:59
<bakkot>
just not for anything else, ever
05:59
<HE Shi-Jun>
it's fine if both sides have same type
05:59
<snek>
the == null unary operator
06:03
<Ashley Claymore>
how have i never noticed that == fails on null proto objects
Object.create(null) == null doesn't throw?
06:04
<bakkot>
Object.create(null) == '' does though
06:05
<Ashley Claymore>
yes. That's what I said in the meeting :)
06:05
<Ashley Claymore>
compare to null is safe
06:05
<Rob Palmer>
Travel information to Spain is here: https://github.com/tc39/Reflector/issues/446
06:05
<Ashley Claymore>
the reason the example in the PR throws is because we are comparing to a non-null primitive
06:10
<Ashley Claymore>

Too tired to read the spec for module namespace. But trying in a console:

const m = await import('data:text/javascript,export default {};')
Reflect.ownKeys(m); // ['default', Symbol(Symbol.toStringTag)]
Reflect.getPrototypeOf(m); // null
m == null; // false
06:36
<bakkot>
shu: https://github.com/tc39/proposal-explicit-resource-management/issues/97 is the issue for TDZ for using bindings if you want to continue there
12:08
<nicolo-ribaudo>
The ecmarkup lint capabilities are getting quite nice, maybe one day it will be a full language server!
15:03
<bakkot>
possibly someday, but right now it's mostly a huge mess of regexes
15:03
<bakkot>
it's remarkable how far regexes will get you
21:20
<rbuckton>
The ecmarkup lint capabilities are getting quite nice, maybe one day it will be a full language server!
I was working on this, but I haven't had much time recently