00:01 | <waldemar> | If you care about performance, it's a bad idea to feed external input into a deterministic hash function. That invites attacks such as Hash Flooding: https://en.wikipedia.org/wiki/Collision_attack#Hash_flooding |
00:40 | <littledan> | If you care about performance, it's a bad idea to feed external input into a deterministic hash function. That invites attacks such as Hash Flooding: https://en.wikipedia.org/wiki/Collision_attack#Hash_flooding |
01:49 | <littledan> | I am not sure what we're supposed to get from Project Valhalla when primitives/value semantics has already been deemed not an option due to implementability issues |
01:51 | <littledan> | if the question is class-based vs object-based immutable things, I think there's a lot of interest among JS developers in having easy-to-use mechanisms for immutable data structures that don't force them to write classes |
01:54 | <rbuckton> | If you care about performance, it's a bad idea to feed external input into a deterministic hash function. That invites attacks such as Hash Flooding: https://en.wikipedia.org/wiki/Collision_attack#Hash_flooding |
01:54 | <littledan> | There's a lot of complexity with class-based immutable things, e.g., we'd need to use initializer list-based constructors, so if there's subclassing it's a totally different instantiation protocol. And if they're value types, it's even more complicated if we want to avoid any kind of global registries like we discussed with shared structs. |
01:55 | <littledan> | Performance and/or security. This is one of the reasons why I indicated non-determinism for string hashing between application restarts is a good thing, actually. |
02:00 | <rbuckton> | The "bad thing" case (non-determinism being bad) seems so narrowly focused that I have a hard time believing it should apply broadly across the language. |
02:04 | <bakkot> | I don't know that class-based vs object-based is a meaningful distinction in JS, but in any case, the main things I want to take away are
|
02:16 | <rbuckton> | The overhead incurred by a CompositeKey just seems like a non-starter to me. Maybe we could find another approach that can overcome these caveats. For example, a Hasher class instead of a global Object.hash() . A given Hasher could just use a monotonically increasing number for each unique object instance it comes across, and maybe is configurable as to how it handles string hashing (e.g., algorithm, randomness, seed values, etc.). Then your equaler looks like { equals(a, b), hash(obj, hasher) } and you then can write new Map([], { equaler, hasher }) . Object hashes could be deterministic without being a global communications channel. String hashing can be deterministic if you want it to be, or not, as best fits your scenario. |
02:33 | <littledan> | yes, I agree that having values which === each other based on their contents is useful; it's unfortunate that we can't go in that direction per implementer feedback, but I think we can still find many related but different improvements to JavaScript. |
02:35 | <littledan> | Class-based vs object-based is more about syntax and conventions. I think these sorts of features are more likely to have broad adoption with convenient syntax, especially with something related to object/array syntax. If we had class syntax, we'd have to think through how new works. But it's also useful to look at other issues in this area which don't relate to those. |
02:36 | <littledan> |
If this means one which is interned so that === works, I agree |
02:37 | <littledan> | The "bad thing" case (non-determinism being bad) seems so narrowly focused that I have a hard time believing it should apply broadly across the language. |
02:49 | <rbuckton> | That's why I mentioned alternatives in the preceding message. I'm willing to consider alternatives that support the scenarios I've discussed. I'd entertain an opaque hash if it were feasible to actually support those scenarios in a performant way. A Composite key cannot satisfy those scenarios as it can never be fast in a custom collection, only in native Map/Set. Maybe I'd be less concerned if a ConcurrentMap were in the MVP for shared structs, but I know that shared structs without concurrent collections is already a hard sell. The problem is that shared structs without concurrent collections is nearly unusable for my use cases without the ability to implement a fast efficient custom collection. To support it in the dev trial I essentially had to tag every shared struct with a monotonically increasing identity to use as the hash, and implement a string hashing algorithm just so I could roll my own ConcurrentMap . That approach has a lot of overhead I'd rather avoid. |
02:53 | <rbuckton> | tl;dr, I don't need hash/equals if I know I'll get ConcurrentMap , but I know that's a long shot at this point. I'd still lament the overhead of a CompositeKey , but my primary use cases would be covered. On the other hand, hash/equals means I'm less concerned about when, if ever, I get ConcurrentMap since I could readily implement it in userland. |
03:04 | <bakkot> | it was not clear to me that this feedback applied to interning at construction time, with a constructor function rather than syntax |
03:05 | <bakkot> | and being actual typeof "object" objects rather than a new kind of primitive, and so on |
03:05 | <bakkot> | https://matrixlogs.bakkot.com/TC39_Delegates/2024-04-09#L294-L290 |
06:06 | <eemeli> | Could a hashcode be a global symbol for which Symbol.keyFor() returned undefined ? Wouldn't that avoid the concerns about non-determinism? |
08:43 | <Ashley Claymore> | Symbols don't help non-determinism. ``` typeof hash("a"); // "symbol" if (hash("a") === hash("b")) { print("foo"); } ``` |
08:43 | <Ashley Claymore> | Does the program print foo? |
08:47 | <Ashley Claymore> | If the spec says that the symbol hash of every value is different and never equal, then we haven't hashed into a smaller space. If the spec says which values have the same hash then this opens up code to collisions attacks. If the spec says it's random which values have the same hash then it's non-deterministic if the program prints foo. is my understanding of the problem statement with the various design constraints put forward from committee. |
09:14 | <eemeli> | Plus, AFAIK equals/hash is how every implementation implements maps natively, it's just not exposed to user code. |
09:18 | <nicolo-ribaudo> | Hashes can have conflicts — in a Map you store a list of entries per each hash. When looking up values in the map, you:
|
09:26 | <Ashley Claymore> | Ashley Claymore: I understood from this earlier assertion by Ron that implementations have already found ways to square the circle with respect to "different and never equal", but maybe I misunderstood? |
09:53 | <eemeli> | Ah, got it. So for a "different and never equal" sort of hash, A related thought I had that could limit the impact of that would be making the hash (or composite key, not really sure how they'd be very different) linked to the lifetime of an object. Then |
12:05 | <Ashley Claymore> | That is essentially the CompositeKey / R&T design. That doesn't help Ron's use case of building high performance custom hash maps. To implement custom hash maps the only truely useful hashing function is one where it is possible for values to collide, otherwise the space hasn't been reduced to something that can fit into a small number of indexable buckets |
12:07 | <Ashley Claymore> | this is why Ron desires a hashing function that returns integers |
12:16 | <Ashley Claymore> | I would like to think that the object based R&T provide lots of value without needing to expose hashing. While also not precluding another proposal for exposing hashing function for the use cases when full control and minimal object allocations are desirable. For use cases when R&T can be adopted as the data model of the application, this would be efficient. Using these values as Map/Set keys would require not extra allocation (outside of the map/set's own storage naturally), and the hashing and equality functions would be 100% native with zero need for userland re-entrancy guards. However Ron is correct that when R&T need to be created as keys because the data model and the keys are not directly compatible then those application will need to allocate more objects to create the keys. This should be more memory efficient than the current solution of flattening values into one long string, but is still an extra allocation that a purely manual hashing+equality interface would avoid. |
12:26 | <littledan> | The overhead incurred by a |
12:28 | <littledan> | it was not clear to me that this feedback applied to interning at construction time, with a constructor function rather than syntax |
12:29 | <littledan> | shu: Could you clarify whether "better" is good enough, when it comes to interning overhead? |
12:30 | <rbuckton> | I want to compare object equality structurally, not by reference. The most efficient way to do so for custom equality in a hash table is to calculate a hashcode for bucketing, and use an equals method against each element in the bucket. A user-defined hash table cannot use object reference identity as it is not a numeric value, instead it needs an identity hash. |
12:32 | <nicolo-ribaudo> | Can you convert an object identity into a number by having a WeakMap<object, number> , and assigning a number to each object? |
12:36 | <littledan> | let's see if we can avoid these weak things where possible... it has a real GC perf cost |
12:37 | <littledan> | rbuckton: If your goal is to have a concurrent map, maybe we should focus on that. That could work both by identity (by default) and converting to a R&T for structural comparison (opt in with a keyBy function). Would that implement what you need? |
12:37 | <rbuckton> | That is what is generally done to work around this currently, but it has drawbacks:
It's also only part of the problem with hashing, the other problem is strings. |
12:47 | <littledan> | a concurrent map would hash strings well, right? |
12:47 | <littledan> |
|
12:48 | <littledan> | I'm not sure what you mean by "this is what is done" -- the concurrent map construct doesn't exist yet |
12:49 | <rbuckton> | rbuckton: If your goal is to have a concurrent map, maybe we should focus on that. That could work both by identity (by default) and converting to a R&T for structural comparison (opt in with a keyBy function). Would that implement what you need? Uri or a Point or a Location as a key, I would have to convert it to a R&T type or composite key first, which is an allocation for every call to get /set /has . This can be 1000s of allocations in a tight loop, and if I don't own Uri or Point or Location I can't just convert those to be R&T types. An { equals, hash } object is a single allocation that is reused for every key.Another of my goals is maturing the language. Custom collection classes can't perform as well as native Map /Set because developers don't have access to the requisite core capabilities necessary to make that happen. I'm concerned that composite keys either become evolutionary dead end for the language if these building blocks become available, or they become a rationale to never make these building blocks available and thus we never have the flexibility to write efficient custom collections. |
12:51 | <rbuckton> | I'm not sure what you mean by "this is what is done" -- the concurrent map construct doesn't exist yet Object.hash() required a significant number of workarounds that I wouldn't want to rely on in a released product. |
12:54 | <littledan> | Yes, I can see how composite keys or R&T has more runtime cost than a hashcode. That is sometimes the compromise that we make in high-level languages. I guess when domain-specific hacks are possible, they can be included as the keyBy. |
12:54 | <littledan> | I was responding to nicolo. Also, a concurrent map does exist. I had to build one for TypeScript as part of experimenting with shared structs. The lack of an |
12:54 | <rbuckton> | And yes, if concurrent collections were part of the MVP for shared structs then I might be less concerned, but I don't see that being likely. |
12:54 | <rbuckton> | Strings are an issue for multiple reasons. |
12:55 | <littledan> | And yes, if concurrent collections were part of the MVP for shared structs then I might be less concerned, but I don't see that being likely. |
12:55 | <rbuckton> |
|
12:56 | <littledan> |
|
13:01 | <rbuckton> | I can understand your concern there but in this chat we've identified some serious concerns with exposing identity hashcodes... maybe it would work better to push on ConcurrentMap as a follow-on for shared structs, rather than these lower-level utilities in other parts of the language. Math.random() and Date.now() (and Temporal.Now , and numerous other sources of randomness). Object.hash() could just as easily be denied or made deterministic to serve that case. Determinism for the sake of Determinism does not serve the web platform. |
13:02 | <littledan> | We already know that we must not expose a deterministic identity hashcode operation. But we also have serious issues around nondeterminism. I'm especially concerned with the interop risks over time. This all is why we're currently taking the middle path of hiding the identity hashcode. |
13:04 | <Ashley Claymore> | userland re-entracy is also a common concern for proposals. If the equals function is written in userland, this puts userland re-entracy right at the heart of the map internal bucket probing loop. I'll leave it to engine implementations to state how much of a concern that is to them. |
13:04 | <littledan> | what do you mean by reentrancy here? |
13:04 | <Ashley Claymore> | leaving C++ |
13:04 | <Ashley Claymore> | back to the application's logic |
13:05 | <Ashley Claymore> | which could re-enter the currently executing function. Invalidating pointers, if this re-entrancy was not taken into account. e.g. the buckets being re-sized while walked |
13:05 | <rbuckton> | We already know that we must not expose a deterministic identity hashcode operation. But we also have serious issues around nondeterminism. I'm especially concerned with the interop risks over time. This all is why we're currently taking the middle path of hiding the identity hashcode. |
13:06 | <littledan> | It's easy to imagine someone depending on string hashcodes having certain properties |
13:07 | <littledan> | for example you could check whether the hashcode has changed as an indicator of whether your JS program has restarted, which feels off |
13:09 | <rbuckton> | I can understand your concern there but in this chat we've identified some serious concerns with exposing identity hashcodes... maybe it would work better to push on ConcurrentMap as a follow-on for shared structs, rather than these lower-level utilities in other parts of the language. Deque , Work stealing queue, ConcurrentMap , and ConcurrentBag /ConcurrentSet . |
13:10 | <littledan> | Getting shared structs through committee is going to be an uphill battle. Concurrent collections are a necessity to make them practically useful in large-scale applications. If the MVP for shared structs suddenly became stage 4 today I still wouldn't be able to use them for TypeScript without having to build my own concurrent |
13:10 | <rbuckton> | for example you could check whether the hashcode has changed as an indicator of whether your JS program has restarted, which feels off Object.hash() |
13:10 | <yulia> | shu: I hear you have concerns re: import defer and it's usability for the web. happy to talk about it |
13:11 | <littledan> | "this is an uphill battle" doesn't necessarily imply that taking the battle somewhere else will make it easier |
13:11 | <rbuckton> | "this is an uphill battle" doesn't necessarily imply that taking the battle somewhere else will make it easier |
13:12 | <rbuckton> | It's easy to imagine someone depending on string hashcodes having certain properties |
13:23 | <rbuckton> | JS performance is a hot button issue in the ecosystem. The "why not rewrite in Rust" crowd is growing louder and louder, especially now that WASM-GC is available, and it's becoming easier and easier for people to move off of JS. I want the JS language to evolve and mature, to continue to be relevant. I don't want to end up with JS just being for hobbyists because the rest of the world has moved on. We mature by adding flexibility, smoothing rough edges, improving performance, improving ergonomics, simplifying common tasks, and adding new capabilities that open the doors for new classes of applications. JS is not a specialized language, it is a general purpose language, and that's its strength. |
13:50 | <ryzokuken> | good morning/evening/night delegates! meeting starting in ~10m. |
13:58 | <shu> | shu: I hear you have concerns re: import defer and it's usability for the web. happy to talk about it |
14:03 | <shu> | shu: Could you clarify whether "better" is good enough, when it comes to interning overhead? |
14:04 | <Duncan MacGregor> | I would volunteer for note taking, but I'm getting pinged on non-TC39 things a lot right now. |
14:05 | <ryzokuken> | I would volunteer for note taking, but I'm getting pinged on non-TC39 things a lot right now. |
14:23 | <Michael Ficarra> | hard agree with USA here |
14:24 | <littledan> | hard agree with USA here |
14:26 | <Michael Ficarra> | stage 1 acknowledges that localisation is important for the web and something we should try to address within TC39; that remains the case, regardless of how long it will take or how hard it might be to provide a solution |
14:30 | <littledan> | stage 1 acknowledges that localisation is important for the web and something we should try to address within TC39; that remains the case, regardless of how long it will take or how hard it might be to provide a solution |
14:30 | <Michael Ficarra> | ... nowhere? |
14:30 | <littledan> | oh oops I misread sorry |
14:31 | <littledan> | somehow turned into hard disagree |
14:39 | <Mathieu Hofman> | How would a concurrent map actually be implemented in the context of wasm-gc? Isn't there the same kind of problems that you have an opaque identity for your objects (and possibly strings) like in JS? I actually still fail to understand what the data model is for your use case, and as such what the solution space might look like. You want structural comparisons for your collection entries, but these values are shared? How can a structural comparison be stable if the data itself can change from under you? Could part of the "solution" be to make R/T sharable and valid values inside shared structs? IMO, the ability to shim new features is highly preferable, but it rarely needs to be efficient / performant, nor does it need to be ergonomic for the shim implementer. I'd rather have a cohesive language that doesn't expose sharp edges. |
14:40 | <Ashley Claymore> | +1 to finding ways to reduce champion burnout. There's a reason why it was 2022 when I last presented R&T :) |
14:41 | <littledan> | it has not reached an impasse |
14:50 | <nicolo-ribaudo> | I was scrolling through Twitter and found another case of somebody not realizing that using requires a binding even if it's not actually needed: https://twitter.com/nullvoxpopuli/status/1777364717805142411 |
14:51 | <nicolo-ribaudo> | Maybe we could allow using LeftHandSideExpression ; |
14:51 | <nicolo-ribaudo> | It would beed a cover grammar, but using fn() is much better than using void = fn() |
14:52 | <eemeli> | I used "impasse", or "stuck" in the issue, to indicate that no actions only within TC39 are sufficient for further progress. The impasse may be resolved by actions outside the committee, i.e. industry feedback and/or adoption. But didn't want to get into an argument about semantics. |
14:52 | <nicolo-ribaudo> | It would beed a cover grammar, but using void = would be a syntax error like let void = and const void = |
14:53 | <Michael Ficarra> | @nicolo-ribaudo https://github.com/tc39/proposal-discard-binding/issues/1#issuecomment-2030365690 |
14:55 | <nicolo-ribaudo> | Thank you |
14:59 | <hax (HE Shi-Jun)> | I really like allow redeclare _ ... |
15:05 | <Ashley Claymore> | https://johnnyreilly.com/typescript-eslint-no-unused-vars |
15:06 | <Ashley Claymore> | ^^ for reference to what RBN said about eslint config |
15:07 | <Duncan MacGregor> | I think the Risks and Assumptions section in the Java JEP for unnamed variables is interesting:
|
15:07 | <dminor> | This just feels risky to me, maybe it's better to keep this in extractors and pattern matching and explicit resource management, even if it would be nice to have it elsewhere. |
15:07 | <ljharb> | fwiw eslint supports an ignore prefix pattern for the reason ron just mentioned |
15:08 | <littledan> | This just feels risky to me, maybe it's better to keep this in extractors and pattern matching and explicit resource management, even if it would be nice to have it elsewhere. |
15:09 | <dminor> | I agree it would be weird |
15:09 | <dminor> | But there's no risk of breaking existing code, and we can have _ there, right? |
15:10 | <shu> | i don't understand how any existing variable name can be web compat |
15:11 | <dminor> | It would be nice to use _ in pattern matching, given precedent in other languages with pattern matching |
15:11 | <rbuckton> | const _ = a, _ = b is illegal everywhere |
15:11 | <littledan> | i don't understand how any existing variable name can be web compat |
15:11 | <nicolo-ribaudo> | i don't understand how any existing variable name can be web compat |
15:12 | <nicolo-ribaudo> | Instead of "redeclaring a variable is an error in strict mode" it becomes "reading a variable that has been redeclared in strict mode is an error" |
15:12 | <ljharb> | but var _ = 1, _ = 2; is not illegal anywhere |
15:12 | <nicolo-ribaudo> | but |
15:12 | <Jack Works> | I don't like _ as discard. What's wrong with void ? Is there any pushback on void ? |
15:13 | <ljharb> | what about function f(_, _) {} , which is legal in sloppy mode? |
15:13 | <shu> | Instead of "redeclaring a variable is an error in strict mode" it becomes "reading a variable that has been redeclared in strict mode is an error" |
15:13 | <eemeli> | To me "void" sounds an awful lot like "undefined". |
15:13 | <ryzokuken> | for C/C++ developers it could be a source of confusion, yes |
15:13 | <ljharb> | and indeed the binding is not defined :-p |
15:14 | <nicolo-ribaudo> | sloppy mode so popular though? |
15:14 | <nicolo-ribaudo> | So what works in any mode would keep working the same |
15:14 | <Ashley Claymore> | I assume other languages could cope with _ easier because they didn't have a popular library that was commonly imported as _ |
15:15 | <shu> | The error cases remain the same, with the difference that the error is moved from declaration position to reference position |
15:15 | <Justin Ridgewell> | @rbuckton Why do we lose assignment patterns with _ ? |
15:15 | <ryzokuken> | it's on us for not doing modules/namespaces well at start I guess |
15:15 | <Justin Ridgewell> | The simple case is just to allow redeclaring _ , no other restrictions. |
15:15 | <nicolo-ribaudo> | i am still confused what the proposed semantics is for sloppy mode |
15:16 | <Ashley Claymore> | it's on us for not doing modules/namespaces well at start I guess People do import _ from "lodash" |
15:16 | <bakkot> | I am totally fine restricting this feature to strict mode |
15:16 | <Justin Ridgewell> | How would that change things? |
15:16 | <Ashley Claymore> | Sure, but popular? |
15:17 | <ryzokuken> | plus the _ name is less necessary with imports |
15:17 | <ryzokuken> | but if all you can do is pollute the global namespace, $ and _ abound. |
15:17 | <Justin Ridgewell> | They should be using import { foo, bar } from ‘lodash’ anyways… |
15:17 | <ljharb> | they should be using import foo from 'lodash.foo' anyways, to avoid the CVE noise :-p |
15:18 | <bakkot> | nothing wrong with namespace imports |
15:18 | <shu> | vars and function params can be redeclared, but if you redeclare a let/const then it throws when you reference the binding |
15:18 | <ljharb> | nothing wrong with namespace imports |
15:18 | <Justin Ridgewell> | nothing wrong with namespace imports |
15:19 | <bakkot> | well don't do that |
15:19 | <ljharb> | ^ the latter is also a problem with barrel files |
15:19 | <bakkot> | but put the blame where the problem is, not on something else |
15:20 | <ljharb> | the-world-if-people-didnt-do-things-they-shouldnt-do.jpg |
15:21 | <danielrosenwasser> | you should see the ~bananas stuff~ similar stuff I've seen in Python |
15:22 | <Ashley Claymore> | yes please! I have done very little Python and am all ears! |
15:22 | <danielrosenwasser> | https://docs.djangoproject.com/en/5.0/topics/i18n/translation/ |
15:23 | <danielrosenwasser> | Okay, it's not really bananas - but they're doing the same thing |
15:23 | <Justin Ridgewell> | okay well, i don't think this proposal meets the bar thus far for complicated binding handling |
15:23 | <shu> | that would be more palatable |
15:24 | <ptomato> | aliasing gettext() to _() is a fairly common pattern for programming languages that have an implementation of gettext |
15:25 | <eemeli> | I'm really interested in extractors effectively providing runtime types: https://github.com/tc39/proposal-extractors/issues/20 |
15:25 | <ptomato> | because the xgettext tool extracts strings inside _() out of source files into message files |
15:25 | <danielrosenwasser> | Justin Ridgewell: you mean bindings to _ work in a last-write-wins manner and so there's no ambiguity error? |
15:25 | <Justin Ridgewell> | Yes |
15:25 | <Justin Ridgewell> | It’s just a regular binding, but it can be redeclared. |
15:26 | <bakkot> | I like rust's thing where a redeclaration introduces a new scope which shadows the previous one |
15:26 | <bakkot> | so closures which capture the old one still work |
15:26 | <bakkot> | at least I assume that's how it works, I haven't actually checked |
15:26 | <bakkot> | now that I'm saying this I have no idea why I believe it |
15:26 | <eemeli> | With extractors, you can have
and be sure of |
15:27 | <Ashley Claymore> | I think that's right |
15:27 | <ljharb> | i think you'd need String(const s) etc to make the binding tho? |
15:27 | <eemeli> | Nope. |
15:27 | <Justin Ridgewell> | so closures which capture the old one still work |
15:29 | <Michael Ficarra> | @eemeli what Symbol.customMatcher implementation are you thinking of for the built-in constructors? |
15:30 | <ljharb> | those are already in the pattern matching proposal, fwiw, with brand checking semantics |
15:30 | <bakkot> | ... brand checking or typeof? |
15:30 | <eemeli> | Something like
|
15:31 | <bakkot> | I am categorically opposed to accepting boxed primitives here or anywhere |
15:31 | <ljharb> | it'd check for the internal slot of a Number instead of instanceof, but otherwise yes |
15:31 | <Justin Ridgewell> | The closure would have to move the value in. I’m not sure it works the way you think. _ in a closure (I’ve never tried before): https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=ab28412522dba608f6964c9a11ce600f |
15:32 | <ljharb> | it doesn't make any sense to reject boxed primitives if people are going to be referencing the constructor |
15:32 | <ljharb> | i don't expect it to come up in practice either way tho, tbf |
15:32 | <bakkot> | sure it does, Number(foo) doesn't produce a boxed primitive |
15:32 | <eemeli> | I don't care about boxed primitives tbh. |
15:32 | <ljharb> | it's a constructor. new Number(foo) does |
15:33 | <bakkot> | if the extractor used new I would agree with you |
15:33 | <bakkot> | but it doesn't |
15:33 | <Michael Ficarra> | const new Number(n) = 5 |
15:33 | <ljharb> | so you want the requirement to be new UserClass as a pattern to match a user class? |
15:33 | <eemeli> | new Number is not a valid ExtractorMemberExpression. |
15:34 | <Michael Ficarra> | @eemeli sorry I forgot the Kappa |
15:34 | <ljharb> | iow, a pattern of Number and a pattern of UserClass should work the same - either both require new or neither does |
15:34 | <Michael Ficarra> | I'll go back to TDZ |
15:34 | <ljharb> | and i don't see any argument for requiring new on non-primitive constructors |
15:34 | <bakkot> | UserClass(foo) should throw or either behave the same as new UserClass(foo) , so it's fine for let UserClass(foo) = bar to be the extractor pattern |
15:35 | <bakkot> | but Number doesn't work like that, so we can't use that as precedent for how Number should work |
15:35 | <ljharb> | not if it's an ES5 class |
15:35 | <ljharb> | and many predicates are technically constructible (because they're not written as arrows or concise methods etc) |
15:36 | <bakkot> | I don't think "this is technically constructible" ought to guide our design of these features |
15:36 | <bakkot> | this applies to user-defined predicates and also to Number |
15:36 | <ljharb> | ok. but we don't actually have a clear thing in JS that divides constructors and functions, so PascalCase is the universal convention that JS devs use for that |
15:37 | <ljharb> | so, designing for that universal expectation, Number would (and does) look like a constructor just like UserClass or Set |
15:37 | <bakkot> | users should not regard Number as being constructible and we should not design the feature to suggest that it is |
15:37 | <bakkot> | it is true that Number is technically constructible but I don't care about this fact, and we should not promote it to people's attention, nor make features which support it |
15:38 | <ljharb> | i do not believe that it accepting boxed primitives - and unboxing them, ftr - is going to encourage usage of it. i strongly believe the opposite |
15:38 | <Justin Ridgewell> | Actually you can’t reference |
15:38 | <bakkot> | i do not believe that it accepting boxed primitives - and unboxing them, ftr - is going to encourage usage of it. i strongly believe the opposite |
15:38 | <ljharb> | certainly no new sites of producing a boxed primitive should ever be added |
15:39 | <ljharb> | if you want to use pattern matching, with the current semantics, even if you do the very unlikely thing of making a boxed primitive, you will as rapidly as possible unbox it. just like how sparse arrays are terribad, and all new stuff just papers over that by making them dense, and we do NOT throw when you have a sparse array, we shouldn't throw or fail when you have a boxed primitive. we should just paper over it and get you into "normal primitives" as fast as possible. |
15:39 | <littledan> | I thought V8 optimized array destructuring |
15:40 | <bakkot> | if you want to use pattern matching, with the current semantics, even if you do the very unlikely thing of making a boxed primitive, you will as rapidly as possible unbox it. |
15:40 | <hax (HE Shi-Jun)> | I thought V8 optimized array destructuring |
15:40 | <bakkot> | but here you're suggesting we add a special case to support the thing that people should not encounter |
15:40 | <bakkot> | I want to not do that |
15:40 | <littledan> | we have to avoid saying "stage n concern" because of the ambiguity between this being a thing to resolve before reaching stage n, or only once stage n is reached |
15:41 | <bakkot> | if user is passing around a boxed primitive, they should expect to get an error, not for the language to accommodate them |
15:41 | <ljharb> | p sure the motivation was more "because that hurts people to no benefit" as opposed to "we don't want a special case in the spec/impl" |
15:43 | <bakkot> | I do not think that's right, but in any case, boxed primitives are more different from primitives than holey arrays are from dense arrays |
15:45 | <Justin Ridgewell> | I thought V8 optimized array destructuring |
15:45 | <ljharb> | boxed primitives exist. if they are used, then providing encouragement and ergonomics for quickly getting them unboxed is good. if they are not used, then accepting them provides no such encouragement. |
15:45 | <Ashley Claymore> | I remember they published an article about it several years ago |
15:46 | <bakkot> | counterpoint: if they are used, discouraging their use is good |
15:46 | <bakkot> | no code should accept them |
15:46 | <bakkot> | (unless it accepts other objects, of course) |
15:50 | <bakkot> | I am not looking forward to parsing (bar(baz)) => 0 |
15:50 | <littledan> | extractors do a good job addressing one of the points that Yulia raised a while ago, about the availability of parallel constructs inside and outside of pattern matching. |
15:50 | <bakkot> | but I guess it's not that much worse than ({ bar }) => 0 |
15:51 | <bakkot> | and async(bar(baz)) => 0 of course |
15:52 | <littledan> | yeah by 2017 we already got up to a certain bar of annoying-to-parse, and my feeling was that extractors didn't really add anything that was so much more bad... but non-backtracking parsers will have some more complexity in their error tracking with this |
15:53 | <bakkot> | it is not that much worse, it's just adding more complexity to what is already the single most complex-to-parse construct in the language |
15:54 | <Michael Ficarra> | the extractor has to be an identifier or a member access on an extractor, right? |
15:55 | <bakkot> | yeah |
15:55 | <eemeli> | It can also be e.g. super . |
15:55 | <bakkot> | https://tc39.es/proposal-extractors/#prod-ExtractorMemberExpression |
15:55 | <eemeli> | Ah, not super , just super.foo or this . |
15:58 | <shu> | i mean in general i agree with george but i got bad news for him on the understandability of JS wrt user code today |
16:01 | <Ashley Claymore> | That's because it's all minified on the web |
16:04 | <rbuckton> | @rbuckton Why do we lose assignment patterns with [_, _] = ar today and it has a meaning. |
16:04 | <keith_miller> | Do other engines think that throwing for each failed match seems like it would be way too slow? |
16:04 | <littledan> | Do other engines think that throwing for each failed match seems like it would be way too slow? |
16:04 | <keith_miller> | Or did I just misunderstand the proposal? |
16:04 | <littledan> | also engines don't have to actually do the throw |
16:05 | <Justin Ridgewell> | Because you can already write |
16:05 | <keith_miller> | You'd need to inline every pattern, IIUC, which doesn't seem plausible in all places (it could be virtual, it could be huge, etc) |
16:05 | <rbuckton> | Do other engines think that throwing for each failed match seems like it would be way too slow? default: clause. |
16:06 | <Justin Ridgewell> | We just need the ability to redeclare. |
16:07 | <keith_miller> | I agree it's more of a discussion for pattern matching I don't want to get to a place where we have throwing here and have to have a totally different mechanism for pattern matching |
16:07 | <ljharb> | Do other engines think that throwing for each failed match seems like it would be way too slow? match not for a failed clause, to be clear - the intention for a match construct is that it always matches something |
16:07 | <Ashley Claymore> | We just need the ability to redeclare. |
16:07 | <rbuckton> | But that’s behaving exactly the way discard bindings should. _ isn't already declared, [_, _] = ar will either throw in strict mode, or introduce a global in non-strict mode. I don't think we can change that. If you want to use _ as a discard in assignments, you do that by declaring a var _ somewhere in the same scope. |
16:08 | <rbuckton> | Or are you talking about binding patterns? |
16:08 | <gkz> | i mean in general i agree with george but i got bad news for him on the understandability of JS wrt user code today |
16:09 | <rbuckton> | I agree it's more of a discussion for pattern matching I don't want to get to a place where we have throwing here and have to have a totally different mechanism for pattern matching const {} = null throws or const [] = 1 throws, so must this. |
16:09 | <rbuckton> | match must throw if it has no default. Since match is syntactic, if a default: clause doesn't exist you just synthetically introduce one that throws. |
16:10 | <shu> | If we're creating a brand new feature, why not aim higher? Especially if the increased power/flexibility is not required for the majority of use-cases |
16:10 | <shu> | i'm saying that doesn't meaningfully move the needle for the general problem of understanding user code in JS |
16:11 | <littledan> | shu: dminor : What was the performance concern? |
16:11 | <shu> | the new one? |
16:12 | <shu> | my new one is cover grammars incur cost, as you well know |
16:12 | <shu> | and we don't like it |
16:12 | <shu> | nothing deeper than that |
16:13 | <Justin Ridgewell> | Redeclare what? if _ was already a binding in scope. Why can’t we make sloppy mode continue to function as is, and just not create the binding in strict? |
16:13 | <rbuckton> | Regarding the
|
16:15 | <rbuckton> | Oh, I was assuming That would definitely be a violation of ljharb 's concerns. I think we can only use
Then you're breaking code non-locally. |
16:16 | <Justin Ridgewell> |
|
16:17 | <rbuckton> | In a 10,000 line file, it's easy to lose sight of the imports at the top of the file. |
16:18 | <ljharb> | oof, if you have a 10k line file you have bigger problems than a binding no longer working |
16:19 | <gkz> |
|
16:19 | <rbuckton> | Yeah, yeah. I'd love to refactor checker.ts to something more manageable, but that's still a ways out. |
16:20 | <rbuckton> | I don't disagree that there are use-cases that would be addressed with this, just that the power required to support them (when these are the minority of use-cases, and other languages e.g. OCaml do fine without supporting this), allows completely arbitrary behavior which means that it will be more difficult to understand and analyze the main use-case, which is matching against some datatype and extracting values from it. __match_args__ approach? I've discussed that with the pattern matching champions and there was fairly strong opposition. |
16:21 | <eemeli> | rbuckton: Are there any dynamically typed languages that support extractors? |
16:23 | <rbuckton> | OCaml is a statically typed language, as is Rust. They have type systems to do the work for them. JS does not. It often depends on runtime evaluation of code to do similar things. Extractors is the way it is because it isn't statically typed. |
16:23 | <eemeli> | Racket, apparently? |
16:24 | <gkz> | Do you mean something more like Python's customMatcher -like function which can decide which values to return for extraction, but the check of "is this of this datatype" (be it instanceof or whatever) would always be run, separating these two concerns |
16:24 | <rbuckton> | rbuckton: Are there any dynamically typed languages that support extractors? __match_args__ , not full extractors. They'd considered them but determined they didn't have use cases. JS has private state, which Python does not, so a __match_args__ approach is not sufficient. Other Pattern Matching champions have opinions on this as well. |
16:24 | <gkz> | Dart recently added a pattern matching feature, and seems to work in this way: https://dart.dev/language/pattern-types#object |
16:27 | <rbuckton> | I'm not familiar with that feature, but to explain what I mean in loose terms, you could still have a In Python, you can do
where It checks if |
16:29 | <rbuckton> | There is no opportunity to run user-defined code as part of that validation. It can also only read public properties, but that's fine for Python since it does not have privacy. A Point[Symbol.matchArgs] = ["#x", "#y"] in JS wouldn't be able to read a private #x in JS. |
16:32 | <littledan> | Is Mark Miller here this afternoon? |
16:32 | <littledan> | my presentation is largely about refuting a past argument that he made |
16:32 | <eemeli> | Random thought: Would @@customMatcher as a generator be potentially more performant than returning an array or iterator? |
16:35 | <littledan> | my presentation is largely about refuting a past argument that he made |
16:36 | <littledan> | Random thought: Would |
16:36 | <littledan> | you can intercept __getattr__ though! |
16:39 | <eemeli> | littledan: I mean having
instead of
|
16:39 | <mgaudet> | (side note: Can someone update the topic to point to this meeting rather than feb) |
16:41 | <gkz> | There is no opportunity to run user-defined code as part of that validation. It can also only read public properties, but that's fine for Python since it does not have privacy. A I like this aspect of the feature: But my focus is on that, if you wanted to still have a |
16:43 | <rbuckton> | That does not satisfy my goals and motivations. Pattern matching could do that with x is Point and { let x, let y } . Executing user-defined code is the whole point of the feature. |
16:45 | <Mathieu Hofman> | Is Mark Miller here this afternoon? |
16:45 | <gkz> | To clarify, with that example it would be x is Point(let x, let y) - we check if x instanceof Point , and then get the arguments you want extracted |
16:45 | <gkz> | If would just not allow things like using extractors on strings to extract parts of those strings |
16:46 | <gkz> | I guess this would be similar to how both Python and Dart use these features |
16:47 | <rbuckton> | I want to be able to do that with this feature. Restricting it to data types is a manufactured limitation, not a natural limitation of the syntax. |
16:47 | <Anthony Bullard> | I guess this would be similar to how both Python and Dart use these features |
16:47 | <rbuckton> | Also, instanceof is broken. Pattern matching is not using it. |
16:48 | <gkz> | Sure, whatever actual check is more appropriate to use, I think the general idea I'm talking about is clear though |
16:49 | <iain> | Random thought: Would |
16:49 | <rbuckton> | I understand the semantics you are describing. I just see no reason to introduce an artificial limitation. |
16:50 | <rbuckton> | If a custom matcher is an iterator, then it is irrefutable. It always matches. |
16:51 | <eemeli> | iain: Presumably with the exception of only returning one value, and handling its destruction as a separate concern. |
16:51 | <rbuckton> | The reason I pursued this feature in the first place was to support user-defined validation and transformation. That it works for datatypes is a happy benefit. |
16:53 | <iain> | eemeli: Yes, returning a single value would be significantly more performant |
16:53 | <rbuckton> | Also, you can destructure a string, so restricting extractors to datatypes breaks that. const { length } = "abc" and const [a, b, c]="abc" are perfectly legal. I don't want extractors to be less capable than {} and [] . |
16:54 | <nicolo-ribaudo> | Webex is again refusing to pick up my audio, hopefully I can fix it on time |
16:54 | <eemeli> | eemeli: Yes, returning a single value would be significantly more performant let Point([x, y]) = ... to get the same effect, if you need more than one value out of the extractor. |
16:55 | <gkz> | I understand the semantics you are describing. I just see no reason to introduce an artificial limitation. |
16:55 | <rbuckton> | Besides, writing an extractor to handle a non-datatype input is more complex than the default implementation for a data type. In the pattern matching proposal, class C {} will have a default [Symbol.customMatcher] that performs a brand check and returns the subject. You get it for free unless you want to do the more complex things. |
16:57 | <rbuckton> | And ideally, ADT enums would have a default implementation that matched their definition, i.e.:
You would have to manually write a |
16:58 | <rbuckton> | It seems like languages like Dart, Python, OCaml, Rust all have this limitation. And the reason is what I described before, there is a trade-off between the flexibility this feature provides, and making code easy to understand (for both humans and tooling) unapply , C# has Deconstruct . |
16:59 | <gkz> | Yeah, given that there are trade-offs and different languages have made different design choices regarding this trade-off, it seems worth discussing at least! |
17:00 | <littledan> | Slides for my Array.isTemplateObject presentation: https://docs.google.com/presentation/d/1LTlzpboYwKxRwigATcFYEh06CIbZvOvmFdPzkNn7vJI/edit#slide=id.g2cae2397581_0_66 |
17:01 | <rbuckton> | Yeah, given that there are trade-offs and different languages have made different design choices regarding this trade-off, it seems worth discussing at least! |
17:02 | <gkz> | Thanks, will join now! |
17:08 | <eemeli> | Besides, writing an extractor to handle a non-datatype input is more complex than the default implementation for a data type. In the pattern matching proposal, |
17:11 | <rbuckton> | The exact semantics need to match between destructuring and pattern matching, so yes, most likely. Currently it's being managed as a cross-cutting concern between the two proposals. If extractors in destructuring gets Stage 2, but pattern matching is held back, then we would likely need to move over the default semantics. |
17:18 | <ljharb> | i don't think stage 2 is the time to separate them, it'd be if extractors is ready for 2.7, and if pattern matching isn't stage 2 and also is content with the design constraints that advancing extractors would cause |
17:30 | <ljharb> | ok so why is this stage 3 instead of a normative PR? the discussion was hard to follow |
17:31 | <Michael Ficarra> | it should return a spec enum or a string |
17:31 | <littledan> | ok so why is this stage 3 instead of a normative PR? the discussion was hard to follow |
17:31 | <littledan> | we can still reconsider |
17:31 | <ljharb> | just more ceremony than we probably need |
17:31 | <littledan> | IMO it'd be valid either way |
17:33 | <ptomato> | +1 would be good to document where the dividing line is. I agree with Jordan in that I would've preferred this to be a needs-consensus PR |
17:33 | <littledan> | Nicolo's answer seemed good: the stage process let us develop this proposal and build consensus incrementally |
17:34 | <ljharb> | was this one even a proposal before today? |
17:34 | <nicolo-ribaudo> | was this one even a proposal before today? |
17:34 | <ljharb> | aha, thanks |
17:35 | <ljharb> | i'm not sure being stage 1 for many years, and suddenly getting revived and jumping straight to stage 3, justifies it being a proposal vs a PR, but it really doesn't matter much :-) |
17:36 | <nicolo-ribaudo> | To be honest I spent the last two weeks swinging between "stage 3" and "normative PR", but given that we are changing behavior of eval() (and dropping the guarantee that all objects are returned as-is), I preferred being more conservative |
17:36 | <nicolo-ribaudo> | Implementations are in progress, so this will be ready for stage 4 soon |
17:47 | <Michael Ficarra> | 🤔 could you data URI an iframe and then pull the "literal" template out of it? |
17:48 | <Michael Ficarra> | @bakkot ^ |
17:48 | <bakkot> | no, those are cross-origin |
17:48 | <Michael Ficarra> | okay good |
17:49 | <Michael Ficarra> | I can never remember how data URIs and file/loopback URIs can interact with web pages |
17:49 | <bakkot> | data URIs are opaque (=unique) origins in all contexts I am pretty sure |
17:50 | <Michael Ficarra> | that's good, and I'm sure I will forget before the next time I need that piece of information |
18:00 | <bakkot> | I have to drop for a bit but I'm happy with this proposal with either cross-realm or same-realm semantics |
18:09 | <Michael Ficarra> | is this only useful for bundlers? if so, can't they start using a non-standard directive? |
18:13 | <littledan> | I kinda share danielrosenwasser 's composability concern |
18:13 | <hax (HE Shi-Jun)> | nicolo-ribaudo: I believe "adding TLA currently considered to be a semver-major change" should be true... |
18:14 | <shu> | is this only useful for bundlers? if so, can't they start using a non-standard directive? |
18:14 | <ljharb> | adding TLA is always a breaking change, with or without this proposal |
18:15 | <shu> | if other people share daniel r's view that this is a dev-time thing, that seems to segue naturally into tools doing better here |
18:15 | <hax (HE Shi-Jun)> | is this only useful for bundlers? if so, can't they start using a non-standard directive? |
18:15 | <Michael Ficarra> | feel free to take it from me @shu |
18:15 | <Michael Ficarra> | I do feel like it's a dev time thing though |
18:16 | <keith_miller> | Yeah feels like a dev time issue |
18:16 | <ljharb> | imo we shouldn't have allowed static import of a module using TLA in the first place |
18:17 | <littledan> | Does this proposal have a spec? |
18:17 | <Michael Ficarra> | I find the "you'll get an error anyway" argument compelling |
18:17 | <hax (HE Shi-Jun)> | I kinda share danielrosenwasser 's composability concern |
18:19 | <hax (HE Shi-Jun)> | To be honest, I really feel this proposal is just a patch for TLA footguns... |
18:19 | <littledan> | re semver: What if we standardized semver in Ecma? Darcy Clarke is interested in this, with a better specification than exists currently (including Node semver ecosystem reality). We could do it in a new TC in Ecma, or maybe a TC39 TG. Who's in? Eventually maybe it'd make sense for JavaScript to have a small built-in library for working with semver values, once we have the core defined (this is an extremely popular npm package). |
18:20 | <Ashley Claymore> | I wonder if the service worker API could be updated to not have the late-listener issue |
18:20 | <nicolo-ribaudo> | re semver: What if we standardized semver in Ecma? Darcy Clarke is interested in this, with a better specification than exists currently (including Node semver ecosystem reality). We could do it in a new TC in Ecma, or maybe a TC39 TG. Who's in? Eventually maybe it'd make sense for JavaScript to have a small built-in library for working with semver values, once we have the core defined (this is an extremely popular npm package). |
18:22 | <hax (HE Shi-Jun)> | imo we shouldn't have allowed static import of a module using TLA in the first place |
18:25 | <ryzokuken> | re semver: What if we standardized semver in Ecma? Darcy Clarke is interested in this, with a better specification than exists currently (including Node semver ecosystem reality). We could do it in a new TC in Ecma, or maybe a TC39 TG. Who's in? Eventually maybe it'd make sense for JavaScript to have a small built-in library for working with semver values, once we have the core defined (this is an extremely popular npm package). |
18:26 | <dminor> | Would this be useful in conjunction with deferred imports to catch the switch to eager loading? |
18:26 | <ljharb> | there's two open PRs on the semver spec that, once landed, would make semver v3 actually match npm (neither v1 nor v2 is what npm does, and what npm does is what matters) - and that's what i'd hope lands in the language |
18:27 | <ljharb> | (i'm already working with semver folks to try to get one of those landed) |
18:27 | <nicolo-ribaudo> | Maybe we could have it only for deferred imports rather than as a general language feature
|
18:29 | <Ashley Claymore> | Can this be tested like:
|
18:29 | <nicolo-ribaudo> |
|
18:29 | <nicolo-ribaudo> | You would need to inline ./end-test in your module |
18:30 | <Ashley Claymore> | ah right |
18:30 | <Ashley Claymore> | wrap them up |
18:30 | <Ashley Claymore> | so like:
|
18:30 | <nicolo-ribaudo> | well, again ./end-test.js doesn't wait for ./app.js 😛 |
18:31 | <littledan> | +100 to Jordan, [Symbol.toStringTag] was just a mistake |
18:31 | <Ashley Claymore> | how about
|
18:32 | <Michael Ficarra> | tbf brand checking is icky and I don't like it |
18:32 | <nicolo-ribaudo> |
|
18:35 | <Ashley Claymore> | Yeah this seems to work in Node
|
18:35 | <Ashley Claymore> | If I add a TLA to "app.js" the assert fails |
18:37 | <Jack Works> |
|
18:38 | <hax (HE Shi-Jun)> | Is Error.isError(new Proxy(new Error, {})) returns true? |
18:39 | <ljharb> | in the 9 year old spec, yes. it's fine if it returns false tho |
18:39 | <Ashley Claymore> | this is like implementing lodash.throttle but you also need to be a ES module specialist |
18:41 | <ljharb> | eemeli: yes, and we would regardless of this proposal |
18:44 | <ljharb> | shu: i checked and the HTML spec already requires doing that, i think? |
18:44 | <shu> | no it doesn't? |
18:45 | <ljharb> | https://html.spec.whatwg.org/#structureddeserialize, step 21 |
18:45 | <shu> | where do you read that it does |
18:45 | <shu> | wait what are you responding to? |
18:45 | <eemeli> | So if extractors or pattern matching would provide for an Error[Symbol.customMatcher] "regardless", does it make sense to advance a separate proposal on this? |
18:45 | <ljharb> | "be a real subclass" is the same as "has the expected slots and [[Prototype]]" |
18:45 | <ptomato> | { isError() { return true; } } yup that's a native Error |
18:46 | <shu> | "be a real subclass" is the same as "has the expected slots and [[Prototype]]" |
18:46 | <ljharb> | So if extractors or pattern matching would provide for an |
18:46 | <shu> | those steps are deserializing actual JS errors, not subclassed |
18:46 | <ljharb> | oh i don't care about domexceptions |
18:46 | <shu> | i do |
18:46 | <Jack Works> | Maybe this could be an npm library? |
18:46 | <littledan> | So if extractors or pattern matching would provide for an |
18:46 | <ljharb> | i mean that i'm 100% fine with domexceptions passing this predicate, or not. what would you prefer? |
18:47 | <littledan> | yes, that's an excuse we can use to stash the check there -- Jordan has generally been requesting that proposal authors find a way to make such an API (though it has to serve some sort of dual use case today, due to this set of arguments we're having) [Symbol.customMatcher] should make this easier |
18:47 | <shu> | i mean that i'm 100% fine with domexceptions passing this predicate, or not. what would you prefer? |
18:47 | <Jack Works> | i do |
18:48 | <eemeli> | yes, that's an excuse we can use to stash the check there -- Jordan has generally been requesting that proposal authors find a way to make such an API (though it has to serve some sort of dual use case today, due to this set of arguments we're having) |
18:48 | <shu> | yes, we can do HostIsErrorLike. but look, like, 90% of the time, when i ask a question, i'm not also implicitly asking "how do we do this mechanically". most things are possible to do! i'm asking if it's a good idea |
18:49 | <Jack Works> | then no, DOMException does not inherit Error and missing some property Error have |
18:50 | <Michael Ficarra> | @littledan regarding "What policies has TC39 adopted?" do you think we need to cover this during Jordan's limited time? |
18:50 | <bakkot> | if no code can distinguish between "a real error" and "not a real error", why would you ever need to know the answer to that question when debugging? |
18:50 | <bakkot> | "I need this for debugging" is confusing to me |
18:50 | <shu> | i am also confused by this point |
18:50 | <Jack Works> | then no, DOMException does not inherit Error and missing some property Error have |
18:52 | <Jack Works> | then yes, DOMException is a normal sense of "Error". but by that mean, if I made my own Exception, is it a "Error" |
18:54 | <Jack Works> | but why we should care this... I'll just log/report anything thrown to me, without categorize them |
18:57 | <littledan> | @littledan regarding "What policies has TC39 adopted?" do you think we need to cover this during Jordan's limited time? |
18:57 | <littledan> | but it's kinda funny that we have these dualing policies asserted by Jordan and Mark, and these are not written down and just enforced personally by them... we have to get out of this situation by documenting shared policies that we agree on |
18:58 | <Michael Ficarra> | hopefully after this meeting we will have a place to put them: https://github.com/tc39/how-we-work/pull/136 |
18:59 | <Michael Ficarra> | everybody just say yes to everything Kevin suggests and then we will have a normative-conventions.md on Friday |
18:59 | <hax (HE Shi-Jun)> | yeah, DOMException is strange, domException instanceof Error is true, but they do not have all properties of Error (eg. no stack?) |
18:59 | <Jack Works> | yeah, DOMException is strange, domException instanceof Error is true, but they do not have all properties of Error (eg. no stack?) |
19:00 | <bakkot> | (new DOMException).stack // undefined |
19:02 | <littledan> | IIRC it differs between browsers whether you get the stack when allocating the exception or when it's thrown |
19:05 | <eemeli> | I'm getting a stack from that in Firefox and Node.js. |
19:06 | <rbuckton> | Chakra did not set .stack during construction, but during throw . Setting .stack during construction was a V8 contrivance, I think, that was later adopted by some other implementations. |
19:07 | <rbuckton> | So try { throw new DOMException(); } catch (e) { e.stack; } would still show a stack in Chakra |
19:08 | <Jack Works> | on firefox |
19:17 | <bakkot> | yeah looks like this is browser-specific |
19:17 | <bakkot> | fun! |