-
Notifications
You must be signed in to change notification settings - Fork 113
Reflection over fields #204
Comments
Why not both well-known |
Hm. Is there a situation for which
The only thing which comes to mind is that it would expose things which ought to be implementation details, namely, the difference between |
I think that reflecting over class, and its fields before creating |
My hope was that decorators would be the basis for reflection over fields. In particular, a few years ago, @wycats proposed that we make a reflective mechanism to add fields to classes. I am very concerned about imperative mechanisms to change what happens inside a constructor later! Decorators now permit fields to be added, but only before the constructor function is born. Ultimately, I see the set of fields as a sort of implementation detail of a class, similar to whether a function is async or a generator. We aren't keeping it super secret (you can tell with function.prototype.toString), but it's really the same thing to have a class with no field declarations that creates properties in its constructor, just like the difference between async functions and Promise-returning functions is an implementation detail, and generators vs iterable-returning functions. |
It might be helpful to make a distinction between merely getting information about fields and actually changing their values or descriptors at run-time. If the native reflection API were read-only, would that mitigate some of your concerns @littledan? Here's a simple example use case for read-only reflection of public fields—it could be useful to have this facility for private fields as well: #36. But there are also use cases that require actually changing (or at least initializing) the value of private fields via reflection. I gave an example in tc39/proposal-decorators#191. Let's think about the pros and cons of a native API vs. using decorators for this. One advantage of a native API capable of both reading and writing private fields is that you would never have a situation where you're switching back and forth or combining the native API with some userland implementation. And there are already native APIs for reflection of public properties, so all reflection-related facilities would be grouped together in a consistent way. |
Note: I edited my above comment—I remembered that my first example used only first public fields. I can write a new code example using private fields if it's unclear why someone would want this feature. |
A reflection mechanism for public fields - that allows read-only access to their initializers - seems very useful to me. In the same way as i can extract a getter, i should be able to extract an initializer. I feel like a (non-Reflect, since it’s not directly a proxy trap) method on Object would work just fine - Object.getOwnFields - but the tricky part is, what would you pass into it? An instance no longer has any fields - so the only options that make sense to me are the constructor and the prototype. Since changing the prototype doesn’t change the fields, that suggests the constructor is the proper place to have the fields “live”. So, I’d expect |
|
That’s a fair point; that suggests we’d need a proxy trap, and that thus Reflect would be the place to put the api method. |
@ljharb Is it still a possibility that there might be a way to declare private fields on object literals in the future? If so, then obviously a reflection API that requires a constructor as an argument could only be used with objects created by classes and not from object literals. Also, JS has historically been more object-oriented than class-oriented, meaning among other things that an object might acquire additional properties/methods after it's instantiated, beyond what's defined in the class. I don't know if similar freedom should ever be granted to fields (especially private fields), but in the interest of designing for the future (just in case), I think it might be important to offer reflection not only for classes but also individual instances. I realize that field definitions would actually be stored in the class and not copied to each instance (which would be a silly thing for the engine to do). As a counterpoint to what I said above, it's quite reasonable to say that if you want to reflect on declarations inside a class, the class (specifically the constructor function) should be the target of your inquiry. I'm just not sure how that concept would extend to individually varying instances—trying to think ahead. |
@mbrowne we're talking only about reflecting on public fields, and it wouldn't make sense for an object literal to have public fields. There must not be any reflection on private fields :-) |
I agree with the policy of no reflection on private fields by default. But why not as an opt-in feature (where you would explicitly opt-in a particular class)? Or would you view that as redundant since it will probably already be possible with decorators? |
If one makes their own class’s private fields reflectable, then why use private fields at all? (or, why not use a decorator to make them symbols, or something) |
Here's a thought. What of a means only accessible within the class itself to get the entire collection of fields, public, private, and inherited? |
@ljharb Using a decorator to make them symbols would probably work for the use cases I'm thinking of (in fact that's probably how I would implement a Also, can anyone think of a reason why you might want to have both reflectable private fields and symbol-keyed properties (for some other purpose) within the same class? I can't think of anything and it seems a bit odd, but thought I'd ask in case I overlooked something. We can return to private fields later if that's more constructive; I didn't mean to start a distracting side discussion. |
There's a more core question that has to be answered before your question has an answer. Does making a private field reflect-able mean it is also publicly accessible? If so, then there's no difference between a Symbol-keyed field and a reflectable private field. If not, then there is a distinct difference, and we'd have to define what it means for a private field to be reflectable. |
Reflectable is publicly accessible. |
@ljharb In most cases I would agree with you, but I was thinking of a case (like testing) where reflectable would only mean visible (as in readable, but not writable). |
Publicly accessible doesn’t mean publicly writable, it means accessable. Read-only counts. |
Fair enough. Then there's no difference between a reflectable private field and a Symbol-keyed private field. |
Yes, read-only reflection is different from a field-adding API; I shouldn't falsely conflate those. But, I am missing something: what are some intended use cases for this API? |
@zenparsing How would you feel about pursuing reflection in a separate proposal? That's what we are doing for my preferred proposal in this space, decorators. |
I think the discussion here has satisfied the goals of the OP. Thanks everyone! |
I would like to follow future discussions about adding a reflection API for fields. It sounds like a separate proposal might eventually be created for that. It would be great if someone could post a comment here if/when that happens (although I realize you might not remember so I'll try to keep an eye out for future proposals regardless). |
To answer the very first question: another reason to not offer reflection on public fields is that it would reveal which constructor in the inheritance chain defines the field, which may impede refactoring. IMO it is slightly more JavaScripty to just deal with the final instances, rather than to be able to inquire in detail about the ancestry of each property. This is a very mild opinion. |
I would like to start a conversation on how we might provide a reflection mechanism over class instance fields in the future. I'm not proposing any changes for this proposal.
The general idea is that in many object oriented languages, the fields declaratively defined within a class body are available via reflection APIs. This proposal adds a
[[Fields]]
internal slot to constructors but does not provide a way to reflect over that list. I can see use cases potentially developing.First, are there compelling reasons why we would not want to provide reflection? (We can assume from the start that nothing "private" will be made available by such an API.)
Second, regardless of the answer to the first question, what might such a reflection API look like? I'll throw a couple of possibilities out there:
Reflect.fields(obj)
: Presumably this would go along with an extension of the MOP (meta-object-protocol).constructor[Symbol.fields]
: This would require no extension to the MOP.Thoughts?
The text was updated successfully, but these errors were encountered: