15:16
<bakkot>
my takeaway from this poll is not "we need a bunch more syntax for accessors"
15:18
<nicolo-ribaudo>
Oh damn I missed the beginning of plenary, thank you for writing a message here
15:20
<nicolo-ribaudo>
Is there a link to the slides?
15:20
<Aki>
reminder to note-takers that if the captioner is inserting newlines, they don't have to worry about those. I'll take care of them at conversion time.
15:22
<Chris de Almeida>
proposal repo, it's html
15:22
<Chris de Almeida>
https://github.com/LeaVerou/proposal-composable-accessors
15:22
<Chris de Almeida>
slides dir
15:22
<nicolo-ribaudo>
Thank you
15:25
<bakkot>
I am confused about what this "internal property" is that is not the existing accessor property
15:27
<nicolo-ribaudo>
Maybe this proposal is pointing to a need to split off accessor from the decorators proposal so that it's not blocked on them?
15:28
<rbuckton>
accessor is sort of split off. It was originally introduced in Grouped and Auto-Accessors and brought over to decorators to meet implementor requirements
15:31
<nicolo-ribaudo>
I think I would prefer for these things to be reified as accessors and not directly visible in descriptors, to avoid making descriptors being more complex
15:54
<nicolo-ribaudo>
What's the time box? I noticed we are almost at the hour, and my reply is low priority so I can drop it if needed
15:54
<bakkot>
1 hour
15:59
<keith_miller>
nicolo-ribaudo: Which syntax were you talking about the one on the slide now with the decorator?
15:59
<nicolo-ribaudo>
The one with the validate keyword
16:00
<keith_miller>
Or the class Foo { accessor x = 2; } syntax?
16:00
<nicolo-ribaudo>
I meant that class Foo { validate x(val) { ... } } looks a lot like class Foo { set x(val) { ... } }, so I'd expect similar performance
16:01
<nicolo-ribaudo>
I'd also expect similar perf with the decorator example, since I'm literally seeing a function attached to the property
16:01
<nicolo-ribaudo>
I would have less of that expectaton for a plain accessor x = 2, but I guess most times you'd want to decorate that anyway
16:02
<nicolo-ribaudo>
I wonder if accessor x = 2 is more optimizable though, since there is no user code running there even if it's a get/set pair
16:02
<keith_miller>
The motivation for this, as I understand it anyway, is to use it everywhere for reflection/metaprogramming
16:03
<keith_miller>
Which is the reason I mentioned hiding the cost of the call. Not sure about others
16:04
<nicolo-ribaudo>
Ok I think I was mostly focusing on the second use case, I'm not sure I agree with the first one
16:04
<ljharb>
only for the use cases where the instance shape is needed from outside
16:04
<nicolo-ribaudo>
For this could we have a bult-in @public decorator, with the decorators metadata proposal?
16:04
<nicolo-ribaudo>
That marks it as "public" in the metadata
16:06
<ljharb>
true - and a class decorator could do it all in one
16:06
<keith_miller>
Are people realistically going to add the mental overhead of figuring that out though? My assumption would be that codebases that don't particularly care about performance would just apply it everywhere
16:07
<ljharb>
sure but if they don’t care about performance then it doesn’t matter there, right?
16:07
<bakkot>
we care about performance even if they don't
16:08
<nicolo-ribaudo>
"people are not thinking about what things make their code slow" and "people want their code to be fast" unfortunately have a large intersection
16:08
<bakkot>
we should design the language such that even people who don't care about performance end up on the happy (= reasonably performant) path
16:09
<ljharb>
fair
16:20
<rbuckton>
New features aren't necessarily free, especially when we implement whole new capabilities to the language.
16:27
<nicolo-ribaudo>

I'm looking at my accessors usage, and they are (numbers are not measured):

  • 75% "property forwarding"
  • 15% lazy initial computation
  • 10% validation
  • 5% other
