18:42 | <devsnek> | rickbutton: is there any info on RefCollection outside of that presentation |
18:42 | <devsnek> | because i have many many objections i'd like to write down |
18:55 | <rickbutton> | devsnek: yeah, there is a proposal repo, but it is currently private, msg-ing rricard to open it up |
18:56 | <devsnek> | removing ownership of refs from where they are contained seems like it automatically disqualifies the design tbh |
18:56 | <devsnek> | individual ref cells at each position would be better |
18:56 | <devsnek> | and better yet would be removing the restriction on objects in records |
18:57 | <rickbutton> | not sure what you mean by individual ref cells |
18:57 | <devsnek> | like instead of one thing owning a bunch of cells |
18:57 | <devsnek> | each instance is its own thing |
18:57 | <rickbutton> | oh i see |
18:57 | <rricard> | yea I can see that work as well |
18:58 | <devsnek> | but like i said |
18:58 | <devsnek> | better would be just allowing objects in records |
18:58 | <rickbutton> | what would it mean for a primitive to contain an object? |
18:58 | <rricard> | it's an early idea, I'm gonna try to get the RefCollection up asap |
18:58 | <devsnek> | it would mean there was a pointer to the object |
18:58 | <devsnek> | like how there will be pointers to the doubles |
18:58 | <devsnek> | because engines put doubles in the heap |
18:58 | <rickbutton> | well sure |
18:59 | <devsnek> | i don't understand your question then |
18:59 | <devsnek> | the record holds some keys and values |
18:59 | <devsnek> | why is there a limitation on the type of the value |
18:59 | <rricard> | Ergonomically there is a point in having to make the extra dereferencing hoo[ |
18:59 | <rricard> | hoop* |
18:59 | <devsnek> | yeah the extra reference is kind of awful too |
18:59 | <devsnek> | even if you move to a per-instance cell |
19:00 | <rricard> | we can improve it |
19:00 | <rricard> | I agree it's annoying |
19:00 | <devsnek> | you could make the cell implicit in the record |
19:00 | <devsnek> | so you just do `#{ a: {} }` |
19:00 | <devsnek> | and then `whatever.a === that object` |
19:00 | <rickbutton> | one of the major arguments against objects in records is also an ergonomic one |
19:01 | <devsnek> | yeah people want to guarantee the objects are frozen all the way down |
19:01 | <rricard> | the ergonomic goal here is to typeerror |
19:01 | <rricard> | if you bail out, you have to explicitely do it |
19:01 | <rickbutton> | exactly, from our research most bugs introduced while using immutable libraries like Immutable.js and immer are at the boundaries |
19:01 | <rricard> | `#{ a: r({}) } |
19:01 | <devsnek> | from my perspective not allowing objects is unergonomic to the point of being a blocking point |
19:01 | <rricard> | whatever.a.deref() === that object |
19:02 | <rricard> | I don' t know but in this area |
19:02 | <devsnek> | so when two things collide i generally feel its better to go for the one that allows both |
19:02 | <devsnek> | allowing objects doesn't mean you can't have things that are immutable all the way down |
19:02 | <rricard> | I need to go for a quick while |
19:03 | <devsnek> | wasn't there a suggestion to have `##{}` or something which enforces immutable in its children |
19:03 | <devsnek> | or maybe ## was the one that allows mutable children |
19:03 | <devsnek> | i don't remember which was which |
19:03 | <rickbutton> | ^ that question is why I'm not in favor of that syntax |
19:03 | <devsnek> | in either case |
19:03 | <devsnek> | not having objects directly seems like a huge pain point |
19:03 | <rickbutton> | allowing objects means that given a record you can't know if it is actually immutable |
19:04 | <devsnek> | like i'm imagining how i'd use these in node core |
19:04 | <devsnek> | and this would just be a huge pain |
19:04 | <devsnek> | to the point of not using them |
19:05 | <rickbutton> | do you have any specific examples? vv interested in them |
19:05 | <devsnek> | for example datagram packets |
19:05 | <devsnek> | to reply to a datagram message you need the host+port |
19:06 | <devsnek> | moving to `#{ host, port, socket }` would be cool |
19:06 | <devsnek> | except making socket a ref of some sort is a terrible api |
19:06 | <devsnek> | so i wouldn't bother |
19:06 | <devsnek> | or maybe `#{ host, port, socket, reply() }` |
19:06 | <devsnek> | except again you can't put the reply function in directly |
19:07 | <devsnek> | its like there's this cool feature but its so annoying to use its better to just not use it |
19:08 | <devsnek> | another example would be, if we supported evaluation results from TLA modules, they have to be packed up like { result: value } |
19:08 | <devsnek> | which seems like a good case for a record |
19:08 | <devsnek> | since mutating that would just cause confusion |
19:08 | <devsnek> | but if value happens to be an object that breaks |
19:11 | <rickbutton> | yes, allowing objects in records simplifies that use case, but on the other end, I think that allowing #[{}] !== #[{}] causes a significant amount of sharp edges in real world code, we find a majority of bugs in immutables happen because the user of the library didn't expect an object or doesn't understand object identity, so I worry that introducing a primitive that can deeply compare but won't if you aren't careful |
19:11 | <rickbutton> | is a super sharp edge |
19:12 | <devsnek> | do actual humans think of things in js as primitives and objects |
19:12 | <devsnek> | because i find that distinction pretty much meaningless |
19:12 | <rickbutton> | maybe not using the nouns "primitive" and "object" but certainly "things i can compare with ===" and "things i need to write a deep compare function for" |
19:13 | <rickbutton> | in the context of equality, at least |
19:13 | <devsnek> | so the problem is identity |
19:13 | <devsnek> | not the categorization |
19:14 | <rickbutton> | yeah exactly, the meat of the sharp edge is the fact that putting an object in a record implicitly gives the record identity |
19:14 | <devsnek> | i mean if they're defined to recursively have the identity of their parts (how most languages do it) then its not really a problem |
19:14 | <devsnek> | i guess you're saying people would be surprised that it stopped at the object boundary |
19:15 | <rickbutton> | yep, there is no spec reason it isn't possible, it's surprising for humans imo |
19:15 | <devsnek> | weird cuz i find it surprising the other way around |
19:15 | <rricard> | the main motivation we got on why it's like this is actually interviewing some developers using Immutable.js that ended up not trusting immutable equality because they started mixing object |
19:15 | <Bakkot> | my intuition exactly matches rickbutton's fwiw |
19:16 | <rricard> | that's a footgun we want to avoid |
19:17 | <devsnek> | if it was added without that behaviour i wouldn't find much use in it probably |
19:17 | <rricard> | making the ref hoop explicit is a way to at least bring awareness on that footgun |
19:17 | <devsnek> | so its hard to comment much on it at that point |
19:17 | <Bakkot> | devsnek: that seems fine; not every feature needs to be useful for everyone |
19:17 | <devsnek> | eh |
19:17 | <devsnek> | i mean if we're adding new primitives |
19:17 | <devsnek> | seems like a pretty high bar |
19:17 | <rickbutton> | (there is a balance, of course) |
19:18 | <Bakkot> | I would not find it useful if it allowed non-primitive objects, because of that sharp edge, so one of us is going to be out of luck |
19:18 | <Bakkot> | devsnek: oh, there is no way this would get through the committee if it was primitive and also allowed objects inside of it |
19:18 | <devsnek> | like private fields, i've probably used them like once ever because they're so limited |
19:18 | <Bakkot> | if it has objects inside of it, it is not primitive |
19:18 | <devsnek> | i don't want the same to happen to records |
19:18 | <devsnek> | because i think they're cool |
19:19 | <rickbutton> | if you don't want them to be primitives, what do you expect the behavior of === to be? |
19:19 | <rricard> | we're just starting on this, I'm sure we can find ways to make them useful |
19:19 | <rricard> | for you* |
19:19 | <devsnek> | on the subject of identity https://github.com/tc39/proposal-record-tuple/issues/65#issuecomment-606178554 |
19:19 | <rricard> | refcollection is just a very basic way to do it so far |
19:19 | <devsnek> | i think the solution here is going to end up being some sort of new ref primitive |
19:20 | <devsnek> | and i'm just never going to use them |
19:20 | <Bakkot> | there is no way any sort of ref primitive is going to get through committee either |
19:20 | <rricard> | that's why symbol was a nice way to tie things up |
19:20 | <devsnek> | wait so then what is refcollection |
19:20 | <rricard> | but it has ergonomic issues |
19:20 | <devsnek> | oh it returns a symbol? |
19:20 | <rricard> | yes |
19:20 | <devsnek> | ah jeez |
19:20 | <Bakkot> | that's kind of cute |
19:20 | <rricard> | yes reakized my slides suck |
19:21 | <devsnek> | that seems like a huge footgun too |
19:21 | <devsnek> | nothing implies its a ref to another object in that case |
19:21 | <devsnek> | you have to implicitly know |
19:21 | <rricard> | the refcollection is there to make you manage it |
19:21 | <devsnek> | no like |
19:21 | <devsnek> | if i hand you a record |
19:21 | <devsnek> | you have to know |
19:21 | <devsnek> | exactly where all those symbols are |
19:22 | <rricard> | I agree it's not perfect |
19:22 | <devsnek> | and that they need to be unwrapped |
19:22 | <devsnek> | and you have to be given the refcollection somehow |
19:22 | <devsnek> | out of band |
19:22 | <rickbutton> | if you hand me a record, you also need to hand me a refcollection |
19:22 | <devsnek> | why not just hand you an object then |
19:22 | <devsnek> | like if Object.freeze is easier |
19:23 | <devsnek> | i end up never using this |
19:23 | <rricard> | the most primitive form of that is I hand you an object with the record and the symbol mapping |
19:23 | <rickbutton> | with Object.freeze you lose deep equality checks |
19:24 | <devsnek> | there are deep equality libraries 🤷🏻 |
19:25 | <Bakkot> | devsnek: stepping back a bit, why do you want to have these records-with-mutable-contents? |
19:25 | <devsnek> | i gave some examples above |
19:25 | <Bakkot> | you said you wanted to write `#{ host, port, socket }`, but I don't know why you want to write that |
19:25 | <devsnek> | i think there are more use cases for records than diffing react trees |
19:26 | <devsnek> | right because those shouldn't be mutable |
19:26 | <rricard> | we also discussed with jridsgewell about template-records and tuples |
19:26 | <devsnek> | and that also means you can compare the two message origins |
19:26 | <devsnek> | with === |
19:26 | <rricard> | jridgewell* |
19:26 | <devsnek> | instead of doing a.host === b.host && a.port === b.port && a.socket === b.socket |
19:26 | <devsnek> | yeah template records make zero sense to me |
19:26 | <devsnek> | template tuples maybe |
19:27 | <Bakkot> | devsnek: what's the advantage of them not being mutable? are you handing the same object off to multiple parties somewhere? |
19:27 | <devsnek> | Bakkot: they're part of the public node api |
19:27 | <devsnek> | if you mutated them all you're doing is creating confusion |
19:27 | <Bakkot> | devsnek right my question is, does the API return the same object to multiple different callers, or does it make a fresh object for each caller? |
19:28 | <Bakkot> | because if it's fresh, I don't see much advantage in immutability |
19:28 | <devsnek> | i mean if you just don't think this is a valid use case then whatever |
19:28 | <Bakkot> | no I am just trying to understand why you want this |
19:28 | <Bakkot> | for context, to me the advantage of immutability is mostly when you have one object you're handing to multiple parties, so that one party cannot mutate it and thereby mess up the other party |
19:29 | <devsnek> | immutability is whatever |
19:29 | <devsnek> | the main reason in this case is equality and being able to use it as a map key or something |
19:29 | <Bakkot> | ahh |
19:30 | <devsnek> | i really hope tuples can replace Symbol.compositeKey |
19:30 | <devsnek> | but you just end up with huge leaks if you use ref cells |
19:31 | <devsnek> | well refcollection cells |
19:31 | <Bakkot> | the map key thing I tend to just solve in userland, but it does get a bit awkward |
19:31 | <Bakkot> | I should clean up the library I wrote for that and publish it maybe |
19:31 | <rickbutton> | how do you end up with leaks? |
19:31 | <Bakkot> | ends up being pretty clean for users, just messy internally |
19:31 | <devsnek> | RefCollection has to strongly reference the object |
19:31 | <devsnek> | as long as the symbol could possibly be alive |
19:31 | <devsnek> | and since userland can't iterate over the heap |
19:32 | <devsnek> | we just have to call that forever |
19:32 | <rricard> | the refcollection polyfill will leak until it's released |
19:32 | <devsnek> | yeah so |
19:32 | <devsnek> | not good for maps |
19:33 | <rricard> | but you can see an implementation in engine where it automatically releases |
19:33 | <devsnek> | the map implementation can't tell if the object is still being used from the symbol it has |
19:33 | <devsnek> | you can clean the entire map |
19:33 | <devsnek> | but not individual keys |
19:33 | <devsnek> | individual entries |
19:33 | <rricard> | yes I agree in the case of the polyfill |
19:34 | <rickbutton> | well, you can use a FinalizationRegistry |
19:34 | <devsnek> | i guess you could |
19:34 | <rickbutton> | but an in-engine impl can handle that of course |
19:34 | <devsnek> | that seems incredibly complex for the simple case of compound keys |
19:34 | <rickbutton> | I don't see where RefCollection comes into play for compound keys |
19:35 | <devsnek> | because the keys could be objects |
19:35 | <rricard> | ultimately if this works for a unique refcollection we can think of a more global mechanism |
19:35 | <devsnek> | maps can use objects as keys |
19:36 | <rickbutton> | oh I see, you are saying if records allowed objects via RefCollection then records-as-keys-in-Map gets more weird if the records include objects |
19:36 | <devsnek> | i just want there to be more use to this than react vdoms |
19:37 | <devsnek> | without super contrived apis |
19:38 | <devsnek> | if the object thing can't be worked out, perhaps the equality can be split into a separate syntax |
19:38 | <devsnek> | #@{} for immutable+deeply equal or something |
19:38 | <devsnek> | though immutability with implicit object refs is nice :( |
19:43 | <rickbutton> | to be fair none of our internal research involves using record&tuple for vdoms, they just provide a consise and well-understood example (they would obviously be beneficial though) |
19:43 | <devsnek> | i'm just thinking of all the things i do with PartialEq in rust |
19:43 | <Bakkot> | yeah, I confess have also (almost) never used react vdoms |
19:44 | <devsnek> | all the examples i've seen so far involve vdoms |
19:44 | <devsnek> | not inherently react i guess |
19:44 | <Bakkot> | or any other vdoms |
19:44 | <ljharb> | tbh i'm still not clear on why having first class refs in the language would not be a terrible thing |
19:45 | <devsnek> | yeah i'd probably block if symbols in global caches was the solution |
19:45 | <ljharb> | like, i've seen a couple use cases for them, but nothing compelling to me |
19:45 | <devsnek> | (not block as in tc39 block) |
19:45 | <ljharb> | and i've always considered it a wonderful thing that JS doesn't have refs/pointers |
19:46 | <devsnek> | symbols seem strictly worse imo |
19:46 | <devsnek> | in this use case |
19:54 | <bradleymeck> | well i missed some talking |
19:55 | <devsnek> | lol |
19:55 | <rickbutton> | hahaha |
19:55 | <rickbutton> | I'm on day 22 of isolation if I don't argue semantics then I can't tell if I'm alive |
19:56 | <devsnek> | its mostly just me having private symbol deja vu with records |
20:00 | <jmdyck> | I argue therefore I am. |
20:01 | <ljharb> | nuh uh |
20:02 | <bradleymeck> | i doubt compositeKey could be replaced as long as things strongly hold onto refs |
20:03 | <devsnek> | that's my fear |
20:04 | <bradleymeck> | why does it have to replace compositeKey? |
20:04 | <devsnek> | it doesn't have to |
20:04 | <devsnek> | but it is kind of an easy step to take |
20:04 | <bradleymeck> | i don't see how it is easy? since these are strong refs |
20:04 | <devsnek> | since they have the identity of their children |
20:05 | <devsnek> | no I mean if they didn't require all the ref stuff |
20:05 | <bradleymeck> | kind of? |
20:05 | <bradleymeck> | compositeKey allows partial GC idk how this would |
20:05 | <devsnek> | it does what |
20:06 | <bradleymeck> | compositeKey(a, b, c) lets b GC regardless of if the key lives |
20:06 | <devsnek> | weird |
20:06 | <bradleymeck> | a, b, and c do not have their lifetimes interwoven nor tied to the key |
20:07 | <devsnek> | I mean that makes sense in some ways |
20:07 | <devsnek> | I think this is just the problem of having a weird symbol wrapper |
20:07 | <bradleymeck> | you just won't be able to reproduce the key from them (since b is gone) |
20:07 | <devsnek> | it's a contrived solution |
20:07 | <bradleymeck> | how so? |
20:07 | <devsnek> | you're making a weird function that does magic with objects to create some symbol |
20:07 | <bradleymeck> | its not magic, just some weakmaps |
20:08 | <devsnek> | Vs just using the inherent identity rules of the language |
20:09 | <devsnek> | yeah I know how it works internally, I mean it's not obvious or natural |
20:09 | <bradleymeck> | rickbutton: rricard it might be interesting to compare the RC to compositeKey in general. compositeKey explicitly doesn't have a way to reflect on component parts |
20:09 | <devsnek> | in languages with tuples like rust and python you just naturally use them as map keys |
20:09 | <devsnek> | bevause it makes sense |
20:09 | <bradleymeck> | devsnek: it seems fine for me coming from the perspective of DBs with composite keys |
20:10 | <bradleymeck> | you could use things as a map key in those languages but they stay strongly held |
20:10 | <bradleymeck> | which is part of the point of not doing that in the compositeKey proposal |
20:10 | <devsnek> | yeah I mean it's not crazy |
20:10 | <devsnek> | I'm fine with composite key |
20:11 | <devsnek> | but given we might have tuples |
20:11 | <devsnek> | those would be way more natural |
20:12 | <devsnek> | if you needed the weakmap semantics you'd usually do that yourself |
20:12 | <devsnek> | also various things about whether you want it to drop from the map or not |
20:12 | <bradleymeck> | you could have both but you would want to lean towards compositeKey for anything with differing lifetimes if memory is a premium, also you can't weakmap tuples so that makes things hard to deal with in a weak way |
20:12 | <devsnek> | there are a lot of options there that the language can't infer for you |
20:12 | <bradleymeck> | i'd agree but i wouldn't say tuples replace arrays or weak keys |
20:12 | <Bakkot> | map can't have it drop; it would have to be weakmap |
20:13 | <Bakkot> | and a weakmap could drop it iff the key itself was dropped and all its values were |
20:13 | <Bakkot> | that one I think the language can infer |
20:13 | <bradleymeck> | Bakkot: but that requires the key to drop before its components can gc is my point |
20:14 | <bradleymeck> | so you have interwoven the lifetimes |
20:14 | <Bakkot> | bradleymeck wait, why does it? |
20:14 | <devsnek> | you'd use finalization registry to make it drop |
20:14 | <Bakkot> | I am not suggesting you can use the composite to get its components |
20:14 | <bradleymeck> | Bakkot: if you can get the key you can access the components |
20:15 | <devsnek> | even if the objects are dead, because you hold that unique symbol |
20:15 | <Bakkot> | in your proposal? that is not required to be true for the thing I said |
20:16 | <bradleymeck> | Bakkot: in my proposal that isn't true, but i thought your antecedent was about tuples |
20:16 | <Bakkot> | ah |
20:16 | <Bakkot> | no, was speaking of composite keys, sorry |
20:17 | <devsnek> | my overarching point was that weird behaviour like interactions with object lifetimes should be explicit because you're probably doing something that is a special case |
20:17 | <Bakkot> | for composite keys, map cannot drop the key ever because maps cannot drop anything, and for weakmaps the key can be dropped if the key itself and at least one of its components has been dropped |
20:18 | <devsnek> | you can't use a composite key in a weakmap |
20:18 | <devsnek> | it's just a symbol |
20:18 | <bradleymeck> | you can in the object form |
20:18 | <devsnek> | there's an object form? |
20:19 | <bradleymeck> | it has object and symbol, symbol was only added because people wanted it |
20:19 | <devsnek> | interesting |
20:19 | <devsnek> | I wish weakmaps could hold symbols |
20:19 | <bradleymeck> | you can recreate compositeSymbol easily enough and people wanted it so 🤷 |
20:19 | <ljharb> | they can hold boxed symbols |
20:20 | <devsnek> | ljharb: not that helpful though |
21:45 | <ljharb> | does anyone know where to report that w3 has uncool URLs? https://www.w3.org/TR/wai-aria/roles no longer works |
21:45 | <ljharb> | (and a bunch of others) |
21:51 | <devsnek> | what is /roles supposed to be |
21:53 | <ljharb> | specifically, https://www.w3.org/TR/wai-aria/roles#composite_header 404s and the content now lives at https://www.w3.org/TR/wai-aria/#composite |
21:53 | <ljharb> | also https://www.w3.org/TR/wai-aria/states_and_properties → https://www.w3.org/TR/wai-aria/#states_and_properties |
21:54 | <ljharb> | and a few others |
21:54 | <ljharb> | basically it looks like they regressed their site back to hashbang URLs, like it's 2006 |
21:54 | <ljharb> | the main thing is that they need to add redirects, but i'm not sure where to report that |
22:15 | <jridgewell> | Late to the R&T discussion, but |
22:15 | <jridgewell> | > my intuition exactly matches rickbutton's fwiw |
22:15 | <jridgewell> | Same here. I really like this new design. |
22:15 | <devsnek> | 😢 |
22:23 | <jridgewell> | And I also don't understand how tuples could have replaced CompositeKeys |
22:23 | <jridgewell> | Even the old design |
22:24 | <jridgewell> | Would have caused a permanent memory leak, ala the tagged template literal redesign |
22:24 | <jridgewell> | As soon as you stick the old Tuple into a WeakMap, it becomes a permanent value |
22:25 | <devsnek> | if you have the tuple you have the key |
22:25 | <devsnek> | if you don't you don't |
22:25 | <devsnek> | i agree the semantics on how to release them is iffy |
22:27 | <jridgewell> | The problem is that I could create the key at any future point |
22:27 | <jridgewell> | So that key can never be released |
22:27 | <devsnek> | right as long as the objects exist |
22:27 | <devsnek> | that isn't surprising to me |
22:28 | <jridgewell> | Consider it without the objects, purely immutable primitive types |
22:28 | <devsnek> | you can't put that in a weakmap |
22:28 | <jridgewell> | If I did `weakMap.set([1, 2, 3], {})` |
22:29 | <devsnek> | you can't use numbers as weakmap keys though |
22:29 | <jridgewell> | Sorry, meant `weakMap.set(#[1, 2, 3], {})` |
22:29 | <devsnek> | you still can't use numbers as weakmap keys |
22:29 | <jridgewell> | It's a tuple, though |
22:29 | <jridgewell> | Which is just an immutable array |
22:29 | <devsnek> | tuple of numbers |
22:30 | <jridgewell> | Tuple of _anything_ is my point |
22:30 | <devsnek> | there has to be a lifetime somewhere for it to be valid as a weakmap key |
22:30 | <devsnek> | (i probably wouldn't use tuples in weakmaps in either case) |
22:30 | <jridgewell> | I don't think that's going to fly with end users |
22:30 | <jridgewell> | "Why is this tuple allowed and not that one?" |
22:30 | <devsnek> | #[1, 2, 3] as a weakmap key is nonsensical |
22:31 | <devsnek> | the same way 5 as a weakmap key is nonsensitcal |
22:31 | <devsnek> | nonsensical |
22:31 | <jridgewell> | The difficulty is explaining this to anyone |
22:31 | <jridgewell> | I can say the same about `Symbol('foo')` and `Symbol.for('foo')` |
22:31 | <jridgewell> | Why would one be allowed and the other now? |
22:31 | <jridgewell> | Why would one be allowed and the other not**? |
22:31 | <devsnek> | neither are allowed atm |
22:32 | <jridgewell> | Wasn't that just discussed above? |
22:32 | <devsnek> | i don't think its worth trying to argue about the behaviour of values based on weakmap keys |
22:32 | <devsnek> | because everything is very weird |
22:33 | <jridgewell> | But this is a discussion on replacing CompositeKey with R&T, which has to work for maps and weakmaps. |
22:33 | <jridgewell> | I don't think there's we can discuss one without the other. |
22:34 | <devsnek> | i wasn't really thinking about weakmaps |
22:34 | <jridgewell> | Only maps? |
22:35 | <devsnek> | yes, i don't think its bad that you have to build your own keying system for weakmaps |
22:35 | <devsnek> | the lifetimes there are tricky to get right and if any of it is done implicitly you might accidentally leak something |
22:35 | <devsnek> | or end something too early |
22:36 | <jridgewell> | For maps only, I think either R&T design would work? |
22:36 | <devsnek> | what is r&t |
22:36 | <jridgewell> | Records & Tuples |
22:36 | <devsnek> | by either design do you mean with or without objects allowed? |
22:37 | <jridgewell> | Yah, if I've skimmed the above discussion correctly |
22:37 | <jridgewell> | One with `Ref`, and one with direct mutable objectcs |
22:37 | <devsnek> | i think it would be pretty useless without objects allowed |
22:38 | <devsnek> | you have to set up really a bunch of lifetime management with the ref stuff |
22:38 | <devsnek> | even though you don't care about lifetimes at all |
22:38 | <devsnek> | and ref has a lot of other problems |
22:38 | <jridgewell> | I guess it couldn't be just a second tuple to let it handle for you. |
22:39 | <devsnek> | i'd be fine with two kinds of tuples |
22:39 | <jridgewell> | Eg, `Map.set(#[ immutable, refs ])`, where you get the immutable and refs from the first R&T. |
22:39 | <jridgewell> | Because the `refs` reference wouldn't be allowed. |
22:40 | <devsnek> | i don't understand what this means |
22:40 | <jridgewell> | Bradley's `CompositeKey` might work |
22:40 | <jridgewell> | It's an attempt to store both the tuple and it's mutable refs in a single Map key. |
22:40 | <devsnek> | that doesn't seem possible |
22:40 | <jridgewell> | So you don't manually manage the lifetime of the refs, it's done for you |
22:41 | <devsnek> | given just the objects that make up the key |
22:41 | <devsnek> | where do you derive the refs from |
22:41 | <devsnek> | for a get() operation |
22:42 | <devsnek> | on a larger level this is the problem, if you just allow objects you don't have to solve all these problems because the semantics just naturally work together |
22:43 | <devsnek> | but apparently that's also a footgun |
22:43 | <devsnek> | (i never encountered that being a problem in python though) |
22:46 | <jridgewell> | Is `refs` not a map itself? |
22:46 | <jridgewell> | So it holds onto the mutable objects? |
22:47 | <devsnek> | you're the one who brought refs into this |
22:48 | <devsnek> | in order to get an item out of the map you need to have the identity with it, so you'd need a reference to `refs` |
22:48 | <devsnek> | i'm not sure what `refs` is though |
22:48 | <devsnek> | is it the RefCollection? |
22:48 | <jridgewell> | Yah |
22:49 | <jridgewell> | `const refs = new RefCollection();` |
22:49 | <devsnek> | well you can't store that in a tuple |
22:49 | <devsnek> | since its an object |
22:49 | <jridgewell> | I see it uses `deref` |
22:49 | <jridgewell> | Instead of `get` |
22:49 | <devsnek> | RefCollection.prototype.ref apparently returns a symbol |
22:50 | <jridgewell> | Yah |
22:50 | <devsnek> | so you just have to know that some symbols in records and tuples need to be derefed in some random refcollection somewhere |
22:50 | <devsnek> | which refcollection? who knows! |
22:50 | <jridgewell> | This is the same as with Tagged Template Literals |
22:50 | <devsnek> | in what way |
22:51 | <jridgewell> | Mutable data is stored out-of-band (in the invocations arguments) |
22:51 | <jridgewell> | And immutable data is stored as a frozen `TemplateStringsArray` |
22:51 | <jridgewell> | You reference the mutable data based on the index |
22:51 | <devsnek> | is it frozen? |
22:51 | <jridgewell> | Is what frozen? |
22:51 | <devsnek> | the array |
22:51 | <devsnek> | i know its cached |
22:52 | <jridgewell> | The `TemplateStringsArray` is frozen |
22:52 | <jridgewell> | The arguments aren't frozen, you have to generate your own array |
22:52 | <devsnek> | yeah |
22:52 | <devsnek> | i'm not seeing how this relates to map keys though |
22:53 | <devsnek> | are you saying you want to have a key that is the template string array and the values together |
22:53 | <devsnek> | for caching the rendered result or something |
22:57 | <jridgewell> | No, I was talking about using a Tuple for CompositeKey still |
22:57 | <jridgewell> | But it doesn't work it still doesn't work. |
22:57 | <jridgewell> | CompositeKey would, though |
22:57 | <jridgewell> | You'd just stick your immutable R&T and the mutable Refs into a CompositeKey, and let the `===` take care of the equality |
22:58 | <devsnek> | where do you get refs from |
22:58 | <devsnek> | for get() |
23:00 | <jridgewell> | https://button.dev/talks/record-and-tuple-tc39-march-2020.pdf https://www.irccloud.com/pastebin/CWoXdvIl/RefCollection.js |
23:00 | <jridgewell> | `rc` is a brand instance you make with every tuple |
23:00 | <jridgewell> | (`rc` being what I was calling `refs`) |
23:01 | <devsnek> | yeah i've seen the slides |
23:07 | <jridgewell> | With the Tagged form of R&T, you'll could get them automatically as a parameter |
23:08 | <jridgewell> | We'd just have to settle on a syntax for Tagged form and inclusions. |
23:08 | <devsnek> | jridgewell: i'm still not convinced that tagged records and tuples make any amount of sense |
23:09 | <devsnek> | its like saying tagged object literal or tagged array literal |
23:10 | <jridgewell> | You had a very real complaint about the burden of creating a `RefCollection` |
23:10 | <jridgewell> | It just solves that for you |
23:11 | <devsnek> | i don't understand how it does anything |
23:11 | <devsnek> | it doesn't make sense to me |
23:14 | <jridgewell> | `tag#{ foo: object }` would invoke `foo` with an already built record and refs |
23:14 | <devsnek> | `tag#{}` doesn't make sense to me |
23:14 | <jridgewell> | So you wouldn't have to create and do the `rc.ref(...)` manually |
23:14 | <devsnek> | regardless of what {} contains |
23:14 | <devsnek> | and that doesn't solve the ref problem |
23:15 | <jridgewell> | Did ``` tag`x` ``` make sense at first? |
23:15 | <devsnek> | yes |
23:15 | <jridgewell> | Why? |
23:15 | <devsnek> | but i literally cannot figure out what semantics `tag#{}` has |
23:15 | <devsnek> | it feels like you're saying tagged number |
23:15 | <devsnek> | actually i can think of things that tagged numbers would do |
23:16 | <devsnek> | the best i can come up with is something analogous to the JSON.stringify callback for a "tagged record" |
23:16 | <devsnek> | but that seems like a reach |
23:19 | <ljharb> | it does look kind of weird, `tag#` |
23:19 | <jridgewell> | https://www.irccloud.com/pastebin/Ub13FBLx/ |
23:19 | <ljharb> | like at least `obj.#x` has the `.` in it |
23:19 | <devsnek> | oh you're saying `tag#{}` is shorthand for proper records that allow all values in them |
23:19 | <jridgewell> | Yes |
23:19 | <devsnek> | there are a lot of problems with that |
23:19 | <devsnek> | for example |
23:19 | <jridgewell> | Because the values will be put into the refs automatically |
23:20 | <devsnek> | `tag1#{ x: tag2#{ y: {} } }` |
23:20 | <jridgewell> | What's wrong with that? |
23:20 | <devsnek> | having separate refs just don't work |
23:20 | <jridgewell> | We do the same with tagged templates |
23:20 | <devsnek> | you lose the refs in x |
23:20 | <jridgewell> | You have to return a result from the tag |
23:20 | <jridgewell> | If you want to use a result |
23:21 | <devsnek> | what could the possible return value be |
23:21 | <devsnek> | to be useful |
23:21 | <jridgewell> | https://github.com/polymer/lit-html/ |
23:21 | <devsnek> | like if i just want records that can hold objects |
23:21 | <jridgewell> | The entire design of Tagged Template Literals is about the return value |
23:21 | <jridgewell> | Lit uses `TemplateResult` values |
23:21 | <jridgewell> | Which allow you to render |
23:21 | <devsnek> | yes but we're not trying to compose the values of a record |
23:22 | <devsnek> | they're already composed |
23:22 | <devsnek> | into key: value |
23:22 | <jridgewell> | But they're not, because you can't hold mutable values. |
23:22 | <devsnek> | right |
23:22 | <devsnek> | so you end up |
23:22 | <devsnek> | with this useless function |
23:22 | <jridgewell> | You need a tag that actually does something |
23:22 | <devsnek> | what is "something" |
23:22 | <devsnek> | what possible use is there |
23:23 | <jridgewell> | https://github.com/Polymer/lit-html/blob/master/src/lit-html.ts#L67-L68 |
23:23 | <jridgewell> | https://github.com/Polymer/lit-html/blob/7c7442278281a6923eb1dfbb77600b8b64522a50/src/lit-html.ts#L67-L68 |
23:23 | <jridgewell> | You return a value that composes the immutable and mutable parts |
23:23 | <devsnek> | they're already composed |
23:24 | <jridgewell> | And do something with that value in your library |
23:24 | <devsnek> | into keys and values |
23:24 | <jridgewell> | They are not by definition. |
23:24 | <devsnek> | if you don't want that |
23:24 | <devsnek> | why are you using a key value thing |
23:24 | <jridgewell> | You cannot hold mutable data in an immutable record |
23:24 | <devsnek> | this doesn't solve the problem |
23:24 | <jridgewell> | So, no, they are not composed. |
23:25 | <devsnek> | i started with mutable and immutable components that can't be composed |
23:25 | <devsnek> | and ended with mutable and immutable components that can't be composed |
23:26 | <devsnek> | because they're still stuck in a record and a floaty "refs" object |
23:26 | <devsnek> | the refs can't be associated with the record in any way |
23:26 | <devsnek> | and if you want nested structures |
23:26 | <devsnek> | you can't put refs anywhere |
23:26 | <devsnek> | because again its an object |
23:27 | <devsnek> | and refs really fall apart as soon as you start combining records and tuples from different places |
23:28 | <devsnek> | have like five different ref collections each holding a different bit of this tuple i'm trying to get values out of |
23:28 | <devsnek> | does accessing a value become O(n) to the number of ref collections since you have to check each one |
23:29 | <devsnek> | where do you even put all these ref collections |
23:29 | <jridgewell> | https://www.irccloud.com/pastebin/84lzR9OW/deep-tagged-record.js |
23:30 | <jridgewell> | This is entirely up to the tag library |
23:30 | <jridgewell> | And it's the _same_ super powers we give to Tagged Template Literals |
23:30 | <devsnek> | so in this case |
23:31 | <devsnek> | you nest the refs deeply |
23:31 | <devsnek> | refs.get(refs).refs.get(refs) |
23:31 | <jridgewell> | As example code, yes. |
23:31 | <jridgewell> | Again, the tag library can do anything. |
23:31 | <devsnek> | can it solve this problem in a reasonable way? |
23:32 | <devsnek> | refs.get(refs).refs.get(refs) doesn't seem to pass the bar for in-the-language design |
23:35 | <jridgewell> | https://www.irccloud.com/pastebin/WqdrIhyA/deep-tagged-template-literals.js |
23:35 | <devsnek> | obviously yes |
23:36 | <jridgewell> | Is this any different than^ |
23:36 | <devsnek> | tagged templates flatten into one value |
23:36 | <devsnek> | records flatten into two values |
23:36 | <jridgewell> | How? |
23:36 | <devsnek> | because tagged templates are allowed to return objects that reference objects |
23:36 | <jridgewell> | Because if you flatten it all into a single object, you lose the positions of the interpolations |
23:37 | <jridgewell> | Which means it was no better than constructing the object in the first place. |
23:37 | <devsnek> | i'm just really not sure what problem tagged records solve |
23:37 | <devsnek> | i guess it makes making vdoms easier |
23:37 | <devsnek> | it doesn't solve my problem though |
23:38 | <jridgewell> | I don't think your problem can be solved while giving interpolation positions. |
23:38 | <devsnek> | i don't think my problem can be solved by tagged records |
23:38 | <devsnek> | period end of story |
23:38 | <jridgewell> | Which would limit the use of records in any tree structure |
23:39 | <jridgewell> | vdom just being one use case. |
23:39 | <devsnek> | as far as i can tell i would not have any use cases for records and tuples |
23:39 | <devsnek> | with the current design |
23:39 | <devsnek> | they just wouldn't be usable |
23:40 | <devsnek> | and its giving me private fields vibes (i have no uses for those either because they're so limited) |
23:40 | <jridgewell> | As a counter, I don't think mixing mutable and immutable in the same structure solve any real problem. |
23:40 | <jridgewell> | It's just a mutable structure then. |
23:40 | <devsnek> | immutable isn't the only thing that records and tuples have |
23:40 | <devsnek> | their identity is the identity of their children |
23:41 | <devsnek> | which is useful in lots of cases |
23:41 | <devsnek> | even if the structure is mutable |
23:41 | <jridgewell> | Which would be broken with any mutable reference |
23:41 | <devsnek> | i'm not diffing vdoms |
23:42 | <devsnek> | i gave an example above of a udp datagram origin object |
23:42 | <jridgewell> | How would you generate a second UDP packet that's equal? |
23:43 | <devsnek> | its not the packet data |
23:43 | <devsnek> | its the origin of the packet |
23:43 | <devsnek> | the ip and port |
23:43 | <jridgewell> | s/packet/datagram/ |
23:43 | <devsnek> | you need both to reply |
23:43 | <devsnek> | but you also don't want to combine them into a string or something because then you have to parse it apart |
23:44 | <jridgewell> | There are no problems with 2 primitives in a tuple, though. |
23:44 | <devsnek> | (notice that composite keys also don't work here because they don't ref the items that they're made of) |
23:44 | <devsnek> | yes but then maybe you want to add the socket reference |
23:45 | <devsnek> | anyway if this comes down to my use cases not being valid there's not much point in arguing |
23:46 | <jridgewell> | What socket reference? |
23:46 | <devsnek> | on a high level, node has a datagram socket instance |
23:48 | <devsnek> | you could also do a reply function right on the origin record |
23:49 | <devsnek> | though the semantics of the identity of that would be iffy |
23:53 | <jridgewell> | What if we had operator overloading? |
23:54 | <jridgewell> | Eg, my deep equality tag returns some instance that will do equality for you. |
23:54 | <devsnek> | i don't need deep equality of normal objects |
23:54 | <jridgewell> | Or, just a `Datagram` class that had equality without even involving R&t. |
23:54 | <devsnek> | oh i see what you mean |
23:55 | <devsnek> | on a very theoretical level that would work |
23:55 | <devsnek> | but i don't think operator overloading is a good idea in js |
23:56 | <devsnek> | afaik it has yet to be shown that you can do it in an ergonomic way without slowing down all occurrences of the overloaded operator |
23:56 | <jridgewell> | Even the static import design? |
23:56 | <jridgewell> | I thought that solved the issue. |
23:56 | <devsnek> | the `with operators` thing? |
23:56 | <jridgewell> | Yah |
23:56 | <devsnek> | i don't consider that ergonomic |
23:57 | <devsnek> | honestly i think its more of a footgun |
23:57 | <devsnek> | since you can just forget to have it |
23:57 | <devsnek> | especially the more you scope it |
23:57 | <devsnek> | the more you need it |
23:57 | <devsnek> | the more places you can forget it |
23:57 | <ljharb> | that's the first design that i don't instantly and violently abhor |
23:57 | <ljharb> | but i'm not convinced either |
23:57 | <devsnek> | it's not as bad as others |
23:58 | <devsnek> | but i don't think it's good enough |
23:58 | <devsnek> | i also have issues with how static it is |