On Wed, 17 Apr 2024 13:23:53 GMT, Maurizio Cimadamore <mcimadam...@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