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. |
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 |
01:34 | <Michael Ficarra> | littledan: no? a closure could escape |
01:34 | <littledan> | oh, oops |
01:35 | <nicolo-ribaudo> |
|
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 |
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 |
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 |
02:12 | <littledan> | what is the new tdz, i missed it 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 |
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 |
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? |
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) |
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? 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? |
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 |
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? |
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 |
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 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 |
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 |
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 |
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> |
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> |
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
|
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 |
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:
|
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> |
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.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 |
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? 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. |
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> |
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 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 |
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? |
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? |
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 |
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 |
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 |
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 |
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? |
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 |
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 |
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 |
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 |
04:57 | <littledan> | someone mentioned the epic |
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 |
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? |
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 |
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 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:
|
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! |