RFR: 8330465: Stable Values and Collections (Internal)
Per Minborg
pminborg at openjdk.org
Tue May 14 14:14:24 UTC 2024
On Mon, 13 May 2024 12:18:28 GMT, Chen Liang <liach at openjdk.org> wrote:
>> As we need the array in both cases, how would such a solution look like without duplicating code?
>
> I was thinking about changing the StableEnumMap factory to directly take an EnumSet/BitSet indicating the indices without conversions to full objects and arrays.
Sounds like a good idea. I will fix this.
>> I see what you mean with distinct initialization logic. This is not the intended use though.
>>
>> The reason these methods exist is to avoid lambda capturing. Let's say we have a `Function<K, V>` we want to apply to a `Map<K, StableValue<V>>`. Then, retrieving a `stable = StableValue<V>` and applying `stable.computeIfUnset(() -> function.apply(key))` would capture a new `Supplier`. Another alternative would be to write imperative code similar to what is already in these methods.
>>
>> What we could do is provide factories for memorized functions (the latter described in the draft JEP at the end (https://openjdk.org/jeps/8312611) ) even though these are easy to write.
>>
>> I think what you are proposing is something like this?
>>
>>
>> Map<K, StableValue<V>> map = StableValue.ofMap(keys, k -> createV(k));
>>
>>
>> or perhaps even:
>>
>>
>> Map<K, V> map = StableValue.ofMap(keys, k -> createV(k));
>
> Yes, consider the 3 capture scenarios:
> | API | Capture frequency | Capture Impact | Code Convenience | Flexibility |
> |-----|-------------------|----------------|------------------|-------------|
> | `StableValue.ofMap(map, k -> ...)` | By accident | single capture is reused | OK | One generator for all keys |
> | `StableValue.computeIfUnset(map, key, k -> ...)` | By accident | capture happens for all access sites | somewhat ugly | Different generator for different keys |
> | `map.get(k).computeIfUnset(() -> ...)` | Always | capture happens for all access sites | OK | Different generator for different keys |
>
> Notice the `ofMap` factory is the most tolerant to faulty captures: even if it captures, the single capturing lambda is reused for all map stables, avoiding capture overheads at access sites. Given Java compiler doesn't tell user anything about captures during compilation, I think `ofMap` is a better factory to avoid accidentally writing capturing lambdas.
I see what you mean. Initially, I thought it would be easy to create memorized functions but it turned out, that was not the case if one wants to retain easy debugability etc. So, I have added a couple of factory methods including this:
/**
* {@return a new <em>memoized</em> {@linkplain Function } backed by an internal
* stable map with the provided {@code inputs} keys where the provided
* {@code original} Function will only be invoked at most once per distinct input}
*
* @param original the original Function to convert to a memoized Function
* @param inputs the potential input values to the Function
* @param <T> the type of input values
* @param <R> the return type of the function
*/
static <T, R> Function<T, R> ofFunction(Set<? extends T> inputs,
Function<? super T, ? extends R> original) {
Objects.requireNonNull(inputs);
Objects.requireNonNull(original);
...
}
-------------
PR Review Comment: https://git.openjdk.org/jdk/pull/18794#discussion_r1600035236
PR Review Comment: https://git.openjdk.org/jdk/pull/18794#discussion_r1568671241
More information about the compiler-dev
mailing list