Deconstructor reflection Was: Re: Deconstruction patterns
Paul Sandoz
paul.sandoz at oracle.com
Wed Mar 8 17:25:42 UTC 2023
I think the current java.lang.reflect model is the path of lease resistance. (Bindings are the dual of method arguments which are also bundled the same way.)
We can leverage the runtime benefits of the carrier with method handles.
Paul.
> On Mar 8, 2023, at 9:18 AM, Brian Goetz <brian.goetz at oracle.com> wrote:
>
> That's sort of where I was coming from. For example, when we added Class::getRecordComponents (which, btw, should really be a pattern), it returns RecordComponent[] instead of List<RecordComponent>, because that's how reflection rolls. The benefit of using List (which are clearly "better" than arrays) is lost by the inconsistency of "some methods return arrays, some return lists."
>
> On 3/8/2023 12:05 PM, Dan Heidinga wrote:
>> Thanks for the response. This seems reasonable though I still have an aversion to the Object[] return type for the `match` method though it fits with the way reflection works today. I hate wasting the Carrier runtime's benefits by immediately boxing and collecting to the array for reflection but maybe anything else is trying to fix the sins of the past?
>>
>> --Dan
>>
>> On Tue, Mar 7, 2023 at 3:16 PM Brian Goetz <brian.goetz at oracle.com> wrote:
>>
>>
>> On 3/7/2023 2:51 PM, Dan Heidinga wrote:
>>>
>>> #### Reflection
>>>
>>> Since matchers are a new kind of class member, they will need a new kind of
>>> reflective object, and a method that is analogous to
>>> `Class::getConstructors`.
>>> The reflective object should extend `Executable`, as all of the existing
>>> methods
>>> on `Executable` make sense for patterns (using `Object` as the return
>>> type.) If
>>> the pattern is reflectively invoked, it returns `null` for no match, or an
>>> `Object[]` which is the boxing of the values in the carrier.
>>>
>>>
>>> This surprised me slightly and I'm not sure I follow the reasoning on why the return value would be boxed and collected into an Object[]?
>>>
>>> The design says matcher methods return Object as an opaque descriptor for the actual implementation carrier object. Yet, reflection will take that carrier object, and replace it with an equivalent Object[] resulting in more allocations and (potentially) boxing on the return path.
>>>
>>> I get it's a developer friendly approach but I wonder if it encourages the wrong mental model about what matchers return?
>>>
>>> Would it make sense to add an extra step in that process so the java.lang.reflect.Matcher instance has a `Object[] resultToArray(Object)` method? It's more ceremony (yuck) but allows avoiding the array creation and maybe the boxing on the return path for allocation-sensitive callers?
>>
>> There's a few things here, let's try to unpack them.
>>
>> I agree that the actual classfile descriptor of the synthetic method need not be all that related to what reflection does, though it is nice to minimize that difference if we can.
>>
>> Reflection routinely boxes everything; if you're working reflectively, this is just the price of entry? MethodHandles are a different story, of course, and we should be able to unreflect a Matcher to something that can be invoked directly.
>>
>> I had been assuming that `invoke` was a method on Executable, but now that I look, I realize that invocation lives on the subclasses Method and Constructor. So we have more latitude than I thought. Which nudges me a little towards
>>
>> Object[] match(Object matchTarget, Object... additionalArgs)
>>
>> where match failure is reflected as null.
>>
>> This also nudges me a bit towards hiding matchers from getMethods() and friends (as we do with constructors).
>>
>>> And speaking of MethodHandles, will there be new MethodHandles.Lookup.findMatcher , findDeconstructor, etc methods? Or do you see them being looked up with the existing find* methods?
>>
>> I had been assuming that we would use MethodHandle::findStatic for this.
>>
>>>
>>> We will then need some additional methods to describe the bindings, so the
>>> subtype of `Executable` has methods like `getBindings`,
>>> `getAnnotatedBindings`,
>>> `getGenericBindings`, `isDeconstructor`, `isPartial`, etc. These
>>> methods will
>>> decode the `Matcher` attribute and its embedded attributes.
>>>
>>> What does `getBindings` return? The MethodType describing the bindings? A Class[] describing the types of the bindings? Something else?
>>>
>>
>> Class[] getBindings();
>> Type[] getGenericBindings();
>> AnnotatedType[] getAnnotatedBindings();
>>
>>
>>
>
More information about the amber-spec-observers
mailing list