| 03:04 | <devsnek> | engine262 rewrite now failing fewer than 10,000 tests 🎉 |
| 03:04 | <leobalter> | Nice one! |
| 03:04 | <devsnek> | its mostly early errors at this point :( |
| 03:04 | <devsnek> | still don't have a great way to do early errors |
| 03:05 | <devsnek> | might rip out all the early error stuff i currently have and make it a second pass |
| 03:07 | <Bakkot> | traditionally early errors are part of the parser |
| 03:08 | <devsnek> | yeah still part of the parser api |
| 03:08 | <devsnek> | i mean like, `parseScriptBody` wouldn't be what checks for duplicate lexical bindings |
| 03:08 | <Bakkot> | I mean it could be |
| 03:08 | <devsnek> | everything is a mess |
| 03:08 | <devsnek> | `a.await` is a parse error rn |
| 03:09 | <devsnek> | i'm liking the idea of shift's reduce thing |
| 03:46 | <devsnek> | i wonder if it would be web compatible to make evaluators able to throw AggregateErrors instead of SyntaxError |
| 04:17 | howdoi | 10,000 tests! wow! |
| 13:26 | <gibson042> | the link for Decorators slides at https://github.com/tc39/agendas/blob/master/2020/07.md and in the notes doc doesn't work, can someone involved with the proposal fix it? |
| 15:11 | <devsnek> | what if function parameters had elisions |
| 15:12 | <bradleymeck> | `Object.defineProperty(new Proxy({}, {getOwnProperty(o, k) {return {value:1}}}), 'why', {value:2})` is... strange |
| 15:14 | <bradleymeck> | `{ let o = new Proxy({}, {getOwnPropertyDescriptor(o, k) {return {value:1}}}); Object.defineProperty(o, 'why', {value:2}).why; }` => 2 |
| 15:14 | <bradleymeck> | `{ let o = new Proxy({}, {getOwnPropertyDescriptor(o, k) {return {value:1}}}); o.why; Object.defineProperty(o, 'why', {value:2}).why; }` => 2 |
| 15:15 | <bradleymeck> | { let o = new Proxy({}, {getOwnPropertyDescriptor(o, k) {return {value:1}}}); Object.getOwnPropertyDescriptor(o, 'why'); Object.defineProperty(o, 'why', {value:2}); } => error |
| 15:15 | <bradleymeck> | you can read the non-configurable value |
| 15:16 | <bradleymeck> | and you can replace it, as long as you don't check if it is configurable |
| 15:16 | <Bakkot> | the `get` trap being distinct from the `getOwnPropertyDescriptor` trap is a bit weird, yeah |
| 15:17 | <Bakkot> | bradleymeck in your last example it's the `Object.getOwnPropertyDescriptor` which throws, not the `Object.defineProperty` |
| 15:18 | <bradleymeck> | also confusing |
| 15:19 | <bradleymeck> | you can read it, but not see its descriptor? |
| 15:19 | <bradleymeck> | what is this hell |
| 15:22 | <Bakkot> | > the `get` trap being distinct from the `getOwnPropertyDescriptor` trap is a bit weird, yeah |
| 15:22 | <bradleymeck> | i'm learning yea |
| 15:27 | <devsnek> | proxies are strange |
| 15:29 | <bradleymeck> | reflection always gets strange eventually |
| 18:22 | <bradleymeck> | what is a JS builtin that takes an options bag ? |
| 18:22 | <ljharb> | bradleymeck: 262 doesn't really have any, unless you count a Proxy handler, or Object.defineProperties/Object.create's second argument |
| 18:23 | <bradleymeck> | no, per the discussion we just had |
| 18:23 | <bradleymeck> | handler is thing which gets "this" value apparently |
| 18:23 | <bradleymeck> | options bag, doesn't |
| 18:23 | <bradleymeck> | defineProperties kind of? |
| 18:23 | <ljharb> | there's nothing in 262 that takes a function off an object and calls it directly |
| 18:23 | <ljharb> | in 402, there's tons of examples tho |
| 18:24 | <bradleymeck> | it doesn't really configure the algorithm |
| 18:24 | <bradleymeck> | should we cross ref 402 in the how-we-work term list? |
| 18:35 | <bradleymeck> | how about requisition() instead of upsert()/emplace() |
| 18:36 | <leobalter> | this proposal would be great if we had Decorators |
| 18:36 | <rkirsling> | ensure()? |
| 18:36 | <devsnek> | what if we made a new kind of function declaration |
| 18:36 | <devsnek> | that wasn't hoisted |
| 18:37 | <devsnek> | then we could just do normal style generators |
| 18:37 | <devsnek> | decorators* |
| 18:41 | <leobalter> | `await @all x` |
| 18:42 | <leobalter> | for me this looks better than `await.all x`, not going through stacking trace issues |
| 18:43 | <leobalter> | I'd be pretty satisfied with `await * x`, only giving sugar for Promise.all |
| 18:43 | <devsnek> | what about |
| 18:43 | <devsnek> | `for await concurrent (const x of y) {}` |
| 18:43 | <devsnek> | ~= `await Promise.all(y.map((x) => {}))` |
| 18:44 | <leobalter> | devsnek: probably a separate discussion but I'd be up to discuss it as well |
| 18:44 | <Bakkot> | devsnek that interacts weirdly with abrupt completions |
| 18:45 | <Bakkot> | it seems like it would lead to a lot more concurrency bugs than the current patterns |
| 18:46 | <devsnek> | i guess the hard part would be deciding what `break` does |
| 18:46 | <devsnek> | everything else seems well defined |
| 18:47 | <Bakkot> | presumably `return` exits the function as soon as any iteration hits it, and it's just a race? |
| 18:48 | <Bakkot> | that is the "a lot more concurrency bugs" thing |
| 18:48 | <devsnek> | oh yeah return |
| 18:48 | <bradleymeck> | rkirsling: kind of, but idk how that implies potential update |
| 18:49 | <devsnek> | Bakkot: the map is more about mapping to async functions, not the result of those async functions |
| 18:49 | <Bakkot> | devsnek I am not sure what that means |
| 18:49 | <Bakkot> | or, like, why it matters |
| 18:49 | <Bakkot> | I know how Promise.all works I just don't know why you are mentioning this |
| 18:50 | <rkirsling> | bradleymeck: true. was all I could think of at that moment though |
| 18:54 | <devsnek> | this is interesting https://es.discourse.group/t/nan-trapping-operators/402 |
| 18:55 | <bradleymeck> | rkirsling: .summon()? |
| 18:55 | <michaelficarra> | jackworks: the proposal I was referring to was https://es.discourse.group/t/array-prototype-uniqby/138 |
| 18:55 | <rkirsling> | nice |
| 18:55 | <bradleymeck> | .claim is pretty good |
| 19:01 | <bradleymeck> | do we have any knowledge of static JS tools that assume after a map.has(k) === true that map.get(k) should get a value that does exist |
| 19:01 | <bradleymeck> | also how do people feel about .get(k) firing a user code trap? |
| 19:15 | <jridgewell> | devsnek: I think this is at the wrong poit |
| 19:15 | <jridgewell> | point** |
| 19:15 | <jridgewell> | You'd want to throw when you generate the NaN, not when you compare with it. |
| 19:15 | <jridgewell> | bradleymeck: Typescript doesn't, last I checked |
| 19:15 | <devsnek> | jridgewell: `nonanscope {}` |
| 19:15 | <jackworks> | devsnek: "`for await concurrent (const x of y) {}`" yeah, I always want to have some kind of concurrent control over `for await` loops. maybe even further, with args `for await concurrent 4` means max 4 running or `for await concurrent _identifier_` to refer a custom task dispatcher |
| 19:17 | <jackworks> | Bakkot: yeah, maybe we ban the "break" or "return" only leave "continue" in `for await concurrent` like the do expression did |
| 19:21 | <Bakkot> | jackworks yeah, though I would still worry about closed-over values |
| 19:21 | <jackworks> | I remember there was a "use strong" in v8 that will throw immediately when a math operation yielding NaN, I like that idea, not in the language, but as a debugger tool. |
| 19:21 | <Bakkot> | for loops it is very common to have, like, `let x; for (...) { if (test) x = whatever }` |
| 19:21 | <Bakkot> | which, if the loop is concurrent, is probably a bug |
| 19:21 | <Bakkot> | you can have the same thing with Promise.all, of course, but it is much harder to run into |
| 19:21 | <jackworks> | Oh maybe we can suggest v8 to add a "Pause on NaN calculation" |
| 19:23 | <jackworks> | "if the loop is concurrent, is probably a bug" yeah but since it is opt-in, developer should learn about what is race condition |
| 19:24 | <Bakkot> | ehhhhhhhhhhhhhhhh |
| 19:24 | <Bakkot> | concurrency bugs are notoriously one of the Hard Problems |
| 19:24 | <Bakkot> | I would be very, very reluctant to introduce an affordance which makes them easier to run into |
| 19:25 | <Bakkot> | I do not find "developers should be careful" a compelling argument in this case, especially given how hard concurrency bugs are to avoid in codebases that don't have Rust-style ownership or similar guarantees |
| 19:25 | <jackworks> | "Promise.all, of course, but it is much harder to run into" agree, but the problem is today, async concurrent calculation is pain to write `await Promise.all(arr.map(async x=> ...))` too tedious |
| 19:29 | <jackworks> | "very reluctant to introduce an affordance which makes them easier to run into" reasonable... so we just let async concurrent pain to write, to let devs write serial async code? at least for me, sometimes it's really safe to do the async concurrent calculation but I don't want to write `await Promise.all(arr.map(async x=> ...)) ` these bunch of code so I let it run in serial |
| 19:31 | <Bakkot> | I am happy to explore affordances which make it easier to write concurrent code; I don't think `await.all` would have the same problem as `for await concurrent`, for example |
| 19:31 | <jackworks> | I think WeakRef and FinalizationRegistry are applicable to the same argument. they're easy to misuse but they're still added to the 262 and mdn.io gives warnings on it "Avoid where possible" |
| 19:32 | <Bakkot> | We would never have added WeakRefs if they hadn't been necessitated by wasm |
| 19:32 | <jackworks> | lol |
| 19:32 | <Bakkot> | but they're a fundamental capability, whereas `for await concurrent` is not a fundamental capability |
| 19:34 | <jackworks> | my friend was working on the membrane, he also needs WeakRef; my RPC library provides iterating on remote async generators so I need WeakRef too |
| 19:34 | <Bakkot> | `for await concurrent` is about making certain kinds of code easier to write, which means we need to think hard about whether we in fact want those kinds of code to be easier to write, and my answer is no because that _particular_ syntax makes it much easier to have concurrency bugs, more than other possible affordances for writing concurrent code |
| 19:34 | <Bakkot> | yeah they're useful in other cases, it's just that they have way too many problems to justify for those cases alone |
| 19:37 | <jackworks> | hmm, so people will re-inventing it again and again and it's more easily to have bugs in their own impl |
| 19:37 | <jackworks> | is it possible to design a syntax to make it easy to write, meanwhile reduce the possible race condition bugs? |
| 19:38 | <TabAtkins> | Phew, I haven't had to write it yet myself, but `await Promise.all(arr.map(async x=>...))` is *awful* to write. |
| 19:38 | <TabAtkins> | `for await.all(const x of arr){...}` plz |
| 19:38 | <jackworks> | (WebCrypto API on mdn even gives a big strong red warning box writes "If you're not sure you know what you are doing, you probably shouldn't be using this API.") |
| 19:40 | <jackworks> | maybe the `for await.all` sugar is too sweet and people will start to abuse it everywhere leads tons of race conditions, then the eslint decides it's a super foot gun and ban it 🤣 |
| 19:43 | <jackworks> | oh I recall a joke, when `async await` steps into our life, I saw an article says `await` is easy to get abused and new comer to JS will try to write `await` before any expression. |
| 19:43 | <Bakkot> | tbf people do that! |
| 19:43 | <Bakkot> | it just doesn't lead to bugs |
| 19:43 | <Bakkot> | so it's not a big problem |
| 20:42 | <bradleymeck> | name for a .get that defaults a value if no such key is present but does not insert it? (getDefault was seen as having either semantics) |
| 20:42 | <ljharb> | why not `.get(key[, default])` |
| 20:43 | <ljharb> | matches the current default of undefined |
| 20:43 | <Bakkot> | getOrDefault |
| 20:43 | <Bakkot> | or jordan's thing |
| 20:44 | <jridgewell> | `default` would need to be a function, there |
| 20:44 | <ljharb> | ah, true. can default to `() => {}` |
| 20:45 | <Bakkot> | getOrCompute |
| 20:45 | <devsnek> | getpute |
| 20:45 | <Bakkot> | jridgewell why "need"? |
| 20:45 | <ljharb> | winner |
| 20:45 | <Bakkot> | I see the use for it, just not the necessity |
| 20:46 | <jridgewell> | Because the default value can be very expensive to create |
| 20:47 | <Bakkot> | can be, but is that frequent enough that it is necessarily something which has to be met by this method? |
| 20:47 | <Bakkot> | for those cases, you can still do `if (!x.has()) x.put()` or whatever |
| 20:47 | <ljharb> | yes, in my experience |
| 20:48 | <ljharb> | it's often constructing an object or transforming data |
| 20:48 | <jridgewell> | To counter, you could just do `x.has(key) ? x.get(key) : default` for your case |
| 20:48 | <Bakkot> | right, of course |
| 20:48 | <Bakkot> | this is a question about frequency |
| 20:48 | <Bakkot> | in my experience, the cheap key is much more common |
| 20:48 | <Bakkot> | I am curious if your experience differs |
| 20:49 | <jridgewell> | cheap key? |
| 20:49 | <Bakkot> | *cheap default |
| 20:49 | <jridgewell> | Ah, |
| 20:49 | <jridgewell> | My use is templateing libraries |
| 20:50 | <jridgewell> | Particularly lit-html, which creates a whole `<template>` and children and walks the entire tree. |
| 20:51 | <Bakkot> | java has getOrDefault, which takes a value, and computeIfAbsent, which takes a lambda (and updates the map( |
| 23:06 | <TabAtkins> | I think a default-value factory is a requirement, yeah. Arrow functions make it cheap to define one for constant values, and they're necessary for expensive defaults. |
| 23:07 | <TabAtkins> | And I think the functionality should indeed just be added as a second parameter on .get(). |
| 23:08 | <TabAtkins> | (Python separates default-value from default-value-factory, but Python also has annoying inline functions.) |
| 23:33 | <devsnek> | what if we allowed elisions in function parameters |
| 23:45 | <TabAtkins> | Elaborate? |