RFR: 8330465: Stable Values and Collections (Internal)

Chen Liang liach at openjdk.org
Tue May 14 14:14:25 UTC 2024


On Wed, 17 Apr 2024 13:23:53 GMT, Maurizio Cimadamore <mcimadamore at openjdk.org> wrote:

>> 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);
>>         ...
>>     }
>
> I agree that these method appear to be confusing. We have:
> 
> 
> StableValue::of()
> StableValue::ofList(int)
> StableValue::ofMap(Set)
> 
> 
> These methods are clearly primitives, because they are used to create a wrapper around a stable value/array. (Actually, if you squint, the primitive is really the `ofMap` factory, since that one can be used to derive the other two as well, but that's mostly a sophism).
> 
> Everything else falls in the "helper" bucket. That is, we could have:
> 
> 
> StableValue::ofList(IntFunction) -> List<V> // similar to StableValue::ofList(int)
> StableValue::ofMap(Function) -> Map<K, V> // similar to StableValue::ofMap(Set)
> 
> 
> Or, we could have:
> 
> 
> StableValue::ofSupplier(Supplier) -> Supplier<V> // similar to StableValue::of()
> StableValue::ofIntFunction(IntFunction) -> IntFunction<V> // similar to StableValue::ofList(int)
> StableValue::ofFunction(Function) -> Function<K, V> // similar to StableValue::ofMap(Set)
> 
> 
> IMHO, having both sets feel slightly redundant. That is, if you have a Map<K, V>, you also have a function from K, V - namely, map::get. And, conversely, if a client wants a List<V> of fixed size, which is populated lazily, using a memoized IntFunction is, effectively, the same thing.

I prefer these:

StableValue::ofSupplier(Supplier<V>) -> StableValue<V>
StableValue::ofIntFunction(keys, IntFunction<V>) -> IntFunction<StableValue<V>>
StableValue::ofFunction(keys, Function<K, V>) -> Function<K, StableValue<V>>

These still expose StableValue so users can set the values if they need. In addition, the List/Map functionalites are mostly useless so a getter/function suffices for the most part.

These APIs are less error-prone to accidental context capture compared to the individual use-site ones, as use-site leaks means each access involves an allocation, but the allocation for construction site is shared.

-------------

PR Review Comment: https://git.openjdk.org/jdk/pull/18794#discussion_r1568895552


More information about the compiler-dev mailing list