00:26 | <devsnek> | :w |
00:26 | <devsnek> | oops |
18:40 | <bradleymeck> | devsnek: new variable declarator kind? |
18:40 | <devsnek> | bradleymeck: hmm? |
18:41 | <bradleymeck> | def {x,y} = pt; pt.translate(1,1); def {x,y} = pt; |
18:41 | <bradleymeck> | though questions of shadowing get confusing due to JS having hoisting |
18:42 | <ljharb> | is that just a re-declareable var? |
18:42 | <ljharb> | *let, sorry |
18:42 | <devsnek> | yes |
18:42 | <devsnek> | that's what i originally suggested |
18:42 | <devsnek> | i feel like there's a stage 1 proposal here |
18:45 | <shu> | agree with bradleymeck on hoisting |
18:45 | <shu> | seems very confusing given hoisting. which one gets closed over? |
18:45 | <bradleymeck> | shu: i mean, thats the only real complaint left with the repl goal |
18:46 | <bradleymeck> | in other languages it is via source location, which honestly would be fine, but diff problem than repl has |
18:48 | <devsnek> | the rule i'm imagining is |
18:48 | <devsnek> | if the code would cause a redeclaration error |
18:49 | <devsnek> | make it valid |
18:49 | <devsnek> | leave everything else untouched |
18:49 | <bradleymeck> | so they point to the same storage? |
18:49 | <bradleymeck> | thats what v8 did for making their repl design doc |
18:50 | <devsnek> | i think either they're the same variable or you invalidate the old ones |
18:50 | <devsnek> | invalidating seems needlessly annoying |
18:50 | <ljharb> | `sudo let x` |
18:51 | <rkirsling> | ljharb: 👏 |
18:52 | <ljharb> | could also reassign consts that way |
18:53 | <devsnek> | `sudo eval = undefined` |
18:53 | <bradleymeck> | proposal: make const less constant |
18:53 | <bradleymeck> | free me of linter errors! |
18:53 | <devsnek> | proposal: `let const` |
18:53 | <devsnek> | also `const mut` |
18:54 | <bradleymeck> | `let let let = 1` gasp a new tier |
18:54 | <shu> | why is redeclaring important in js exactly? |
18:54 | <bradleymeck> | i think it is just convenient in other langs |
18:54 | <bradleymeck> | that was the point |
18:54 | <shu> | do these other langs have types and benefit from being able to shadow a same-named declaration with a differently typed one? |
18:54 | <rkirsling> | I think this is just a repl impl issue |
18:55 | <shu> | ah, for repls that makes sense |
18:55 | <rkirsling> | redecl in a static file is bad news |
18:55 | <rkirsling> | (I'm not representing the discussion here though, that's just my view) |
18:56 | <devsnek> | shu: i just hate putting destructuring reassignments in parens |
18:56 | <devsnek> | ts users could benefit from the type thing you mentioned though |
18:56 | <devsnek> | rust allows that too and its very nice |
18:56 | <rkirsling> | ohhh that was your motivation |
18:56 | <shu> | the destructuring thing doesn't seem that compelling to me |
18:56 | <shu> | the TS one i have to think about |
18:57 | <devsnek> | yeah this is like |
18:57 | <devsnek> | a nit of a feature |
18:57 | <rkirsling> | I'm kind of unclear on why type-shadowing wouldn't be dangerous though |
18:57 | <devsnek> | which is why i didn't think it warranted new syntax |
18:57 | <devsnek> | it might be dangerous in ts due to the laxness of its type system |
18:58 | <devsnek> | or maybe taken in stride |
18:58 | <shu> | rkirsling: the *only* case where it's kind of legit, and probably the one devsnek ran into in Torque, V8's DSL, is where you cast an argument |
18:58 | <devsnek> | ah yeah that's the same as rust |
18:58 | <rkirsling> | oh from something void*-esque to what it really is? |
18:58 | <shu> | e.g., in pseudo-ish code `function f(xArg: Any) { let x = Cast<T>(xArg); }` |
18:58 | <shu> | yes exactly |
18:58 | <rkirsling> | yeah |
18:58 | <shu> | i don't know of other places where shadowing with different type is a good idea |
18:58 | <devsnek> | except you can do `x: Any` and `let x = Cast<T>(x)` |
18:59 | <shu> | that is far from sufficient motivation for allowing redecls in general imo |
18:59 | <devsnek> | yeah its not like the most revolutionary deature |
18:59 | <rkirsling> | that's kind of surprising even without the redecl though |
18:59 | <devsnek> | feature* |
18:59 | <rkirsling> | that `{ let x = ... x ...;} ` is allowed |
18:59 | <shu> | yeah actually with JS's parameter scope rules that isn't a redeclaration |
19:00 | <devsnek> | its not valid due to tdz though |
19:00 | <devsnek> | actually in strict mode its also invalid to declare the name |
19:01 | <devsnek> | or maybe all modes |
19:01 | <devsnek> | yeah all modes |
19:01 | <rkirsling> | at top-level function scope sure, but you could block it |
19:01 | <rkirsling> | can't get around the TDZ though |
19:01 | <devsnek> | anyway yeah this is one of those features that's too small to feel justified |
19:02 | <rkirsling> | yup |
19:02 | <devsnek> | we could add it in variables v3 |
19:02 | <rkirsling> | lol |
19:02 | <devsnek> | (`val` and `val mut`, eta 2030) |
19:02 | <rkirsling> | if this wasn't about redecl and was simply about destructuring assignment to existing bindings then maybe there's something there |
19:03 | <rkirsling> | but only if it didn't add to the syntax budget |
19:03 | <rkirsling> | which seems unlikely to achieve |
19:03 | <devsnek> | right if you want to separate those two item |
19:03 | <devsnek> | items |
19:11 | <Bakkot> | devsnek if you don't like parens for destructuring you can use `0,` instead |
19:11 | <Bakkot> | `let x; 0,{ x } = { x: 1 }; |
19:11 | <devsnek> | that's almost worse |
19:11 | <Bakkot> | it's better 'cause it's local! |
19:12 | <devsnek> | though it makes me wonder |
19:12 | <devsnek> | `void`? |
19:12 | <Bakkot> | i.e. you don't have to put parens at the end |
19:12 | <devsnek> | omg are you kidding me |
19:12 | <Bakkot> | I am not entirely serious, no |
19:12 | <devsnek> | that really binds as `(void { a }) = 1`? |
19:12 | <Bakkot> | yeah |
19:12 | <devsnek> | sad times |
19:12 | <Bakkot> | assignment expressions are very low precedence |
19:12 | <rkirsling> | oh god the comma |
19:13 | <rkirsling> | what a truly horrific invention |
19:14 | <Bakkot> | you know I was pretty much joking but looking at it it's kind of growing on me |
19:14 | <devsnek> | that "horrific invention" is the basis of minifiers, have some RESPECT |
19:14 | <rkirsling> | lolol |
19:15 | <devsnek> | ok new proposal idea |
19:16 | <devsnek> | somehow allow `void AssignmentExpression` |
19:18 | <Bakkot> | you could allow it in statement position easily enough, I think |
19:19 | <Bakkot> | tricker in expression position |
19:19 | <Bakkot> | or, well, it might be easy I'm just less sure if the obvious thing would work |
19:19 | <Bakkot> | and can more readily convince myself it works in statement position |
19:19 | <rkirsling> | Bakkot: lol I shared that with a colleague and he was like "if only it worked for `() => 0,{ bar: 42 }`" |
19:19 | <Bakkot> | rkirsling ha |
19:20 | <Bakkot> | that one I am happy to write the parens |
19:20 | <Bakkot> | and also happy that comma doesn't work |
19:20 | <rkirsling> | agreed, just made me laugh |
19:21 | <devsnek> | that comma works perfectly don't @ me |
20:47 | <devsnek> | shu: are you worried about people setting really high maximum values on resizable buffers in order to get around the resize limit |
20:48 | <devsnek> | s/resize limit/maximum size limit/ |
20:49 | <leobalter> | This is not slack but I basically need some @channel for the broad message: |
20:49 | <devsnek> | i feel like the primary use case for resizing is that you don't know what the maximum will be |
20:49 | <leobalter> | if you consume Test262, please read this: https://github.com/tc39/test262/issues/2699 |
20:49 | <shu> | devsnek: not really, why? |
20:50 | <devsnek> | like i said above |
20:50 | <devsnek> | i don't think wasm requires a maximum limit either |
20:50 | <shu> | it does for shared |
20:51 | <shu> | but that doesn't answer why i might be worried about people setting really high maximum values? |
20:51 | <devsnek> | cuz that's how you would get around the problem |
20:52 | <devsnek> | and browsers might not want people falling into that pattern |
20:52 | <devsnek> | idk |
20:52 | <shu> | i don't understand the full concern, that because applications do not know ahead of time what their max buffer size is, they will reserve something huge? |
20:53 | <shu> | and that reserving something huge is bad because... they cause their own application to run out of address space? |
20:53 | <devsnek> | right, that seems like a bad pattern |
20:53 | <shu> | that seems no worse than if someone wants to do that today, they would either commit to a huge buffer today |
20:53 | <shu> | or they keep making new buffers and copying |
20:53 | <devsnek> | well the point of resizing is that you don't commit to huge |
20:53 | <devsnek> | i don't get this |
20:54 | <shu> | you commit to the address range, but you do not commit to the actual memory |
20:54 | <shu> | what do you not get? did you read the motivation? |
20:54 | <devsnek> | yeah |
20:54 | <shu> | is there a part of the motivation that you don't understand, or you disagree with the motivation? |
20:54 | <devsnek> | ok so for example |
20:55 | <devsnek> | i have this package called earl |
20:55 | <devsnek> | that converts js values into erlang term format buffers |
20:55 | <devsnek> | ideally you start smallish and only grow if needed |
20:55 | <devsnek> | the main performance hit rn is growing, i have to allocate a new buffer and copy everything over |
20:56 | <devsnek> | resizing sounds great |
20:56 | <shu> | for that use case, is it important that you keep the identify of the ArrayBuffer the same? |
20:56 | <devsnek> | no |
20:56 | <shu> | then you should be using transfer() which i am reviving as part of this proposal |
20:56 | <shu> | Resizable buffers are for in-place grwoth |
20:56 | <shu> | if your issue is you need zero-copy growth, realloc semantics are sufficient, and that should be used instead |
20:57 | <devsnek> | so realloc is possible? |
20:57 | <shu> | what do you mean by "possible"? |
20:58 | <devsnek> | your explainer says that realloc is bad because xyz and so you want to avoid it |
20:58 | <devsnek> | which is fine |
20:59 | <devsnek> | but if transfer doesn't avoid it |
20:59 | <devsnek> | then why are you avoiding it with resize |
21:00 | <shu> | because there are two use cases |
21:01 | <shu> | 1) an in-place growable buffer like wasm has, so that an underlying buffer can grow without requiring buffers and TA views be remade. that use case requires the identities of the buffers and TAs to stay the same. related is the webgpu re-pointing use case |
21:01 | <shu> | 2) realloc for arbitrary growing and shrinking, avoiding copying where possible. this use case doesn't care about the identity of the buffers and TAs and can make new JS objects if needed |
21:02 | <devsnek> | i don't get why these are exclusive |
21:02 | <shu> | semantically, they are not. implementation and security experience says otherwise |
21:02 | <devsnek> | is there some inherent thing about security and the identity of the object i'm missing |
21:03 | <shu> | the implementation and security thing boils down to what i said in the explainer, that you want to be able to implement the first use case as not moving the underlying data pointer |
21:03 | <devsnek> | but if they *can* move |
21:03 | <shu> | if the underlying data pointer can move, you have perf cliffs, extra security risk to make sure you don't have stale data pointers, etc |
21:04 | <devsnek> | ah ok |
21:04 | <shu> | detaching was a huge security bug farm |
21:04 | <devsnek> | so its specifically, moving with the same object identity |
21:04 | <shu> | if you allow general data pointer moves, which is a much larger surface than "data pointer or null", it puts security folks on edge |
21:04 | <shu> | yep |
21:05 | <devsnek> | got it |
21:05 | <devsnek> | ok one other question |
21:05 | <shu> | shoot |
21:05 | <devsnek> | you mention the wasm detach problem in the motivation section |
21:05 | <devsnek> | about how you have to check every time you read if the buffer was detached |
21:06 | <devsnek> | is that solved with this? i didn't see anything but maybe i missed it |
21:06 | <shu> | good question, i didn't outline the solution in the explainer since. it's solved with the JS integration exposing a new API that uses growable buffers |
21:07 | <devsnek> | so its still on wasm to provide some sort of hook, ok |
21:07 | <shu> | no hook |
21:07 | <shu> | but right now, when you ask for a wasm memory, you get an AB back. i doubt that can be changed to return a ResizableAB and be web compat |
21:07 | <devsnek> | it couldn't be a resizableAB anyway |
21:07 | <devsnek> | wasm memories don't have to have an upper limit |
21:08 | <shu> | there can be a new API where the wasm JS api gives you a ResizableAB, which emscripten would use |
21:08 | <shu> | in practice there is, surely, set it at that |
21:08 | <devsnek> | i doubt most compilers emit an upper limit |
21:08 | <shu> | the emscripten team has told me this would solve their issue, at least |
21:09 | <shu> | i imagine not having a limit can be reported as like, 3gb when exposed to JS |
21:09 | <shu> | that can a contract for emscripten output, i imagine |
21:09 | <devsnek> | but then 3gb of your heap is reserved |
21:10 | <devsnek> | if you did that more than once in node you'd get an OOM |
21:10 | <shu> | that is not observable? and it also is in practice |
21:11 | <shu> | where possible wasm memory bounds checks are implemented with guard pages + segfault handler, and doing so reserves the virtual memory range |
21:12 | <shu> | perhaps you are confusing virtual memory reservation and getting them backed by physical memory |
21:12 | <devsnek> | if you make two reservations |
21:12 | <devsnek> | each 3gb or so |
21:13 | <shu> | if you are running the engine in 32bit, you've exhausted your vm range and are shit out of luck |
21:13 | <shu> | if you are running the engine in 64bit, you're probably fine |
21:13 | <devsnek> | but the v8 heap is only so large |
21:13 | <shu> | what do you mean by the v8 heap? |
21:13 | <shu> | the GC heap is not the process heap |
21:13 | <devsnek> | the place where all the stuff is stored |
21:14 | <shu> | there are several places :) |
21:14 | <devsnek> | the place where all the heap objects are stored |
21:14 | <shu> | buffer backing stores are not allocated on the GC heap |
21:14 | <devsnek> | oh |
21:14 | <devsnek> | interesting |
21:14 | <shu> | i don't think any engine does that? |
21:15 | <devsnek> | so they're just out in process memory? |
21:15 | <shu> | it's hookable by the embedder depending on where they need the buffer to be, but yes they're externally managed |
21:15 | <shu> | could be mmap'd directly |
21:15 | <devsnek> | interesting |
21:15 | <shu> | it's hard to get a contiguous address range otherwise |
21:16 | <devsnek> | i just figured most arraybuffers were usually small |
21:16 | <shu> | the GC heap can't be reserve contiguous gig+ ranges just in case an app needs it, for instance |
21:16 | <shu> | nope |
21:16 | <shu> | i think there's probably a bimodal distribution of buffer sizes |
21:16 | <shu> | given asmjs and wasm |
21:16 | <devsnek> | well wasm in itself is probably bimodal |
21:17 | <devsnek> | its either tiny canvas app or firefox running inside firefox |
21:17 | <devsnek> | anyway this is starting to make more sense |
21:17 | <shu> | thumbs up |
21:17 | <shu> | gotta run, bbl |
21:18 | <devsnek> | 👋🏻 |
21:22 | <Bakkot> | leobalter: I consume test262 in ~half a dozen projects and they will all need to be updated to point to main instead of master unless github supports redirecting branches, but I don't expect that to be all that much work |
21:22 | <leobalter> | Thanks Bakkot! Idk how to name it in that list so I'll have your name :) |
21:22 | <ljharb> | Bakkot: leobalter: iirc one thing you can do is point to "HEAD" and it'll always point to the default branch |
21:23 | <ljharb> | that's not the same as github supporting redirection for web URLs (which is still critically important) but it should suffice for tooling |
21:23 | <leobalter> | ljharb: too many thinks directly pointing to master |
21:23 | <leobalter> | in the import process |
21:23 | <leobalter> | or linking (which is worse) |
21:27 | <ljharb> | leobalter: what i mean is, the tools can all update from master to HEAD and they'll work moving forward, regardless of the default branch name (or the availability of an alternate name) |
21:30 | <leobalter> | FWIW is to set a plan to completely delete the master branch, so I need people to know we are doing this change |
21:43 | <rkirsling> | I think it might be better to lean harder on GitHub for updates on how soon they plan to deploy redirection |
21:45 | <ljharb> | i agree |
23:48 | <rkirsling> | so here's a question |
23:48 | <rkirsling> | if we're inclined to discouraging direct eval |
23:48 | <rkirsling> | how come we let it apply to lexical scopes too? |
23:50 | <rkirsling> | seems like some bud-nipping could've been done when introducing let/const |
23:50 | <Bakkot> | making direct eval do something more complicated than evaluate the code it's passed in the context it's in seems like it would be worse |
23:50 | <Bakkot> | at least the current state is possible to explain |
23:51 | <rkirsling> | hmm it's possible I haven't thought it through enough but |
23:51 | <rkirsling> | I was just thinking in terms of like |
23:52 | <rkirsling> | if vars hoist through blocks then it would seem like direct eval's visibility could be similarly scoped to the containing function |
23:56 | <rkirsling> | I mean I guess that would amount to creating a more awful thing just for the sake of discouraging its use though |