16:27
<nicolo-ribaudo>
The % symbol obviously means " / 105"
16:27
<rbuckton>
For point 1 in the problem statement, would we ever consider adding something like Function.getInstanceFieldNames(ctor)?
16:28
<nicolo-ribaudo>
Some feedback that was shared at last plenary is that class fields should behave similarly to properties defined in the constructor
16:28
<rbuckton>
You could add a proxy trap for it like apply/construct
16:29
<rbuckton>
I don't necessarily agree with that position, though I do agree you shouldn't be able to just grab them via getOwnPropertyDescriptors.
16:30
<rbuckton>
A getInstanceFieldNames would be simpler than a @Public decorator that uses Symbol.metadata to do the same thing
16:34
<waldemar>
Test message; please ignore
16:36
<waldemar>
(Matrix seems to have glitched and kicked me out for a while)
16:44
<rbuckton>
If Decorators were at stage 4, the composable accessors piece would easily be a public API proposal a la iterator helpers or similar, which would likely be easier to discuss and advance
16:48
<rbuckton>
That said, most of the composition pieces presented or in the explainer could be implemented in userland decorators first before standardizing
16:49
<rbuckton>
The built-in decorators that can't be implemented in userland that I'm mostly interested in are not these composable decorators, however.
16:57
<nicolo-ribaudo>
Let's continue with this please, to keep context
17:02
<Michael Ficarra>
overall the transcription was pretty good but there was a writer switchover at 11:00 while @rbuckton was speaking and that paragraph got completely mangled
17:02
<Michael Ficarra>
you'll know it when you see it
17:02
<Michael Ficarra>
@rbuckton do your best to fix it up
17:05
<hax (HE Shi-Jun)>
Yeah I have similar usage, and I think lazy init is especially hard to expresss even with current decorator proposal.
17:16
<hax (HE Shi-Jun)>
But if u consider validate/sideeffect use cases, it actually don't require to be use accessor but only some observer ability. Force it to become accessor have performance result (To be clear, I don't care much about perf issue, but I always hear some delegates attack some proposals use this reason).
17:18
<hax (HE Shi-Jun)>
I think lea already suggest similar proposal in last meeting and be refused? Or maybe I misunderstand it?
17:20
<Lea Verou>
Oooh yes, lazy init is another big one.
17:21
<Lea Verou>
While most things can be done with decorators, I suspect alias accessors likely would need some syntax if we want to support private members. And property chains would be a lot more elegant as an actual language construct (like in delete) as opposed to e.g. an array of property names.
17:21
<hax (HE Shi-Jun)>
To be honest, my question is, whether decorator could really go to stage 4, considering the position of browser vendors? (If there was any news on that, pls tell me...)
17:23
<Lea Verou>
Another advantage of the decorators direction is that it's a lower-cost way to prototype and test the waters. E.g. if this ends up being used tremendously much, we could introduce a syntax-level solution down the line. For this alone, I'm actually now myself in favor of the decorators direction
17:23
<hax (HE Shi-Jun)>
@lazy accessor x = lazy.of(() => init) // example provided by Jack Works
Full of syntax noise 🙃
17:25
<rbuckton>
Which paragraph?
17:25
<Lea Verou>
hax (HE Shi-Jun): it doesn't need to, it could be let { lazy } = SomeObject and then just @lazy(init) accessor x . I'm less worried about O(1) DX issues, it's the O(N) (per property) DX issues that are the main problem
17:28
<hax (HE Shi-Jun)>
I'm afraid @lazy(init) does not work if u want to access something on the instance in initializer?
17:31
<nicolo-ribaudo>
@lazy accessor x = () => init?
17:36
<hax (HE Shi-Jun)>
I think u can't make it type safe in ts. Though it maybe the fault of TS 😆
17:38
<hax (HE Shi-Jun)>
Anyway, I really feel lazy init is too common and should have better DX. If u check other morden langauge, in Kotlin u write var x by lazy { ... }, in swift u write lazy var x = { ... }(), in Scala u write lazy val x = { ... }. It's shame that we can't provide js programmers a good solution
17:38
<ljharb>
A getInstanceFieldNames would be simpler than a @Public decorator that uses Symbol.metadata to do the same thing
it is critically important that it be opt in and not available by default.
17:39
<Lea Verou>
hax (HE Shi-Jun): A way I often implement lazy accessors myself is to set a symbol property in the getter and just return that if set. I believe that can totally be done with decorators, right?
17:39
<Steve Hicks>
That's my point about well-known builtin decorators - TypeScript could learn the type calculus behind them.
17:44
<hax (HE Shi-Jun)>
Obviously 10 js developers can have 10 different way to do it. If u ask GPT it may teach u write class C { get val() { Object.defineProperty(this, 'val', { value: expensive(), writable: false }); return this.val; } }
17:45
<Lea Verou>
Yes, but if it's a built-in decorator that's not an issue
17:46
<hax (HE Shi-Jun)>
I'm very curious how engine vendors see such re-defining prop descriptor which LLM like to encourage but they refused in decorator proposal.
17:52
<hax (HE Shi-Jun)>
Is there any specific reason that it should be opt in? And what opt-in mechnism will u like to see?
18:00
<Jack Works>
@lazy accessor x = lazy.of(() => init) // example provided by Jack Works
Full of syntax noise 🙃
(my version is to let TypeScript happy, otherwise it can be @lazy accessor x = () => init
18:01
<rbuckton>
It's certainly feasible for TS to implement. It's on the backlog and I had planned to work on it at some point.
18:02
<nicolo-ribaudo>
Put those that didn't help yet during this meeting on random.org/lists and pick two!
18:03
<Aki>
yoooo it's Julie captioning
18:03
<Aki>
she's really good
18:05
<Andreu Botella>
we should do this every time – maybe excluding first-timers
18:06
<ljharb>

because anything that's introspectable is part of the public API and a breaking change if altered. i don't care what the mechanism is, it just has to be solely up to the class author.

separately, public class fields are sugar for statements in the constructor, and statements inside function bodies are never introspectable in the language, nor should they be.

18:06
<Aki>
the only reason the chairs haven't done that in the past (at https://takingnotes.rocks) is that there are some people who can't for personal reasons and it's not fair to put them on the spot
18:07
<rbuckton>
Isn't that why we have private fields? public fields are introspectable on an instance, just not from a constructor. IMO, you shouldn't need to construct an instance for that introspection.
18:07
<nicolo-ribaudo>
I mean the chairs could do the extraction privately, and those people can privately ask them in advance to be excluded
18:08
<ljharb>
nope, they're not. public properties on the resulting instance are, certainly. but the existence of a field does not guarantee it will end up as a property, nor will the list of fields be exhaustive (meaning there could eventually be properties not in that list). (remember any subclass can arbitrarily delete a property that a field created, for example)
18:10
<rbuckton>
A field declaration will definitely guarantee its presence as a data property. They are always installed on an instance (barring use of delete, which is rarely a good idea).
18:11
<ljharb>
ok but the existence of that caveat means that it's not a guarantee.
18:11
<hax (HE Shi-Jun)>
But u already can use Object.getOwnPropertyNames() or other api or syntax to get the own properties... I don't see any difference here (except u can only get them via object not class).
18:11
<ljharb>
of the instance. you can't do that from a constructor alone.
18:11
<ljharb>
nor should you be able to, without the permission of the class author.
18:11
<rbuckton>
You can delete methods and accessors as well
18:11
<ljharb>
indeed you can
18:12
<ljharb>
but that you can introspect on the class.
18:12
<ljharb>
overall, implicit reflection/introspection is a horrifically bad language "feature" and we should go out of our way to not introduce any additional cases of it.
18:12
<ljharb>
explicit/opt-in reflection is great though! i fully support an author's freedom to expose what they want. but it must be a choice. (ideally it's an easy one, just explicit)
18:13
<rbuckton>
I strongly disagree with that sentiment.
18:13
<ljharb>
that is unsurprising :-)
18:13
<ljharb>
but suffice to say there's no consensus to add any new implicit/default introspection mechanisms, though.
18:14
<rbuckton>
IMO, it's a terrible idea to use delete on any member of a class.
18:14
<ljharb>
oh i agree
18:14
<ljharb>
lots of things in any language are a terrible idea. but they still exist.
18:14
<rbuckton>
And you certainly won't be able to delete fields from struct instances.
18:15
<ljharb>
which is great
18:15
<hax (HE Shi-Jun)>
ljharb: " public class fields are sugar for statements in the constructor, and ..." --- No they are not sugar. No one write "Object.defineOwnProperty(this, "name", ...) in constructor. 😂
18:15
<ljharb>
that nobody writes it is why it's sugar
18:15
<ljharb>
and prior to the [[Define]] decision it was sugar for the thing everyone writes, which is this.foo = 'bar'
18:17
<hax (HE Shi-Jun)>
Ok. I mean, no one want to [[Define]] before this feature, so it's really weird to me to see it as sugar. I always see sugar should not introduce uncommon semantic.
18:18
<nicolo-ribaudo>

