16:14 | <TabAtkins> | I think I'm leaning strong into the "not worth trying, just stick to an ident" camp, because :: is meant to be "like ., but the obj and function don't have to be previously related", and the more we move away from that the more confusing it'll get. |
16:15 | <TabAtkins> | Plus a1.a2::a3.a4::a5.a6() is ????? |
16:15 | <devsnek> | well formed 😄 |
16:16 | <TabAtkins> | i recognize that it's purely a dot-call operator, and so one can argue that any expression that's not of the form ::foo() is clearly doing something different, but I'm not sure about that. |
16:19 | <TabAtkins> | like what if foo::bar , without parens, invoked it like a getter setter |
16:19 | <TabAtkins> | ...wait, what happens when you say foo.bar.baz = 3 and both .bar and .baz are getter/setter pairs |
16:28 | <TabAtkins> | yeah it calls the .bar getter and the .baz setter, that's what i would expect |
16:40 | <TabAtkins> | okay raised https://github.com/js-choi/proposal-bind-this/issues/17 about it; realized while writing it that WeakMaps-as-ephemerons are the most obvious use-case. |
17:02 | <Ashley Claymore> | So there could be a way to do this:
|
17:08 | <devsnek> | 😱 |
17:08 | <Ashley Claymore> | I think atm if someone wanted this they would do something that ends up looking likeref(map, key).v ??= defaultValue |
17:10 | <nicolo-ribaudo> |
key::map ??= defaultValue , otherwise you have to write the implementation in key instead of in map |
17:20 | <Ashley Claymore> | The issue suggests that a :: get/set is treated differently from a call. So the get/set logic comes from the lhs using the rhs as a param |
17:24 | <Ashley Claymore> | Hang on, I did get it wrong way round |
17:26 | <TabAtkins> | Yes, wrong way round. |
17:27 | <TabAtkins> | We just don't have a way to declare that something is a "getter fn", so the RHS would have to, like, use a Symbol to declare it's usable as a bind-getter or bind-setter |
17:27 | <TabAtkins> | class Ephemeron extends WeakMap { [Symbol.bindGet]() { ...} } |
17:28 | <TabAtkins> | the point of weakmaps as ephemerons is letting you attach "properties" to objects in ways that the objects (or anyone else) can't observe on their own |
17:29 | <TabAtkins> | oh lol markdown links really intersect with symbol-keyed methods in a bad way if you don't wrap them in a code span ^_^ |
17:37 | <Ashley Claymore> | So a.b is call [[get]] on deref(a) with toStringOrSymbol(deref(b)).and ~~a::b is call Symbol.bindGet on deref(a) with the value of deref(b)~~. |
17:38 | <TabAtkins> | no |
17:38 | <Ashley Claymore> | and a::b() is call deref(b) with a as this |
17:39 | <TabAtkins> | or, well, i'm unclear on exactly the mechanics you're implying by that second one. i wrote up a possible code example in https://github.com/js-choi/proposal-bind-this/issues/17#issuecomment-946005508 |
17:40 | <TabAtkins> | okay, reading more closely, yeah, you've still got it backwards |
17:40 | <TabAtkins> | the bindGet has to live on b, and be called with a |
17:41 | <TabAtkins> | same as the "bindCall" lives on b and is called with a in a::b() |
17:41 | <TabAtkins> | ("bindCall" just being the unforgable version of b.call() ) |
17:43 | <TabAtkins> | basically if, instead of thinking of a::b() as "call b() with a bound to its this )" you think of it as "call b.call() with a as its first argument", then a::b is exactly the same with b[Symbol.bindGet]() and b[Symbol.bindSet]() |
17:44 | <Ashley Claymore> | My mind has clicked into place now thanks! |
17:47 | <Ashley Claymore> | a::b() looks up [[call]] on b so can’t be intercepted. But get/set would be symbol based so more opportunity for meta-programming |
17:50 | <TabAtkins> | Yeah, the metaprogramming aspect is incidental here, it's just required to get it to work since we have an intrinsic notion of an object being callable, but "invokable as a getter" is extrinsic in today's JS (based on how a function is attached to an object). |
17:57 | <Ashley Claymore> | I think I kept getting it the wrong way around as I was thinking of a::b() as a temporary method on a. So kept jumping to a::b being a temporary property of a.It’s been one of those Mondays |
17:58 | <nicolo-ribaudo> | Wait, isn't a::b() a temporary method on a ? |
17:58 | <TabAtkins> | it's not an unreasonable way to think about it, so that's understandable |
17:58 | <nicolo-ribaudo> | "temporary" = "not actually there" |
17:59 | <TabAtkins> | it's "a temporary method" semantically. Technically it's "unforgably call b.call(a, ...) ", which has nothing to do with methods. |
19:34 | <jschoi> | Aha, I see, so it came from here. 😄 |
19:35 | <jschoi> | I do like Ron’s idea in https://github.com/js-choi/proposal-bind-this/issues/17#issuecomment-946055779. |
19:55 | <bakkot> | You can already do that with a Proxy, right? |
19:56 | <bakkot> | No one uses Proxies for this because they are almost a slow path, but that would be just as a much a problem for this, I would think |
19:56 | <bakkot> | I guess you can't actually do this with a proxy because the key is ToPropertyKey'd before it hits the proxy, never mind |
20:30 | <ljharb> | without extracting it, you can't explicitly call a getter in JS right now at all, and that's not the problem :: is trying to solve |
20:30 | <ljharb> | the engine will call the getter for you, but you're not using invocation parens or new to do it |
21:05 | <TabAtkins> | Proxies do the other direction, where a<op>b is controlled by a and lets them do something arbitrary based on b . The thing I and Ron are talking about is the reverse, where the <op> is controlled by b and can do something arbitrary based on a . |
21:06 | <TabAtkins> | (I already answered this in the issue, but I'm not talking about extracting or calling getters at all, so I'm not sure why ljharb is bringing it up again here.) |
21:07 | <ljharb> | because i'm still not sure why getters are being brought up at all in relation to this proposal |
21:07 | <TabAtkins> | aka, Proxies let you do a Map where map[key] works and turns into map.get(key) . What I and Ron are talking about is having a WeakMap where obj::wm or obj[wm] turns into wm.get(obj) |
21:07 | <ljharb> | the proposal is about syntactic call or bind, which requires there be an actual receiver-sensitive function present in scope first. whether that function came from a data property or a getter is irrelevant. |
21:11 | <TabAtkins> | I can try to explain it again? a.b() is a method call; a::b() is a faked method call, where b is an arbitrary function; it desugars to b.call(a) . Similarly, a.b is property access, but can also be getter/setter invocation if a.b is set up appropriately; my proposal is making a::b possibly to be a faked getter/setter, where b is an arbitrary object advertising some get/set behavior (likely thru a Symbol property); it desugars to b[Symbol.bindGet](a) |
21:11 | <ljharb> | right but that's still what i don't understand |
21:11 | <ljharb> | it's not actually a faked method call |
21:11 | <ljharb> | it's sugar for .call |
21:11 | <ljharb> | which requires a reified function |
21:12 | <ljharb> | and i also don't understand how the symbol protocol would play in, nor what actual use case motivates it |
21:12 | <TabAtkins> | Yes, under the covers it's .call sugar. But in practice it's a fake method call. That's the whole purpose of doing things like const map = Array.prototype.map; foo::map(...) ; it lets you pretend that an arbitrary foo object has a map() method, without actually having to install a property on the foo object at all. |
21:13 | <ljharb> | sure, ok - and getters are methods too. you can do the same thing by extracting the getter and using .call or :: |
21:13 | <ljharb> | adding a new symbol protocol is a lot of overhead, and encouraging/endorsing use of getters and setters is an ideological position i'm not understanding why we'd want to take |
21:14 | <TabAtkins> | "getters are methods too" yeah and they're invoked by a.b . So just like a::b() is a way to fake a.b() , a::b as a way to fake a.b (the getter/setter) is what I"m talking about. |
21:14 | <ljharb> | it would also mean that :: couldn't be transpiled into an actually robust check, because it'd have to do a runtime lookup for the presence of the protocol (altho to be fair transpiling it would still require a robust callBind abstraction) |
21:15 | <ljharb> | so a::b and a::c would, depending on whether a had the Symbol method, invoke the same Symbol method with 'b' or 'c' depending? |
21:15 | <TabAtkins> | No, that's exactly the opposite. >_< |
21:16 | <TabAtkins> | The whole point of a::b() is that you can pretend b is a method of a without a having to know anything about it. This is the same - in a::b , it's the b that advertises it has get/set functionality, allowing you to use it on any object a without a having to know about it. |
21:17 | <TabAtkins> | (I've explained this both here and in the issue, and wrote a short code example showing it off.) |
21:17 | <ljharb> | i'm sorry that none of those explanations have made it clear to me, thanks for bearing with me |
21:17 | <ljharb> | ok so i have to have a b in scope, that has this symbol method defined on it, and then instead of doing b.bind(a) it calls the symbol method? |
21:18 | <TabAtkins> | Yeah. (This is obviously in conflict with using a::b as an unforgeable b.bind(a) .) This lets you use arbitrary "keys" or "properties" on an object without the object having to know how to deal with them. |
21:19 | <TabAtkins> | My first example was using a weakmap as an ephemeron key, but Ron's example of a Slice object applying to array-likes works just as well. |
21:19 | <ljharb> | ok so i think i understand the mechanics. but i definitely don't a) want to lose the critically important bind semantics, and b) i don't see any value in making anything about getters more ergonomic |
21:19 | <ljharb> | and while this shouldn't have much weight in language design, this would add a TON of weight to the transpiler emit for any usage of :: , which would heavily discourage its use for many years |
21:20 | <TabAtkins> | The only complexity would be usage of a::b - a::b() would transpile to a .call() same as now. |
21:20 | <ljharb> | the point of getters is to be able to pretend something's a normal data property - and to hide the function call |
21:20 | <TabAtkins> | (Again, I recognize that this is in complete conflict with the other meaning of a::b .) |
21:21 | <ljharb> | tbh i think i'd rather lose the entire proposal than have this kind of getter-encouraging conceptual complexity |
21:21 | <ljharb> | but i'll think more about it, since that is a strong stance |
21:22 | <TabAtkins> | I don't understand what's any more conceptually complex than the current a::b() , but okay. |
21:23 | <ljharb> | a::b makes a function, and a::b() calls it. just like any other thing that you can call by sticking () on the end of. that . can invisibly call a function and looks like it won't is a huge wart on the language and i would not want to see it expanded. |
21:24 | <ljharb> | (i'd be fine seeing a more ergonomic way to extract getter/setter functions, as a separate proposal; the part i'm repulsed by is adding more invoking-paren-less invocation sites) |
21:27 | <TabAtkins> | I'm still not in any way talking about extracting getters/setters. No part of my proposal is connected to that concept in any capacity. |
21:28 | <ljharb> | i understand that. i'm saying that i think extracting getters/setters would be a better solution to the use cases you've presented. |
21:29 | <TabAtkins> | It... woudln't be? It wouldn't solve what I'm talking about at all. |
21:29 | <TabAtkins> | There is no way in which ephemeron keys can be addressed by extracting getters/setters from anything. |
21:38 | <TabAtkins> | To be a bit clearer, perhaps, you might recall that when we were first talking about private variables, having the syntax just be sugar for using weakmaps was a serious proposal. |
21:39 | <TabAtkins> | Like, using a Symbol, you can attach arbitrary data to an object in a guaranteed-collision-free manner. But it's not private - the object, or anyone who obtains the object, can find your Symbol and get at the data, and potentially get the same data from other objects you've used the Symbol on. |
21:40 | <TabAtkins> | A WeakMap allows the same functionality - associating data to an object without collisions - but does in a private manner. Nobody with the object can tell the data is there unless they also have your weakmap. |
21:41 | <TabAtkins> | The problem with using WeakMaps in this way is the ergonomics are worse than using properties - rather than obj[privateKey] , you have to write privateKey.get(obj) . And since it's method calls rather than syntax, you can' use assignment directly on it; the equivalent to obj[symbol1][symbol2] = 3 is the horrible weakMap2.set(weakMap1.get(obj), 3) . |
21:42 | <TabAtkins> | So the thought was that we could use make some automatic weakmaps and have foo.#bar automatically be sugar for bar.get(foo) (or bar.set(foo, x) ), where bar is a WeakMap the language automatically creates for you and which is syntactically available only in the class body. |
21:43 | <TabAtkins> | We ended up doing private state differently, but the use-case for WeakMaps doing this is still there, for things like membranes, or just generally any object-capability system. That's what I'm talking about. It's not the sole use-case for this syntax, but it's a pretty obvious one that's been discussed here in TC39 many times in the past. |
21:45 | <TabAtkins> | So obj::weakmap1::weakmap2 = 3 would be able to desugar into the above automatically, allowing arbitrary objects to pretend to be getters/setters without the context object knowing about them, just like how a::b() lets b pretend to be a method without the context object knowing about it. |
22:07 | <Domenic> | Am I reading this right that Monrovia actually created a timezone that is 30 seconds off from the rest of the world? https://ptomato.name/talks/tc39-2021-10/#6 |
22:08 | <ptomato> | that used to be much more common; Monrovia was the only one that persisted past 1970 |
22:08 | <Domenic> | Cursed knowledge |
22:09 | <TabAtkins> | oh my lord https://www.timezoneconverter.com/cgi-bin/zoneinfo?tz=Africa/Monrovia |
22:09 | <TabAtkins> | their most recent DST transition was setting the clocks forward 44min30sec |
22:09 | <TabAtkins> | "most recent" being in 1972 |
22:10 | <Domenic> | Wikipedia says they stopped this in 1972 and moved to GMT? |
22:10 | <ptomato> | it's kind of a misnomer that that site calls that a DST transition; it's just a time zone change, they don't have DST there |
22:10 | <Domenic> | Thus the example in the slides being 1972, I see |
22:10 | <ptomato> | right |
22:10 | <TabAtkins> | yeah, i'm assumign this site just treats all time-zone changes as being DST transitions, since that is the 99% case for changing time zones |
22:12 | <Domenic> | Seems kinda sad to lose that information in the serialization, but if the IETF insists, whatevs... |
22:16 | <ptomato> | I agree. if I understood correctly, the insistence was not so much 'you cannot standardize this precision', but more like 'this would detract from the calendar annotation and would be better in a follow up' |
22:17 | <ptomato> | ryzokuken would be able to give more info |