Oh well I can't speak sorry.

It's for Trusted Types, it is being implemented

18:19
<nicolo-ribaudo>
It's not ready for stage 4 yet though, according to my colleague working on it
18:21
<hax (HE Shi-Jun)>
ljharb: On the other side, why it should be opt-in but not opt-out?? For example, in TS you use private x which seems match the case you want (by default it's public api, but private means it's not public)
18:26
<keith_miller>
Can uhh, I convince one of the chairs to add the 3.md agenda so I can add Atomics.pause to stage 4 before I forget... :)
18:26
<hax (HE Shi-Jun)>
ljharb: To be clear, I think opt-in is ok, I even like everything is not public by default, but JS class already make methods public, it's really weird that only fields need to opt-in for public.
18:29
<ljharb>
TS's private is bad for a ton of reasons so i don't think it should be used as an argument
18:30
<ljharb>
because a "field" isn't a first-class thing. a field is just instructions to set up a property - which is indeed public by default. iow, it's sugar.
18:30
<ljharb>
yes, i will do that now
18:34
<Zb Tenerowicz (ZTZ/naugtur)>
I might be interested in co-championing @ljharb:matrix.org: but will need a comprehensive intro
18:39
<hax (HE Shi-Jun)>
I am not sure what "first-class" means. In my opinion, the whole point of fields proposal is to make it declarative and get the "first-class" support in the language.
18:43
<Olivier Flückiger>
@erights Mark Miller (Agoric) MM: I have a counter for last match, left context and right context. It is surprisingly not that small: https://chromestatus.com/metrics/feature/timeline/popularity/5641#V8RegExpStaticPropertiesWithLastMatch
18:44
<Olivier Flückiger>
We would also have counters for the rest (and most probably more common) of the static regexp properties, but it seems there is a feature id collision so that counter is broken...
18:45
<ljharb>
it means "a thing you can pass around". variables are not first-class; objects are, for example.
18:45
<ljharb>
things that aren't first-class aren't reified "things"
18:45
<ljharb>
k, now done
18:48
<hax (HE Shi-Jun)>
So why some class members (methods and accessors) are first-class, others (fields) are not? very inconsistent.
18:48
<nicolo-ribaudo>
Interesting, I can see usage of .lastMatch/leftContext/rightContext on the xiaomi website but not in the other two of the top 3
18:49
<ljharb>
that's not a consistency that i'd expect anyone to expect, but sure, it's inconsistent. the "why" doesn't change that it's the way it is. classes are declarative - they're instructions. that some pieces of them turn into first-class things and some don't is just based on the semantic of the instructions. static blocks aren't first-class either, and neither are private fields.
18:51
<nicolo-ribaudo>
And the fourth one is coming from a twitter sdk (https://github.com/twitter/twitter-text): https://github.com/twitter/twitter-text/blob/30e2430d90cff3b46393ea54caf511441983c260/js/src/extractUrlsWithIndices.js#L70
18:54
<nicolo-ribaudo>
Which is unmaintained but has 50k downloads/week
18:58
<rbuckton>
I'm back, sorry for the interruption
19:00
<Olivier Flückiger>
I found that the combined counter is probably correctly working: https://webstatus.dev/features/regexp-static-properties?q=RegExp . 7% :((
19:01
<nicolo-ribaudo>
Unfortunately not only that library has no recent activity, but every single person that worked on it seems to not be working at that company anymore
19:02
<rbuckton>
Built-in decorators (+ function decorators) would allow something like C#'s StackTraceHiddenAttribute https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.stacktracehiddenattribute?view=net-10.0
19:05
<Olivier Flückiger>
Yeah, but I think the fact that the other static properties are used even more is worse. Unless we split the proposal to only remove the little used ones.
19:05
<ljharb>
I have to drop, tty all next time
19:06
<nicolo-ribaudo>
Looking at that file, we can work on a proposal to remove RegExp.$6! 😛
19:09
<hax (HE Shi-Jun)>
Have no idea why xiaomi website use such legacy feature, I will try to find and ask the guys in xiaomi 🤨
19:09
<Michael Ficarra>
thank you for hosting this session Peter!
19:10
<Steve Hicks>
I'm a Matrix n00b, is there a place where these different spaces are listed?
19:11
<Chris de Almeida>
if only there were...some kind of guide... like a matrix guide.. with information on matrix
19:11
<Chris de Almeida>
https://github.com/tc39/how-we-work/blob/main/matrix-guide.md
19:11
<Chris de Almeida>
https://matrix.to/#/#tc39-space:matrix.org
19:12
<Chris de Almeida>
☝️ TC39 space
19:22
<peetk>
what was this in response to? isTemplateObject?
19:26
<peetk>
my pleasure!
19:26
<rbuckton>

For simple field wrapping to support public read/private write, I'd argue for this feature proposed in grouped accessors:

class C {
  accessor x { get; #set; } // paired public getter/private setter
  m() {
    this.x; // ok
    this.#x = 1; // ok
  }
}
const c = new C();
c.x; // ok
// can't use c.#x

For more complex cases (like subproperty access), alias is maybe interesting?

19:35
<rbuckton>

I have been tempted to propose something like C#'s "expression bodied methods", which use =>, i.e.:

// C#
class C
{
  int _x = 0;
  public int x => _x; // public getter with shorthand expression body
  public int y {
    get => _x;
    set => _x = value;
  }
}

Which could could cut down on boilerplate for the alias case:

class C {
  #x = { value: 0 }; // for something more complex
  get x => this.#x.value;
  accessor y {
    get => this.#x.value;
    set => this.#x.value = value;
  }
}

// vs.

class C {
  #x;
  get x() { return this.#x.value; 
  accessor y {
    get () { return this.#x.value; }
    set (value) { this.#x.value = value; }
  }
}
19:35
<keith_miller>
nicolo-ribaudo: How many lines of code did your numbers come from BTW?
19:38
<Zb Tenerowicz (ZTZ/naugtur)>
Yes
19:38
<nicolo-ribaudo>
Around 200k in a few repos, but I didn't measure as I said above. I scrolled through search results and eyeballed it.
19:38
<nicolo-ribaudo>
All property forwarding was about getters btw, setters are much rarer
19:39
<nicolo-ribaudo>
One codebase uses typescript, the others do not. Validation is higher in the ones that do not use TS
19:41
<Zb Tenerowicz (ZTZ/naugtur)>
I have some appreciation for the feature and have been looking at ASTs of tagged templates recently
19:44
<rbuckton>
This seems like a good use case for expression-bodied getters, IMO.
i.e., get x => this.#x
19:46
<Lea Verou>
is there a proposal for this? (edit: nvm, just saw the earlier messages!)
19:48
<Steve Hicks>

Couldn't aliases/forwarding be done with a decorated accessor? It would be a little wasteful of the unused underlying accessor, but there's any number of ways you could write

@alias({get: () => this.foo.bar, set: (x) => this.foo.bar = x})
accessor bar;

or

@alias({owner: this.#foo, prop: 'bar'}) // obviously order matters here, could be lazy () => this.#foo

And if you wanted to define a bunch of them, you could compose your own decorator that saved some boilerplate

@myalias('foo') accessor foo;
@myalias('bar') accessor bar;

where @myalias could effectively "close over" any this.#owner.

19:48
<rbuckton>
Not yet. I'd considered it when I proposed grouped/auto-accessors but since that proposal was blocked from Stage 2 until decorators hit Stage 4, I held off.
19:49
<Lea Verou>
Separating the object from the prop like that seems even more awkward than the status quo
19:49
<Lea Verou>
though this makes me wonder if instead of alias accessors we need a syntax-level feature for referencing property chains
19:49
<rbuckton>
that certainly isn't very concise, imo.
19:49
<Lea Verou>
then regular decorators could do it
19:49
<Lea Verou>
plus, lots of other use cases outside accessors
19:50
<rbuckton>
C# has Expression<T> that's used in LINQ, but that requires reifying a subset of the AST
19:50
<Lea Verou>
E.g. filtering, various helpers (e.g. lodash's get/set)
19:51
<rbuckton>
Well, not quite the same. Expression<T> is used by an expression rewriter to turn C# expression syntax into other languages like SQL to convert a LINQ pattern into a database query.
19:52
<Lea Verou>
basically a type of literal that returns some kind of PropertyReference object
19:53
<Lea Verou>
which could have methods to get/set/etc the property chain for a given object, perhaps (Just brainstorming out loud here)
19:53
<rbuckton>
I have a proposal for reified references that I have yet to bring to committee (https://github.com/rbuckton/proposal-refs) that would do that.
19:54
<Lea Verou>
Thanks! I'll need to look at this more closely later, I tried skimming but it needs more time than that
19:55
<Lea Verou>
It could be useful to include examples of property chains and privates (if it covers them too)
19:57
<rbuckton>
It does.
19:58
<rbuckton>
Though "property chain" isn't the right term. It captures the Reference resulting from the property chain.
19:58
<Steve Hicks>
FWIW (probably not much), my Closure Compile-addled brain tends to go to something along the lines of {foo: {bar: true}} to represent a property chain... but that's more due to limitations with how our optimizer handles property renaming. I don't know if {#foo: {bar: true}} could work (assuming privates in object literals landed), but it's certainly neither pretty nor performant and probably a non-starter.
20:02
<Steve Hicks>
But at the end of the day, what we're talking about is a pair of functions: {get: (T) => V, set: (T, V) => void}, so I think the question is whether there could be a reasonably ergonomic way to write that object.
20:05
<rbuckton>

The idea with ref is that a ref-expression captures a Reference to an expression and reifies it:

let x = 0;
let rx = ref x; // ref expression
rx.value; // 0
rx.value = 1;
x; // 1

While a ref-declaration dereferences that reference as if it were the roughly the same binding:

let x = 0;
let rx = ref x; // ref expression takes reference

let ref y = rx; // ref declaration dereferences
y; // 0
y = 1;
x; // 1
20:07
<rbuckton>
There's no concept of a ref declaration for a field or accessor however, so while you could take a ref to a property, you can't necessarily install the ref as a property (at least, not yet)
20:13
<rbuckton>
There is a suggestion for ref properties, though (https://github.com/rbuckton/proposal-refs/issues/12)
20:13
<rbuckton>
I really should just present this and at least get stage 1, since one way or another it's worth discussing.
20:18
<Lea Verou>
That seems to be the desirable behavior? Are there any cases where that would break expectation?
20:19
<Lea Verou>
Oof, that's quite a problem. Do you think it's fixable?
20:19
<rbuckton>
Yes, I'm just saying "property chain" is the wrong terminology. to me, "property chain" implies that if some part of the chain is replaced, re-evaluating would produce a different result.
20:20
<rbuckton>
See https://github.com/rbuckton/proposal-refs/issues/12
20:21
<Lea Verou>
For that to work for the intended use cases, it basically needs to re-evaluate the chain itself, kind of like a very very limited eval almost. Which seems like the case here? (aside from the issue above)
20:25
<rbuckton>

For the intended use case, only the Reference at the end matters. Consider:

let x = { y: { z: 0 } };

let rz = ref x.y.z;

rz.value will always point to the Reference for y.z at the time the ref was taken even if you change x.y to something else. This is no different from

let y = x.y;
let rz = ref y.z;

the intermediates don't matter, only the trailing Reference

20:25
<hax (HE Shi-Jun)>

Yeah, I’ve always liked the refs proposal. Just one thing I’m a bit concerned about is the syntax ref x — for example, the syntactic ambiguity of ref (x), and also that ref a.b.c could easily be misunderstood as re-evaluating the entire chain every time.

Maybe a syntax like x.& could be an alternative — it has better ergonomics (in my opinion) and can express capabilities more clearly (for example, x.&r could mean only expose read capability). Anyway, syntax is just bikeshed issue.

20:26
<rbuckton>
ref is a placeholder. It could easily be &. I just was veering away from C-like addresses and pointers.
20:27
<rbuckton>
However, ref x.y.z works with a Reference the same way delete x.y.z does and is consistent with JS on the whole.
20:27
<rbuckton>
x.&r looks highly confusing to me
20:28
<rbuckton>
ref (as expression and as declaration) also has prior art in C#
20:28
<hax (HE Shi-Jun)>
ah, I forgot delete x.y.z, I suppose no one use delete in most morden JS/TS 😂
20:29
<rbuckton>
I would not want & or ref to come in the middle of a property access. It could get lost in a deeply nested chain.
20:29
<Lea Verou>

For alias accessors, the entire chain matters. E.g. suppose I have

class C {
  alias foo = #foo.value 
}

If I replace #foo, I don't want this.foo to still be pointing to the old value (and it's the same for deeply nested objects)

20:29
<Lea Verou>
Also, there are use cases that need access to the intermediate properties (e.g. lodash's get/set that I mentioned earlier)
20:29
<rbuckton>
If that's the case, then ref is not what you want. But I also wouldn't use = as that implies an immediate assignment
20:30
<Lea Verou>
Yes, that exact issue was an open question in the explainer (that = is probably not appropriate)
20:30
<rbuckton>
You could just as easily use as or for or ->, etc.
20:30
<Lea Verou>
But also, after this discussion I'm starting to think that this should also be done with decorators, and we should instead introduce a syntax primitive to make it nice (even if it's not ref)
20:31
<Lea Verou>
There was another proposal in the previous meeting that also needed a way to reference property chains, I forget which one, and there was no good story (perhaps the deep diffing one?)
20:31
<hax (HE Shi-Jun)>
It's a interesting question, let me check other language, eg. Kotlin code var x by a::b::c Not sure which behavior it adopt.
20:34
<Lea Verou>
The main challenge I see is not syntax of the literal, it's how to handle privates. For it to cover forwarding use cases, privates are MVP, but we have no way to represent them in a ComputedPropertyName
20:37
<Lea Verou>
Perhaps it could end up behaving like an array of objects (once created) to represent the chain, then privates could have e.g. private: true, and if you try to construct a literal with a private property that doesn't exist in your current context the construction itself would throw.
20:38
<hax (HE Shi-Jun)>
Shouldn't that be a earlyerror?
20:38
<Lea Verou>
Yes, that's what I'm saying
20:39
<rbuckton>

You could potentially do this with a decorator and ref:

class C {
  #x = { y: { z: 0 } };

  @alias(_ => ref _.#x.y.z) accessor x;
}

Where the get/set are replaced with an invocation of the callback that returns a ref you can read from or assign to

20:40
<Lea Verou>

e.g.

let foo = .a.#b.c.d; /* property chain literal */

which could throw if your current context doesn't include #b (if it does but a doesn't have it that could throw later)
Especially since in a lot of these use cases, the "root" of the property is not fixed but the chain can be applied to a variety of root objects (e.g. in alias accessors there is an implicit this)

20:43
<rbuckton>

I think arrows and ref might be sufficient. _ => _.a.#b.c.d would generally work find as a deferred way to read a value, you just can't write to it. _ => ref _.a.#b.c.d would let you do both:

let foo = x => ref x.a.#b.c.d;

let obj1 = ...;
let obj2 = ...;

foo(obj1).value; // read
foo(obj1).value = ...; // write

foo(obj2).value; // read
foo(obj2).value = ...; // write
20:43
<Lea Verou>
Yes, that would work. I do find it awkward though.
20:44
<Lea Verou>
Also, there are use cases where you may want to break the chain
20:44
<Lea Verou>
E.g. "return the last non-null/undefined object"
20:46
<Steve Hicks>
nicolo-ribaudo: I don't see PKA in this channel, but I was reaching out to Krzysztof Kotowicz about https://github.com/tc39/proposal-dynamic-import-host-adjustment and I think there was a transcription error somewhere since he's not actually mentioned anywhere in that repo. It looks like it's entirely Mike Samuel (who used to be at Google, but is no longer).
20:53
<hax (HE Shi-Jun)>
Ok, kotlin behave same like ref proposal, the syntax is var x by a.b::c which is much clear the last part is the reference (a::b::c is invalid)
20:56
<rbuckton>
I'd still rather not mix & or similar inside a property access. We already have . and ?., and have had proposals for other property-access-likes in the past. IMO that's too noisy.
20:59
<rbuckton>
C, C++, Go, C#, and Rust (among others) use a prefix operand
21:13
<Lea Verou>

rbuckton: What about a leading . ? I have a hunch it would be ambiguous or break existing stuff in some cases, but I can't think of any offhand. It seems like it could be quite elegant if it worked. E.g.

class C {
	@alias (.#foo.value)
	accessor value;
}
21:56
<rbuckton>
Maybe? At first glance it doesn't seem ambiguous, but I'm not 100% sure that's what I would use a leading . syntax for, if we had one. There's a lot of complexity being stuffed into that syntax. IMO, the upside of using x => ref x.#foo.value is that it depends on arrows, which are well known at this point, and ref has a lot more use cases outside of this narrow case, plus it clearly indicates what you have is a Reference. .#foo.value seems almost like partial application, except even that proposal doesn't magically handle References