From dl at cs.oswego.edu Tue Jan 1 03:55:39 2013 From: dl at cs.oswego.edu (Doug Lea) Date: Tue, 01 Jan 2013 06:55:39 -0500 Subject: FJP.CommonPool and VM shutdown Message-ID: <50E2CEBB.8080306@cs.oswego.edu> While writing some CompletableFuture test code, I realized that there's a potential unhappy surprise lurking in code of the form: class X { public static void main(String[] args) { new ComputationUsingCommonPool(...).fork(); } } Because the common pool uses daemon threads, it is OK for the VM to shutdown immediately after the fork(). This is not at all a common usage: normally you'd use invoke() or some other construction that somehow uses the computation. But still, it is sure to be the subject of a bug report someday. There is an easy although currently non-obvious workaround: End such programs with a call to ForkJoinThread.helpQuiescePool(). This could be made a bit less obscure by adding a (basically identical) method: static void FJP.quiesceCommonPool(); and then document for use in such cases. Otherwise, I don't know of a good alternative. We cannot change to use non-daemon threads, because that would then hold up VM termination until an explicit common pool shutdown, which we don't and can't allow. We could ask that the VM implicitly add a call to quiesceCommonPool to every main(), but that would probably be seen as too disruptive and controversial considering the rare cases it applies. We could also add a shutdown hook calling quiesceCommonPool but because of the lack of ordering guarantees for hooks, this may get called after some other needed resources are blown away. Other ideas? -Doug From dl at cs.oswego.edu Tue Jan 1 06:16:26 2013 From: dl at cs.oswego.edu (Doug Lea) Date: Tue, 01 Jan 2013 09:16:26 -0500 Subject: FJP.CommonPool and VM shutdown In-Reply-To: <50E2CEBB.8080306@cs.oswego.edu> References: <50E2CEBB.8080306@cs.oswego.edu> Message-ID: <50E2EFBA.1080008@cs.oswego.edu> On 01/01/13 06:55, Doug Lea wrote: > > While writing some CompletableFuture test code, I > realized that there's a potential unhappy surprise > lurking in code of the form: > > class X { > public static void main(String[] args) { > new ComputationUsingCommonPool(...).fork(); > } > } > > > Because the common pool uses daemon threads, it is > OK for the VM to shutdown immediately after the fork(). > Solved as follows: The use cases are identical to those that, for other pools, require awaitTermination. So I added similarly-spec'ed method awaitQuiescence, plus a static quiesceCommonPool. Plus a simple tie-in so that awaitTermination relays to awaitQuiescence for the common pool (although always returns false), which eliminates need for user special casing of equivalent functionality for common vs other pools. Here's javadoc for these, plus added text for commonPool() /** * Returns the common pool instance. This pool is statically * constructed; its run state is unaffected by attempts to {@link * #shutdown} or {@link #shutdownNow}. However this pool and any * ongoing processing are automatically terminated upon program * {@link System#exit}. Any program that relies on asynchronous * task processing to complete before program termination should * invoke {@link #quiesceCommonPool}, or the timeout-based {@code * commonPool().}{@link #awaitQuiescence}, before exit. * * @return the common pool instance */ public static ForkJoinPool commonPool(); /** * Waits and/or attempts to assist performing tasks until this * pool {@link #isQuiescent} or the indicated timeout elapses. * * @param timeout the maximum time to wait * @param unit the time unit of the timeout argument * @return {@code true} if quiescent; {@code false} if the * timeout elapsed. */ public boolean awaitQuiescence(long timeout, TimeUnit unit); /** * Waits and/or attempts to assist performing tasks indefinitely * until the {@link #commonPool()} {@link #isQuiescent} */ public static void quiesceCommonPool() { common.awaitQuiescence(Long.MAX_VALUE, TimeUnit.NANOSECONDS); } /** * Blocks until all tasks have completed execution after a * shutdown request, or the timeout occurs, or the current thread * is interrupted, whichever happens first. Because the {@link * #commonPool()} never terminates until program shutdown, when * applied to the common pool, this method is equivalent to {@link * #awaitQuiescence} but always returns {@code false}. * * @param timeout the maximum time to wait * @param unit the time unit of the timeout argument * @return {@code true} if this executor terminated and * {@code false} if the timeout elapsed before termination * @throws InterruptedException if interrupted while waiting */ public boolean awaitTermination(long timeout, TimeUnit unit) From brian.goetz at oracle.com Tue Jan 1 08:42:20 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 01 Jan 2013 11:42:20 -0500 Subject: FJP.CommonPool and VM shutdown In-Reply-To: <50E2CEBB.8080306@cs.oswego.edu> References: <50E2CEBB.8080306@cs.oswego.edu> Message-ID: <50E311EC.8050800@oracle.com> OK, let me make sure I have this straight. You used to only be able to call fork from a pool thread. To get to a pool thread, you had to do pool.invoke, which means a likely-non-daemon thread is waiting for the result. Now, someone can call invoke/fork from a non-pool thread, at which point it becomes temporarily coopted to be a limited pool thread. What if, when calling invoke from a non-pool-thread, we set a threadlocal that says "I am a temporary pool thread". Forks from non-pool-threads would only be allowed if they are acting as temporary pool threads? On 1/1/2013 6:55 AM, Doug Lea wrote: > > While writing some CompletableFuture test code, I > realized that there's a potential unhappy surprise > lurking in code of the form: > > class X { > public static void main(String[] args) { > new ComputationUsingCommonPool(...).fork(); > } > } > > > Because the common pool uses daemon threads, it is > OK for the VM to shutdown immediately after the fork(). > > This is not at all a common usage: normally you'd > use invoke() or some other construction that somehow > uses the computation. But still, it is sure to be the > subject of a bug report someday. > > There is an easy although currently non-obvious > workaround: End such programs with a call to > ForkJoinThread.helpQuiescePool(). This could be > made a bit less obscure by adding a (basically > identical) method: > static void FJP.quiesceCommonPool(); > and then document for use in such cases. > > Otherwise, I don't know of a good alternative. > We cannot change to use non-daemon threads, because > that would then hold up VM termination until > an explicit common pool shutdown, which we > don't and can't allow. We could ask that the > VM implicitly add a call to quiesceCommonPool > to every main(), but that would probably be seen > as too disruptive and controversial considering > the rare cases it applies. We could also add a > shutdown hook calling quiesceCommonPool but because > of the lack of ordering guarantees for hooks, this may > get called after some other needed resources are blown away. > > Other ideas? > > -Doug > > From dl at cs.oswego.edu Tue Jan 1 08:55:59 2013 From: dl at cs.oswego.edu (Doug Lea) Date: Tue, 01 Jan 2013 11:55:59 -0500 Subject: FJP.CommonPool and VM shutdown In-Reply-To: <50E311EC.8050800@oracle.com> References: <50E2CEBB.8080306@cs.oswego.edu> <50E311EC.8050800@oracle.com> Message-ID: <50E3151F.9090702@cs.oswego.edu> On 01/01/13 11:42, Brian Goetz wrote: > OK, let me make sure I have this straight. > > You used to only be able to call fork from a pool thread. To get to a pool > thread, you had to do pool.invoke, which means a likely-non-daemon thread is > waiting for the result. > > Now, someone can call invoke/fork from a non-pool thread, at which point it > becomes temporarily coopted to be a limited pool thread. Not really. It was always the case that if you wanted to wait out a submitted async FJ task from main() (to any FJP), you had to awaitTermination. Only those few people who do this (mainly Martin Buchholz and me, writing test code!) ever had to think about it. Now there is another uncommon case along these lines, with the implicit common pool, but it can now be programmed in exactly the same way. -Doug From dl at cs.oswego.edu Tue Jan 1 10:11:14 2013 From: dl at cs.oswego.edu (Doug Lea) Date: Tue, 01 Jan 2013 13:11:14 -0500 Subject: FJP.CommonPool and VM shutdown In-Reply-To: <50E3151F.9090702@cs.oswego.edu> References: <50E2CEBB.8080306@cs.oswego.edu> <50E311EC.8050800@oracle.com> <50E3151F.9090702@cs.oswego.edu> Message-ID: <50E326C2.5010001@cs.oswego.edu> Oh, just to be extra-clear: On 01/01/13 11:55, Doug Lea wrote: > Not really. It was always the case that if you wanted to wait out > a submitted async FJ task from main() (to any FJP), you had > to awaitTermination. That is, some submitted task that you don't know about and so cannot join. > Only those few people who do this (mainly > Martin Buchholz and me, writing test code!) ever had to think > about it. Now there is another uncommon case along these lines, > with the implicit common pool, but it can now be programmed in > exactly the same way. > > -Doug > From brian.goetz at oracle.com Tue Jan 1 10:20:55 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 01 Jan 2013 13:20:55 -0500 Subject: FJP.CommonPool and VM shutdown In-Reply-To: <50E2CEBB.8080306@cs.oswego.edu> References: <50E2CEBB.8080306@cs.oswego.edu> Message-ID: <50E32907.3040805@oracle.com> I can't get too worked up about this failure mode, so I'll revert to bikeshed mode: a nicer name would have "await" in the name. On 1/1/2013 6:55 AM, Doug Lea wrote: > > While writing some CompletableFuture test code, I > realized that there's a potential unhappy surprise > lurking in code of the form: > > class X { > public static void main(String[] args) { > new ComputationUsingCommonPool(...).fork(); > } > } > > > Because the common pool uses daemon threads, it is > OK for the VM to shutdown immediately after the fork(). > > This is not at all a common usage: normally you'd > use invoke() or some other construction that somehow > uses the computation. But still, it is sure to be the > subject of a bug report someday. > > There is an easy although currently non-obvious > workaround: End such programs with a call to > ForkJoinThread.helpQuiescePool(). This could be > made a bit less obscure by adding a (basically > identical) method: > static void FJP.quiesceCommonPool(); > and then document for use in such cases. > > Otherwise, I don't know of a good alternative. > We cannot change to use non-daemon threads, because > that would then hold up VM termination until > an explicit common pool shutdown, which we > don't and can't allow. We could ask that the > VM implicitly add a call to quiesceCommonPool > to every main(), but that would probably be seen > as too disruptive and controversial considering > the rare cases it applies. We could also add a > shutdown hook calling quiesceCommonPool but because > of the lack of ordering guarantees for hooks, this may > get called after some other needed resources are blown away. > > Other ideas? > > -Doug > > From dl at cs.oswego.edu Wed Jan 2 06:53:34 2013 From: dl at cs.oswego.edu (Doug Lea) Date: Wed, 02 Jan 2013 09:53:34 -0500 Subject: FJP.CommonPool and VM shutdown In-Reply-To: <50E32907.3040805@oracle.com> References: <50E2CEBB.8080306@cs.oswego.edu> <50E32907.3040805@oracle.com> Message-ID: <50E449EE.3030509@cs.oswego.edu> On 01/01/13 13:20, Brian Goetz wrote: > I can't get too worked up about this failure mode, so I'll revert to bikeshed > mode: a nicer name would have "await" in the name. I made a small adjustment to make these more consistent. While awaitTermination and awaitQuiescence remain unchanged from version I pasted, and have the same form of requiring a timeout argument, the untimed version is now only available in its previous form of ForkJoinTask.helpQuiesce. The implementation just so happens to relay to awaitQuiescence without a timeout. Even though they are basically the same, the usage contexts differ enough to separate them in this way. Minor updates to javadocs at: http://gee.cs.oswego.edu/dl/jsr166/dist/docs/java/util/concurrent/ForkJoinPool.html (Currently, awaitQuiescence never actually blocks but only tries helping until quiescent. There are a few cases where you might want it to block, as when the last ongoing task before quiescence is itself blocking. It is (barely) possible to trap/detect this, so this might change someday.) -Doug > > On 1/1/2013 6:55 AM, Doug Lea wrote: >> >> While writing some CompletableFuture test code, I >> realized that there's a potential unhappy surprise >> lurking in code of the form: >> >> class X { >> public static void main(String[] args) { >> new ComputationUsingCommonPool(...).fork(); >> } >> } >> >> >> Because the common pool uses daemon threads, it is >> OK for the VM to shutdown immediately after the fork(). >> From brian.goetz at oracle.com Wed Jan 2 11:13:13 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 02 Jan 2013 14:13:13 -0500 Subject: Request for review: proposal for @FunctionalInterface checking In-Reply-To: <50DDFAC7.4030206@oracle.com> References: <50DDFAC7.4030206@oracle.com> Message-ID: <50E486C9.6080107@oracle.com> In Doug's "tyranny of function names", there was an appeal for a known, regularized naming convention: > Using anything other than a known regularized naming scheme > in libraries will make it impossible to ever take advantage of > language-based function type support. Perhaps @FunctionalInterface can be extended to provide this. Instead of just @FunctionalInterface interface Predicate { ... } the annotation could capture the parameter and return types as well: @FunctionalInterface(return = boolean.class, params = { Object.class }) interface Predicate { ... } (or better, some scheme that lets us encode generic signatures rather than erased ones). Then we're making a clear statement that "Predicate is a functional interface with type signature T->boolean", which would open the door to future loosenings in a less ad-hoc way? Doug, does this help at all? On 12/28/2012 3:02 PM, Joe Darcy wrote: > Hello, > > We've had some discussions internally at Oracle about adding a > FunctionalInterface annotation type to the platform and we'd now like to > get the expert group's evaluation and feedback on the proposal. > > Just as the java.lang.Override annotation type allows compile-time > checking of programmer intent to override a method, the goal for the > FunctionalInterface annotation type is to enable analogous compile-time > checking of whether or not an interface type is functional. Draft > specification: > > package java.lang; > > /** > Indicates that an interface type declaration is intended to be a > functional interface as defined by the Java Language > Specification. Conceptually, a functional interface has exactly one > abstract method. Since default methods are not abstract, any default > methods declared in an interface do not contribute to its abstract > method count. If an interface declares a method overriding one of the > public methods of java.lang.Object, that also does not count > toward the abstract method count. > > Note that instances of functional interfaces can be created with lambda > expressions, method references, or constructor references. > > If a type is annotated with this annotation type, compilers are required > to generate an error message unless: > >
    >
  • The type is an interface type and not an annotation type, enum, or > class. >
  • The annotated type satisfies the requirements of a functional > interface. >
> > @jls 9.8 Functional Interfaces > @jls 9.4.3 Interface Method Body > @jls 9.6.3.8 FunctionalInterface [Interfaces in the java.lang package > get a corresponding JLS section] > @since 1.8 > */ > @Documented > @Retention(RUNTIME) > @Target(TYPE) > @interface FunctionalInterface {} // Marker annotation > > Annotations on interfaces are *not* inherited, which is the proper > semantics in this case. A subinterface of a functional interface can > add methods and thus not itself be functional. There are some > subtleties to the definition of a functional interface, but I thought > that including those by reference to the JLS was sufficient and putting > in all the details would be more likely to confuse than clarify. > > Please send comments by January 4, 2013; thanks, > > -Joe > From brian.goetz at oracle.com Wed Jan 2 11:23:05 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 02 Jan 2013 14:23:05 -0500 Subject: Function type naming conventions Message-ID: <50E48919.2060408@oracle.com> Just to recap where we are in function type naming, we've got a few base types: Block T -> void Function T -> R Predicate T -> boolean Supplier () -> T Plus some derived convenience types: UnaryOperator extends Function BinaryOperator extends BiFunction We have arity prefixes to modify the natural arity of these: BiFunction (T,U) -> R BiPredicate (T,U) -> boolean Presumably we'll be forced into TriXxx and worse at some point, but so far we've been able to avoid that. We have a primitive specialization convention that lets you prefix {Int,Long,Double,Boolean} on front of one of these (possibly multiple times), and this gobbles the type argument that appears in the return position, or, if the return is not generic, the first type argument: IntFunction T -> int IntSupplier () -> int IntBiFunction (T,U) -> int IntBinaryOperator (int,int) -> int IntBlock int -> void So far we've got about 30 defined SAMs, and this has been mostly enough to implement our libraries, including primitive specializations. This convention is an uneasy compromise that is part and parcel of the compromise/commitment we made to nominal function types. Its a bit annoying in places, but the rules above are consistent, and the names read mostly reasonably in APIs, and better than most of the alternatives proposed, especially the fully general Function$FromIntAndDouble$ToDouble. In fact, the only type we've come across repeatedly that wants a "nice" name that doesn't fit into the above scheme is: int -> T which shows up in quite a few places. There are probably a few others but this is the biggest. (We also came across a desire for (T,int)->T in the reducer work, but that shows up in only one place.) We could treat int -> R as a specialization of Function, and extend the naming convention to handle it, or we could try to come up with a new top-level name for it. In the latter category, IndexedSupplier would work for some of the use cases (Arrays.fill, array suppliers) but not terribly well for IntStream.map(int -> T). Other ideas? From joe.darcy at oracle.com Wed Jan 2 12:29:18 2013 From: joe.darcy at oracle.com (Joe Darcy) Date: Wed, 02 Jan 2013 12:29:18 -0800 Subject: Request for review: proposal for @FunctionalInterface checking In-Reply-To: <50E486C9.6080107@oracle.com> References: <50DDFAC7.4030206@oracle.com> <50E486C9.6080107@oracle.com> Message-ID: <50E4989E.4010008@oracle.com> On 1/2/2013 11:13 AM, Brian Goetz wrote: > In Doug's "tyranny of function names", there was an appeal for a > known, regularized naming convention: > >> Using anything other than a known regularized naming scheme >> in libraries will make it impossible to ever take advantage of >> language-based function type support. > > Perhaps @FunctionalInterface can be extended to provide this. Instead > of just > > @FunctionalInterface > interface Predicate { ... } > > the annotation could capture the parameter and return types as well: > > @FunctionalInterface(return = boolean.class, > params = { Object.class }) > interface Predicate { ... } > > (or better, some scheme that lets us encode generic signatures rather > than erased ones). Hmmm. I think trying to use an annotation in this way would be very awkward and error prone since we'd basically be asking people to generate an (erased?) signature by hand. -Joe > > Then we're making a clear statement that "Predicate is a functional > interface with type signature T->boolean", which would open the door > to future loosenings in a less ad-hoc way? > > Doug, does this help at all? > > On 12/28/2012 3:02 PM, Joe Darcy wrote: >> Hello, >> >> We've had some discussions internally at Oracle about adding a >> FunctionalInterface annotation type to the platform and we'd now like to >> get the expert group's evaluation and feedback on the proposal. >> >> Just as the java.lang.Override annotation type allows compile-time >> checking of programmer intent to override a method, the goal for the >> FunctionalInterface annotation type is to enable analogous compile-time >> checking of whether or not an interface type is functional. Draft >> specification: >> >> package java.lang; >> >> /** >> Indicates that an interface type declaration is intended to be a >> functional interface as defined by the Java Language >> Specification. Conceptually, a functional interface has exactly one >> abstract method. Since default methods are not abstract, any default >> methods declared in an interface do not contribute to its abstract >> method count. If an interface declares a method overriding one of the >> public methods of java.lang.Object, that also does not count >> toward the abstract method count. >> >> Note that instances of functional interfaces can be created with lambda >> expressions, method references, or constructor references. >> >> If a type is annotated with this annotation type, compilers are required >> to generate an error message unless: >> >>
    >>
  • The type is an interface type and not an annotation type, enum, or >> class. >>
  • The annotated type satisfies the requirements of a functional >> interface. >>
>> >> @jls 9.8 Functional Interfaces >> @jls 9.4.3 Interface Method Body >> @jls 9.6.3.8 FunctionalInterface [Interfaces in the java.lang package >> get a corresponding JLS section] >> @since 1.8 >> */ >> @Documented >> @Retention(RUNTIME) >> @Target(TYPE) >> @interface FunctionalInterface {} // Marker annotation >> >> Annotations on interfaces are *not* inherited, which is the proper >> semantics in this case. A subinterface of a functional interface can >> add methods and thus not itself be functional. There are some >> subtleties to the definition of a functional interface, but I thought >> that including those by reference to the JLS was sufficient and putting >> in all the details would be more likely to confuse than clarify. >> >> Please send comments by January 4, 2013; thanks, >> >> -Joe >> From dl at cs.oswego.edu Wed Jan 2 12:38:13 2013 From: dl at cs.oswego.edu (Doug Lea) Date: Wed, 02 Jan 2013 15:38:13 -0500 Subject: Request for review: proposal for @FunctionalInterface checking In-Reply-To: <50E4989E.4010008@oracle.com> References: <50DDFAC7.4030206@oracle.com> <50E486C9.6080107@oracle.com> <50E4989E.4010008@oracle.com> Message-ID: <50E49AB5.3030808@cs.oswego.edu> On 01/02/13 15:29, Joe Darcy wrote: >> Perhaps @FunctionalInterface can be extended to provide this. Instead of just >> >> @FunctionalInterface >> interface Predicate { ... } >> >> the annotation could capture the parameter and return types as well: >> >> @FunctionalInterface(return = boolean.class, >> params = { Object.class }) >> interface Predicate { ... } >> >> (or better, some scheme that lets us encode generic signatures rather than >> erased ones). > > Hmmm. I think trying to use an annotation in this way would be very awkward and > error prone since we'd basically be asking people to generate an (erased?) > signature by hand. > > -Joe > >> >> Then we're making a clear statement that "Predicate is a functional >> interface with type signature T->boolean", which would open the door to future >> loosenings in a less ad-hoc way? >> >> Doug, does this help at all? >> I thought it was a cute parlor trick that might tide us over until there are proper function types, and would be OK as such unless people like Joe complained, which he did :-) -Doug From dl at cs.oswego.edu Wed Jan 2 13:04:48 2013 From: dl at cs.oswego.edu (Doug Lea) Date: Wed, 02 Jan 2013 16:04:48 -0500 Subject: Spliterator implementations Message-ID: <50E4A0F0.1040701@cs.oswego.edu> In the midst of trying various approaches for ConcurrentSkipList* spliterators (six or more methods supplying them!) I pasted the "All Known Implementing Classes" list from JDK7 Collection and Map javadocs and threw this list together of classes that may need spliterator implementations. (or may not, depending on defaults). As far as I know, exactly two (ArrayList and CHM) are complete and in use at the moment. In case you are interested, here it is: For top-level classes only: j.u.c/jsr166: ArrayDeque, ArrayBlockingQueue, ConcurrentLinkedDeque, ConcurrentLinkedQueue, CopyOnWriteArraySet, DelayQueue, LinkedBlockingDeque, LinkedBlockingQueue, LinkedTransferQueue, PriorityBlockingQueue, PriorityQueue, SynchronousQueue j.u: AbstractCollection, AbstractQueue, AbstractSet, EnumSet, HashSet (delegates to HashMap.keySet), LinkedHashSet (delegates to LinkedHashMap.keySet) Stack, other jdk: BeanContextSupport (delegates to HashMap.keySet) BeanContextServicesSupport (super BeanContextSupport) JobStateReasons, (super HashSet) For both top-level and subList classes j.u.c/jsr166 CopyOnWriteArrayList, j.u: AbstractList, AbstractSequentialList, ArrayList, AttributeList, LinkedList, Stack (super Vector), Vector other jdk: RoleList (super ArrayList), RoleUnresolvedList (super ArrayList), For both top-level and subSet classes: j.u.c/jsr166 ConcurrentSkipListSet, TreeSet For keySet, values and entrySet views of: j.u.c/jsr166 ConcurrentHashMap, j.u: AbstractMap, EnumMap, HashMap, Hashtable, IdentityHashMap, LinkedHashMap, (super HashMap but cannot use same Spliterator) WeakHashMap other jdk: Attributes (delegates to HashMap) AuthProvider (super Provider) PrinterStateReasons, (super HashMap) Properties, (super Hashtable) Provider (super Properties), RenderingHints (delegates to HashMap) SimpleBindings (delegates to any Map), TabularDataSupport, (delegates to UIDefaults, (super Hashtable) For keySet, values and entrySet views, plus the same for subMap views: j.u.c/jsr166 ConcurrentSkipListMap, TreeMap, From tim at peierls.net Wed Jan 2 15:32:38 2013 From: tim at peierls.net (Tim Peierls) Date: Wed, 2 Jan 2013 18:32:38 -0500 Subject: Function type naming conventions In-Reply-To: <50E48919.2060408@oracle.com> References: <50E48919.2060408@oracle.com> Message-ID: Unvarnished reactions: I still hate "Block" -- doesn't mean anything to me -- and I'm sure I'll never remember whether IntFunction is int -> int, T -> int, or int -> T, so it'll trip me up a little each time I read it. I don't love the Binary-/Bi- inconsistency, either, but I think it won't be hard to remember which goes where in that case. IndexedSupplier isn't too bad. I'd understand Indexed more readily. --tim On Wed, Jan 2, 2013 at 2:23 PM, Brian Goetz wrote: > Just to recap where we are in function type naming, we've got a few base > types: > > Block T -> void > Function T -> R > Predicate T -> boolean > Supplier () -> T > > Plus some derived convenience types: > > UnaryOperator extends Function > BinaryOperator extends BiFunction > > We have arity prefixes to modify the natural arity of these: > > BiFunction (T,U) -> R > BiPredicate (T,U) -> boolean > > Presumably we'll be forced into TriXxx and worse at some point, but so far > we've been able to avoid that. > > We have a primitive specialization convention that lets you prefix > {Int,Long,Double,Boolean} on front of one of these (possibly multiple > times), and this gobbles the type argument that appears in the return > position, or, if the return is not generic, the first type argument: > > IntFunction T -> int > IntSupplier () -> int > IntBiFunction (T,U) -> int > IntBinaryOperator (int,int) -> int > IntBlock int -> void > > So far we've got about 30 defined SAMs, and this has been mostly enough to > implement our libraries, including primitive specializations. > > This convention is an uneasy compromise that is part and parcel of the > compromise/commitment we made to nominal function types. Its a bit > annoying in places, but the rules above are consistent, and the names read > mostly reasonably in APIs, and better than most of the alternatives > proposed, especially the fully general Function$FromIntAndDouble$** > ToDouble. > > In fact, the only type we've come across repeatedly that wants a "nice" > name that doesn't fit into the above scheme is: > > int -> T > > which shows up in quite a few places. There are probably a few others but > this is the biggest. (We also came across a desire for (T,int)->T in the > reducer work, but that shows up in only one place.) > > We could treat int -> R as a specialization of Function, and extend > the naming convention to handle it, or we could try to come up with a new > top-level name for it. > > In the latter category, IndexedSupplier would work for some of the use > cases (Arrays.fill, array suppliers) but not terribly well for > IntStream.map(int -> T). > > Other ideas? > From brian.goetz at oracle.com Wed Jan 2 15:58:26 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 02 Jan 2013 18:58:26 -0500 Subject: Spliterator implementations In-Reply-To: <50E4A0F0.1040701@cs.oswego.edu> References: <50E4A0F0.1040701@cs.oswego.edu> Message-ID: <50E4C9A2.1020008@oracle.com> Thanks for making this list. There's no method for spliterator(), instead there is a method for stream() which may or may not require a spliterator. There are defaults (based on Iterator) in Collection, List, Set, and SortedSet, plus optimized implementations in ArrayList and Vector. Plus Stack inherits from Vector. From your list, there are a few that might benefit enough from a better implementation to be worth the effort, such as Enum{Set,Map}, {Hash,Tree,LinkedHash}{Set,Map}. For most of the rest (and some of these, probably), the Iterator version is probably good enough. On 1/2/2013 4:04 PM, Doug Lea wrote: > > In the midst of trying various approaches for > ConcurrentSkipList* spliterators (six or more methods > supplying them!) I pasted the "All Known Implementing Classes" > list from JDK7 Collection and Map javadocs and threw this list > together of classes that may need spliterator implementations. > (or may not, depending on defaults). > As far as I know, exactly two (ArrayList and CHM) are > complete and in use at the moment. > In case you are interested, here it is: > > > For top-level classes only: > j.u.c/jsr166: > ArrayDeque, > ArrayBlockingQueue, > ConcurrentLinkedDeque, > ConcurrentLinkedQueue, > CopyOnWriteArraySet, > DelayQueue, > LinkedBlockingDeque, > LinkedBlockingQueue, > LinkedTransferQueue, > PriorityBlockingQueue, > PriorityQueue, > SynchronousQueue > j.u: > AbstractCollection, > AbstractQueue, > AbstractSet, > EnumSet, > HashSet (delegates to HashMap.keySet), > LinkedHashSet (delegates to LinkedHashMap.keySet) > Stack, > other jdk: > BeanContextSupport (delegates to HashMap.keySet) > BeanContextServicesSupport (super BeanContextSupport) > JobStateReasons, (super HashSet) > > For both top-level and subList classes > j.u.c/jsr166 > CopyOnWriteArrayList, > j.u: > AbstractList, > AbstractSequentialList, > ArrayList, > AttributeList, > LinkedList, > Stack (super Vector), > Vector > other jdk: > RoleList (super ArrayList), > RoleUnresolvedList (super ArrayList), > For both top-level and subSet classes: > j.u.c/jsr166 > ConcurrentSkipListSet, > TreeSet > For keySet, values and entrySet views of: > j.u.c/jsr166 > ConcurrentHashMap, > j.u: > AbstractMap, > EnumMap, > HashMap, > Hashtable, > IdentityHashMap, > LinkedHashMap, (super HashMap but cannot use same Spliterator) > WeakHashMap > other jdk: > Attributes (delegates to HashMap) > AuthProvider (super Provider) > PrinterStateReasons, (super HashMap) > Properties, (super Hashtable) > Provider (super Properties), > RenderingHints (delegates to HashMap) > SimpleBindings (delegates to any Map), > TabularDataSupport, (delegates to > UIDefaults, (super Hashtable) > For keySet, values and entrySet views, plus the same for subMap views: > j.u.c/jsr166 > ConcurrentSkipListMap, > TreeMap, > > > From dl at cs.oswego.edu Wed Jan 2 17:09:04 2013 From: dl at cs.oswego.edu (Doug Lea) Date: Wed, 02 Jan 2013 20:09:04 -0500 Subject: Spliterator implementations In-Reply-To: <50E4C9A2.1020008@oracle.com> References: <50E4A0F0.1040701@cs.oswego.edu> <50E4C9A2.1020008@oracle.com> Message-ID: <50E4DA30.1050302@cs.oswego.edu> On 01/02/13 18:58, Brian Goetz wrote: > From your list, there are a few that might benefit enough from a better > implementation to be worth the effort, such as Enum{Set,Map}, > {Hash,Tree,LinkedHash}{Set,Map}. For most of the rest (and some of these, > probably), the Iterator version is probably good enough. We'd need to document goodness (or good-enough-ness) in the same way we document other algorithmic properties of JDK collections. For anything hopelessly listy (almost all queues, LinkedList, LinkedHashSet, etc) we'd want to clearly indicate O(n) splitting that would be worthwhile only if each function application to each element is very expensive. Not documenting these will surely lead to justifiable bug reports. Conversely most array-based ones (including most hash tables) can document best-case O(n/p) performance (p is parallelism level) because of constant split time. Others somewhere in the middle. Of those others, there may be those that could in principle be faster but might not be for JDK8 ship? -Doug From brian.goetz at oracle.com Wed Jan 2 17:15:42 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 02 Jan 2013 20:15:42 -0500 Subject: Spliterator implementations In-Reply-To: <50E4DA30.1050302@cs.oswego.edu> References: <50E4A0F0.1040701@cs.oswego.edu> <50E4C9A2.1020008@oracle.com> <50E4DA30.1050302@cs.oswego.edu> Message-ID: <50E4DBBE.3050307@oracle.com> > We'd need to document goodness (or good-enough-ness) in the > same way we document other algorithmic properties of JDK collections. > For anything hopelessly listy (almost all queues, LinkedList, > LinkedHashSet, etc) we'd want to clearly indicate O(n) splitting > that would be worthwhile only if each function application to each > element is very expensive. Not documenting these will surely lead > to justifiable bug reports. Right. And similarly the defaults in Collection/List/etc have this property. Note that our default "split an iterator" algorithm is not quite as bad as the obvious O(n); we serve up a sequence of increasing-sized splits, so that we can immediately generate some work to fork while the pool is warming up. So first split might be of size 1, second of size 2, etc, up until we hit some threshold and flatten out. > Conversely most array-based ones (including most hash tables) > can document best-case O(n/p) performance (p is parallelism level) > because of constant split time. Others somewhere in the middle. > Of those others, there may be those that could in principle be > faster but might not be for JDK8 ship? We can upgrade that to "definitely won't be." For example, I have a hard time seeing people using ArrayBlockingQueue as a stream source with any frequency. So while there are plenty that could in principle be faster, engineering reality suggests many never will be -- and that's OK. From dl at cs.oswego.edu Wed Jan 2 17:32:34 2013 From: dl at cs.oswego.edu (Doug Lea) Date: Wed, 02 Jan 2013 20:32:34 -0500 Subject: Spliterator implementations In-Reply-To: <50E4DBBE.3050307@oracle.com> References: <50E4A0F0.1040701@cs.oswego.edu> <50E4C9A2.1020008@oracle.com> <50E4DA30.1050302@cs.oswego.edu> <50E4DBBE.3050307@oracle.com> Message-ID: <50E4DFB2.2030109@cs.oswego.edu> On 01/02/13 20:15, Brian Goetz wrote: >> We'd need to document goodness (or good-enough-ness) in the >> same way we document other algorithmic properties of JDK collections. >> For anything hopelessly listy (almost all queues, LinkedList, >> LinkedHashSet, etc) we'd want to clearly indicate O(n) splitting >> that would be worthwhile only if each function application to each >> element is very expensive. Not documenting these will surely lead >> to justifiable bug reports. > > Right. And similarly the defaults in Collection/List/etc have this property. > > Note that our default "split an iterator" algorithm is not quite as bad as the > obvious O(n); Yeah, but it is still O(n) :-), and you can't even do that unless the spliterator/collection has a stable size/estimate. So nearly all concurrent queues, for example ConcurrentLinkedQueue, are in the really hopeless category... > > > We can upgrade that to "definitely won't be." For example, I have a hard time > seeing people using ArrayBlockingQueue as a stream source with any frequency. > So while there are plenty that could in principle be faster, engineering reality > suggests many never will be -- and that's OK. > The messiest cases are NavigableMap.subMap views (all the flavors -- ascend/descend, head/tail/range, key/value/entry). In JDK classes (TreeMap and CSLM), they don't know their sizes and unlike their top-levels, don't necessarily have regular shapes to leverage for splitting. Without some added bookkeeping that would slow down other existing usages, they might join the hopeless category. -Doug From paul.sandoz at oracle.com Thu Jan 3 01:16:44 2013 From: paul.sandoz at oracle.com (Paul Sandoz) Date: Thu, 3 Jan 2013 10:16:44 +0100 Subject: Background: pipeline architecture In-Reply-To: <50E1FF88.1080702@oracle.com> References: <50E1FF88.1080702@oracle.com> Message-ID: <85CD76D3-E918-49EE-B3ED-DEB1EB7F1878@oracle.com> On Dec 31, 2012, at 10:11 PM, Brian Goetz wrote: > The set of operations are defined in Stream for reference streams, and IntStream for int streams; each of these has a (private) implementation class {Reference,Int}Pipeline who share a (private) base class AbstractPipeline. We represent a stream pipeline as a linked list of XxxPipeline objects, where each holds an op and links to its parent. Because of the shared base class, pipelines can cross shapes and still operations can be jammed together into a single pass, such as in: > > people.stream().filter(..).map(Person::getHeight).max(); > ^Stream > ^Stream > ^IntStream > > and even though the "shape" of the data changes from reference to int we can create a single sink chain where we push Person objects in and (unboxed) ints come out. > And this is how we could plug in a map stream in the future, either one that is sugar for a holding type or something more sophisticated in terms of storage and boxing avoidance of the keys and values. Paul. From forax at univ-mlv.fr Thu Jan 3 02:52:26 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Thu, 03 Jan 2013 11:52:26 +0100 Subject: Request for review: proposal for @FunctionalInterface checking In-Reply-To: <50E4989E.4010008@oracle.com> References: <50DDFAC7.4030206@oracle.com> <50E486C9.6080107@oracle.com> <50E4989E.4010008@oracle.com> Message-ID: <50E562EA.3040300@univ-mlv.fr> On 01/02/2013 09:29 PM, Joe Darcy wrote: > On 1/2/2013 11:13 AM, Brian Goetz wrote: >> In Doug's "tyranny of function names", there was an appeal for a >> known, regularized naming convention: >> >>> Using anything other than a known regularized naming scheme >>> in libraries will make it impossible to ever take advantage of >>> language-based function type support. >> >> Perhaps @FunctionalInterface can be extended to provide this. Instead >> of just >> >> @FunctionalInterface >> interface Predicate { ... } >> >> the annotation could capture the parameter and return types as well: >> >> @FunctionalInterface(return = boolean.class, >> params = { Object.class }) >> interface Predicate { ... } >> >> (or better, some scheme that lets us encode generic signatures rather >> than erased ones). > > Hmmm. I think trying to use an annotation in this way would be very > awkward and error prone since we'd basically be asking people to > generate an (erased?) signature by hand. > > -Joe Or to let IDE do that, it's not far from generating a kind of serial UID, a functional interface UID for the interface and checks if the functional descriptor match the UID or not. I'm still not sure @FunctionalInterface is a good idea at all. R?mi > >> >> Then we're making a clear statement that "Predicate is a >> functional interface with type signature T->boolean", which would >> open the door to future loosenings in a less ad-hoc way? >> >> Doug, does this help at all? >> >> On 12/28/2012 3:02 PM, Joe Darcy wrote: >>> Hello, >>> >>> We've had some discussions internally at Oracle about adding a >>> FunctionalInterface annotation type to the platform and we'd now >>> like to >>> get the expert group's evaluation and feedback on the proposal. >>> >>> Just as the java.lang.Override annotation type allows compile-time >>> checking of programmer intent to override a method, the goal for the >>> FunctionalInterface annotation type is to enable analogous compile-time >>> checking of whether or not an interface type is functional. Draft >>> specification: >>> >>> package java.lang; >>> >>> /** >>> Indicates that an interface type declaration is intended to be a >>> functional interface as defined by the Java Language >>> Specification. Conceptually, a functional interface has exactly one >>> abstract method. Since default methods are not abstract, any default >>> methods declared in an interface do not contribute to its abstract >>> method count. If an interface declares a method overriding one of the >>> public methods of java.lang.Object, that also does not count >>> toward the abstract method count. >>> >>> Note that instances of functional interfaces can be created with lambda >>> expressions, method references, or constructor references. >>> >>> If a type is annotated with this annotation type, compilers are >>> required >>> to generate an error message unless: >>> >>>
    >>>
  • The type is an interface type and not an annotation type, enum, or >>> class. >>>
  • The annotated type satisfies the requirements of a functional >>> interface. >>>
>>> >>> @jls 9.8 Functional Interfaces >>> @jls 9.4.3 Interface Method Body >>> @jls 9.6.3.8 FunctionalInterface [Interfaces in the java.lang package >>> get a corresponding JLS section] >>> @since 1.8 >>> */ >>> @Documented >>> @Retention(RUNTIME) >>> @Target(TYPE) >>> @interface FunctionalInterface {} // Marker annotation >>> >>> Annotations on interfaces are *not* inherited, which is the proper >>> semantics in this case. A subinterface of a functional interface can >>> add methods and thus not itself be functional. There are some >>> subtleties to the definition of a functional interface, but I thought >>> that including those by reference to the JLS was sufficient and putting >>> in all the details would be more likely to confuse than clarify. >>> >>> Please send comments by January 4, 2013; thanks, >>> >>> -Joe >>> > From dl at cs.oswego.edu Thu Jan 3 03:52:32 2013 From: dl at cs.oswego.edu (Doug Lea) Date: Thu, 03 Jan 2013 06:52:32 -0500 Subject: random streams In-Reply-To: <50E1EC5B.3020201@oracle.com> References: <50E1DC68.8070405@oracle.com> <50E1DF4A.4000406@cs.oswego.edu> <50E1E4E8.9060202@oracle.com> <50E1E85A.50100@cs.oswego.edu> <50E1EC5B.3020201@oracle.com> Message-ID: <50E57100.6030703@cs.oswego.edu> On 12/31/12 14:49, Brian Goetz wrote: >> How about ONLY adding to TLR? > > Several potential objections come to mind: > - The above TLR formulation puts a ThreadLocal.get on the path to every random > number. Is that an overhead we need to impose on the serial case just so people > don't shoot themselves in the foot in the parallel case? BTW, this issue may go away. On the todo list is to place this and others into package-private j.u.c.ConsolidatedThreadLocal, and then explore adding a direct link to it in class Thread. (The consolidation will help deal with an increasing number of TLs used in basically similar ways in different classes. Some of these could use exactly the same fields so would reduce footprint.) -Doug From forax at univ-mlv.fr Thu Jan 3 05:16:22 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Thu, 03 Jan 2013 14:16:22 +0100 Subject: Function type naming conventions In-Reply-To: References: <50E48919.2060408@oracle.com> Message-ID: <50E584A6.5020400@univ-mlv.fr> On 01/03/2013 12:32 AM, Tim Peierls wrote: > Unvarnished reactions: I still hate "Block" -- doesn't mean anything > to me -- and I'm sure I'll never remember whether IntFunction is int > -> int, T -> int, or int -> T, so it'll trip me up a little each time > I read it. I don't love the Binary-/Bi- inconsistency, either, but I > think it won't be hard to remember which goes where in that case. Yesterday, I tried to replace a Function by 'Operator', and double-check my repository configuration/Eclipse configuration, etc. before I do an ls in java/util/function to find that the current name is 'UnaryOperator' and not 'Operator'. Ok, I had not fully recover from the excess of day before but I little more consistency should be good here. For me, UnaryOperator should be Operator or Function should be UnaryFunction. > > IndexedSupplier isn't too bad. I'd understand Indexed more readily. the main issue is that int can be an index or the element (of an IntStream). > > --tim R?mi > > > On Wed, Jan 2, 2013 at 2:23 PM, Brian Goetz > wrote: > > Just to recap where we are in function type naming, we've got a > few base types: > > Block T -> void > Function T -> R > Predicate T -> boolean > Supplier () -> T > > Plus some derived convenience types: > > UnaryOperator extends Function > BinaryOperator extends BiFunction > > We have arity prefixes to modify the natural arity of these: > > BiFunction (T,U) -> R > BiPredicate (T,U) -> boolean > > Presumably we'll be forced into TriXxx and worse at some point, > but so far we've been able to avoid that. > > We have a primitive specialization convention that lets you prefix > {Int,Long,Double,Boolean} on front of one of these (possibly > multiple times), and this gobbles the type argument that appears > in the return position, or, if the return is not generic, the > first type argument: > > IntFunction T -> int > IntSupplier () -> int > IntBiFunction (T,U) -> int > IntBinaryOperator (int,int) -> int > IntBlock int -> void > > So far we've got about 30 defined SAMs, and this has been mostly > enough to implement our libraries, including primitive > specializations. > > This convention is an uneasy compromise that is part and parcel of > the compromise/commitment we made to nominal function types. Its > a bit annoying in places, but the rules above are consistent, and > the names read mostly reasonably in APIs, and better than most of > the alternatives proposed, especially the fully general > Function$FromIntAndDouble$ToDouble. > > In fact, the only type we've come across repeatedly that wants a > "nice" name that doesn't fit into the above scheme is: > > int -> T > > which shows up in quite a few places. There are probably a few > others but this is the biggest. (We also came across a desire for > (T,int)->T in the reducer work, but that shows up in only one place.) > > We could treat int -> R as a specialization of Function, and > extend the naming convention to handle it, or we could try to come > up with a new top-level name for it. > > In the latter category, IndexedSupplier would work for some of > the use cases (Arrays.fill, array suppliers) but not terribly well > for IntStream.map(int -> T). > > Other ideas? > > From brian.goetz at oracle.com Thu Jan 3 09:45:21 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 03 Jan 2013 12:45:21 -0500 Subject: Function type naming conventions In-Reply-To: <50E48919.2060408@oracle.com> References: <50E48919.2060408@oracle.com> Message-ID: <50E5C3B1.8050209@oracle.com> > In fact, the only type we've come across repeatedly that wants a "nice" > name that doesn't fit into the above scheme is: > > int -> T > > which shows up in quite a few places. There are probably a few others > but this is the biggest. (We also came across a desire for (T,int)->T > in the reducer work, but that shows up in only one place.) > > We could treat int -> R as a specialization of Function, and extend > the naming convention to handle it, or we could try to come up with a > new top-level name for it. We can extend the existing naming convention to consistently handle this by: Function // T -> R IntTFunction // T -> int This is consistent with the existing convention, in that we are specializing all arguments, and then "unspecializing" the second with T. Similarly, the example that shows up in reducing integers, where we want (T,int)->void, would be TIntBiBlock Alternately we could be more cryptic and do TIFunction TIBiBlock using one-letter codes for each. Not beautiful, but workable in the relatively few cases where it shows up? From dl at cs.oswego.edu Thu Jan 3 11:12:38 2013 From: dl at cs.oswego.edu (Doug Lea) Date: Thu, 03 Jan 2013 14:12:38 -0500 Subject: Function type naming conventions In-Reply-To: <50E5C3B1.8050209@oracle.com> References: <50E48919.2060408@oracle.com> <50E5C3B1.8050209@oracle.com> Message-ID: <50E5D826.5060808@cs.oswego.edu> On 01/03/13 12:45, Brian Goetz wrote: >> In fact, the only type we've come across repeatedly that wants a "nice" >> name that doesn't fit into the above scheme is: >> >> int -> T >> >> which shows up in quite a few places. There are probably a few others >> but this is the biggest. (We also came across a desire for (T,int)->T >> in the reducer work, but that shows up in only one place.) >> >> We could treat int -> R as a specialization of Function, and extend >> the naming convention to handle it, or we could try to come up with a >> new top-level name for it. > > We can extend the existing naming convention to consistently handle this by: > > Function // T -> R > IntTFunction // T -> int > > This is consistent with the existing convention, in that we are specializing all > arguments, and then "unspecializing" the second with T. Why not be plainer about it and use FromIntFunction. People will catch on that the other one, IntFunction means ToIntFunction. Or you could redo that side of the naming pattern as FunctionToInt etc? (Although probably not all the way to renaming IntOperator as FromIntFunctionToInt...) -Doug From brian.goetz at oracle.com Thu Jan 3 11:18:33 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 03 Jan 2013 14:18:33 -0500 Subject: Function type naming conventions In-Reply-To: <50E5D826.5060808@cs.oswego.edu> References: <50E48919.2060408@oracle.com> <50E5C3B1.8050209@oracle.com> <50E5D826.5060808@cs.oswego.edu> Message-ID: <50E5D989.10909@oracle.com> > Why not be plainer about it and use FromIntFunction. > People will catch on that the other one, IntFunction means ToIntFunction. Note that IntFunction only means ToIntFunction for SAMs where the return is generic. Otherwise it means FromIntBlock. The rule we've got now specializes type parameters in order, but pretending that the return position (if generic) is first. > Or you could redo that side of the naming pattern as FunctionToInt etc? > (Although probably not all the way to renaming IntOperator as > FromIntFunctionToInt...) I played with that at first, and wasn't thrilled about how it scaled to to multiple arguments or non-Function bases: BiBlockFromTInt / FromTIntBiBlock vs the proposed TIntBiBlock which seemed more consistent with what we had so far? From brian.goetz at oracle.com Thu Jan 3 11:49:00 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 03 Jan 2013 14:49:00 -0500 Subject: Generators Message-ID: <50E5E0AC.7050701@oracle.com> I'm looking over our generators. Currently we have: repeat(T) repeat(n, T) repeatedly(Supplier) repeatedly(n, Supplier) iterate(T, UnaryOperator) cycle(Iterable) I'd like to pare this down a lot. Paul and I have been discussing techniques for making limit() more efficient, at which point the first four can collapse to generate(Supplier) since you can simulate repeat(T) with generate(() -> T) and the limited versions with generate().limit(). That leaves us with iterate(T, UnaryOperator) cycle(Iterable) and I'm thinking cycle doesn't pull its weight either. If we took that out, we'd be down to the pleasantly small set of: generate(Supplier) iterate(T, UnaryOperator) For integer generators, we have range(from, to) and I think we should add ints() -> sugar for range(0, MAX_INT) (Or should ints() be truly infinite, wrapping around?) From dl at cs.oswego.edu Thu Jan 3 12:23:34 2013 From: dl at cs.oswego.edu (Doug Lea) Date: Thu, 03 Jan 2013 15:23:34 -0500 Subject: Function type naming conventions In-Reply-To: <50E5D989.10909@oracle.com> References: <50E48919.2060408@oracle.com> <50E5C3B1.8050209@oracle.com> <50E5D826.5060808@cs.oswego.edu> <50E5D989.10909@oracle.com> Message-ID: <50E5E8C6.10506@cs.oswego.edu> On 01/03/13 14:18, Brian Goetz wrote: >> Why not be plainer about it and use FromIntFunction. >> People will catch on that the other one, IntFunction means ToIntFunction. > OK, forget I said the second part about generalizing this. I still think "FromIntFunction" is less cringe-y than hiding the "T" in the middle of "IntTFunction" :-) -Doug From brian.goetz at oracle.com Thu Jan 3 12:25:19 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 03 Jan 2013 15:25:19 -0500 Subject: Function type naming conventions In-Reply-To: <50E5E8C6.10506@cs.oswego.edu> References: <50E48919.2060408@oracle.com> <50E5C3B1.8050209@oracle.com> <50E5D826.5060808@cs.oswego.edu> <50E5D989.10909@oracle.com> <50E5E8C6.10506@cs.oswego.edu> Message-ID: <50E5E92F.9040306@oracle.com> > OK, forget I said the second part about generalizing this. > I still think "FromIntFunction" is less cringe-y than hiding > the "T" in the middle of "IntTFunction" :-) If it is the T that makes you cringe, we can use Obj: IntObjFunction From brian.goetz at oracle.com Thu Jan 3 12:41:45 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 03 Jan 2013 15:41:45 -0500 Subject: Generators In-Reply-To: <50E5E0AC.7050701@oracle.com> References: <50E5E0AC.7050701@oracle.com> Message-ID: <50E5ED09.5070303@oracle.com> Related to generators is array pre-filling. Originally I'd though we might want Arrays.fill(arr, Supplier) but that gets doubled with {fill,parallelFill} versions. A possibly more general approach is: IntStream Arrays.indexes(array) which would generate an intRange(0, array.length). Then filling becomes Arrays.indexes(array).forEach(i -> array[i] = ...) or in parallel Arrays.indexes(array).parallel().forEach(i -> array[i] = ...) This also subsumes other use cases like replaceAll, conditional replace, etc, with less library code. On 1/3/2013 2:49 PM, Brian Goetz wrote: > I'm looking over our generators. Currently we have: > > repeat(T) > repeat(n, T) > repeatedly(Supplier) > repeatedly(n, Supplier) > iterate(T, UnaryOperator) > cycle(Iterable) > > I'd like to pare this down a lot. Paul and I have been discussing > techniques for making limit() more efficient, at which point the first > four can collapse to > > generate(Supplier) > > since you can simulate repeat(T) with generate(() -> T) and the limited > versions with generate().limit(). > > That leaves us with > > iterate(T, UnaryOperator) > cycle(Iterable) > > and I'm thinking cycle doesn't pull its weight either. If we took that > out, we'd be down to the pleasantly small set of: > > generate(Supplier) > iterate(T, UnaryOperator) > > For integer generators, we have > > range(from, to) > > and I think we should add > > ints() -> sugar for range(0, MAX_INT) > > (Or should ints() be truly infinite, wrapping around?) > From sam at sampullara.com Thu Jan 3 12:44:31 2013 From: sam at sampullara.com (Sam Pullara) Date: Thu, 3 Jan 2013 12:44:31 -0800 Subject: Generators In-Reply-To: <50E5E0AC.7050701@oracle.com> References: <50E5E0AC.7050701@oracle.com> Message-ID: These look pretty reasonable to me. What is the motivation for ints()? Sam On Thu, Jan 3, 2013 at 11:49 AM, Brian Goetz wrote: > I'm looking over our generators. Currently we have: > > repeat(T) > repeat(n, T) > repeatedly(Supplier) > repeatedly(n, Supplier) > iterate(T, UnaryOperator) > cycle(Iterable) > > I'd like to pare this down a lot. Paul and I have been discussing > techniques for making limit() more efficient, at which point the first four > can collapse to > > generate(Supplier) > > since you can simulate repeat(T) with generate(() -> T) and the limited > versions with generate().limit(). > > That leaves us with > > iterate(T, UnaryOperator) > cycle(Iterable) > > and I'm thinking cycle doesn't pull its weight either. If we took that > out, we'd be down to the pleasantly small set of: > > generate(Supplier) > iterate(T, UnaryOperator) > > For integer generators, we have > > range(from, to) > > and I think we should add > > ints() -> sugar for range(0, MAX_INT) > > (Or should ints() be truly infinite, wrapping around?) > > From dl at cs.oswego.edu Thu Jan 3 12:46:33 2013 From: dl at cs.oswego.edu (Doug Lea) Date: Thu, 03 Jan 2013 15:46:33 -0500 Subject: Function type naming conventions In-Reply-To: <50E5E92F.9040306@oracle.com> References: <50E48919.2060408@oracle.com> <50E5C3B1.8050209@oracle.com> <50E5D826.5060808@cs.oswego.edu> <50E5D989.10909@oracle.com> <50E5E8C6.10506@cs.oswego.edu> <50E5E92F.9040306@oracle.com> Message-ID: <50E5EE29.4070802@cs.oswego.edu> On 01/03/13 15:25, Brian Goetz wrote: >> OK, forget I said the second part about generalizing this. >> I still think "FromIntFunction" is less cringe-y than hiding >> the "T" in the middle of "IntTFunction" :-) > > If it is the T that makes you cringe, we can use Obj: > > IntObjFunction > Which is getting pretty close to my initial suggested scheme of IntToObject (why bother with the word "function" here?) that everyone hated? And of course, if you do this, then you'd naturally do "ObjectToInt" etc. But given that no naming scheme is good in any absolute sense, even IntTFunction is probably something people could learn to live with. -Doug From brian.goetz at oracle.com Thu Jan 3 12:50:17 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 03 Jan 2013 15:50:17 -0500 Subject: Generators In-Reply-To: References: <50E5E0AC.7050701@oracle.com> Message-ID: <50E5EF09.6030008@oracle.com> Things like int sumOfFirstHundredPrimes = ints().filter(isPrime()).limit(100).sum(); where you don't know how many input elements you'll have to examine. On 1/3/2013 3:44 PM, Sam Pullara wrote: > These look pretty reasonable to me. What is the motivation for ints()? > > Sam > > On Thu, Jan 3, 2013 at 11:49 AM, Brian Goetz > wrote: > > I'm looking over our generators. Currently we have: > > repeat(T) > repeat(n, T) > repeatedly(Supplier) > repeatedly(n, Supplier) > iterate(T, UnaryOperator) > cycle(Iterable) > > I'd like to pare this down a lot. Paul and I have been discussing > techniques for making limit() more efficient, at which point the > first four can collapse to > > generate(Supplier) > > since you can simulate repeat(T) with generate(() -> T) and the > limited versions with generate().limit(). > > That leaves us with > > iterate(T, UnaryOperator) > cycle(Iterable) > > and I'm thinking cycle doesn't pull its weight either. If we took > that out, we'd be down to the pleasantly small set of: > > generate(Supplier) > iterate(T, UnaryOperator) > > For integer generators, we have > > range(from, to) > > and I think we should add > > ints() -> sugar for range(0, MAX_INT) > > (Or should ints() be truly infinite, wrapping around?) > > From joe.bowbeer at gmail.com Thu Jan 3 12:55:20 2013 From: joe.bowbeer at gmail.com (Joe Bowbeer) Date: Thu, 3 Jan 2013 12:55:20 -0800 Subject: Generators In-Reply-To: <50E5EF09.6030008@oracle.com> References: <50E5E0AC.7050701@oracle.com> <50E5EF09.6030008@oracle.com> Message-ID: To be clear: range and ints() are the only remaining int generators? There should be an iterator for stepping functions and infinite streams of 1's etc. ints() is nice anyway for samples and demos. I can't complain too much. Though the iterator form is pretty simple. I think ints() should wrap, just like the simple iterator implementation. Will the T generators allow us to generate streams of BigInteger? Those are useful for samples and demos. Generate a gazillion Fibonacci numbers. On Thu, Jan 3, 2013 at 12:50 PM, Brian Goetz wrote: > Things like > > int sumOfFirstHundredPrimes = > ints().filter(isPrime()).**limit(100).sum(); > > where you don't know how many input elements you'll have to examine. > > > > On 1/3/2013 3:44 PM, Sam Pullara wrote: > >> These look pretty reasonable to me. What is the motivation for ints()? >> >> Sam >> >> On Thu, Jan 3, 2013 at 11:49 AM, Brian Goetz > > wrote: >> >> I'm looking over our generators. Currently we have: >> >> repeat(T) >> repeat(n, T) >> repeatedly(Supplier) >> repeatedly(n, Supplier) >> iterate(T, UnaryOperator) >> cycle(Iterable) >> >> I'd like to pare this down a lot. Paul and I have been discussing >> techniques for making limit() more efficient, at which point the >> first four can collapse to >> >> generate(Supplier) >> >> since you can simulate repeat(T) with generate(() -> T) and the >> limited versions with generate().limit(). >> >> That leaves us with >> >> iterate(T, UnaryOperator) >> cycle(Iterable) >> >> and I'm thinking cycle doesn't pull its weight either. If we took >> that out, we'd be down to the pleasantly small set of: >> >> generate(Supplier) >> iterate(T, UnaryOperator) >> >> For integer generators, we have >> >> range(from, to) >> >> and I think we should add >> >> ints() -> sugar for range(0, MAX_INT) >> >> (Or should ints() be truly infinite, wrapping around?) >> >> >> From brian.goetz at oracle.com Thu Jan 3 13:00:34 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 03 Jan 2013 16:00:34 -0500 Subject: Generators In-Reply-To: References: <50E5E0AC.7050701@oracle.com> <50E5EF09.6030008@oracle.com> Message-ID: <50E5F172.7040707@oracle.com> > To be clear: range and ints() are the only remaining int generators? Ranges: there's a "stepping" version of range too. And there will be long equivalents. Repeating: there will be int versions of iterate/generate. BigIntegers: This already works: Stream range = iterate(new BI(0), bi -> bi.plus(1)) but we could add something to BigInteger too. > > There should be an iterator for stepping functions and infinite streams > of 1's etc. > > ints() is nice anyway for samples and demos. I can't complain too much. > Though the iterator form is pretty simple. I think ints() should > wrap, just like the simple iterator implementation. > > Will the T generators allow us to generate streams of BigInteger? Those > are useful for samples and demos. Generate a gazillion Fibonacci numbers. > > > On Thu, Jan 3, 2013 at 12:50 PM, Brian Goetz > wrote: > > Things like > > int sumOfFirstHundredPrimes = > ints().filter(isPrime()).__limit(100).sum(); > > where you don't know how many input elements you'll have to examine. > > > > On 1/3/2013 3:44 PM, Sam Pullara wrote: > > These look pretty reasonable to me. What is the motivation for > ints()? > > Sam > > On Thu, Jan 3, 2013 at 11:49 AM, Brian Goetz > > __>> wrote: > > I'm looking over our generators. Currently we have: > > repeat(T) > repeat(n, T) > repeatedly(Supplier) > repeatedly(n, Supplier) > iterate(T, UnaryOperator) > cycle(Iterable) > > I'd like to pare this down a lot. Paul and I have been > discussing > techniques for making limit() more efficient, at which > point the > first four can collapse to > > generate(Supplier) > > since you can simulate repeat(T) with generate(() -> T) and the > limited versions with generate().limit(). > > That leaves us with > > iterate(T, UnaryOperator) > cycle(Iterable) > > and I'm thinking cycle doesn't pull its weight either. If > we took > that out, we'd be down to the pleasantly small set of: > > generate(Supplier) > iterate(T, UnaryOperator) > > For integer generators, we have > > range(from, to) > > and I think we should add > > ints() -> sugar for range(0, MAX_INT) > > (Or should ints() be truly infinite, wrapping around?) > > > From daniel.smith at oracle.com Thu Jan 3 13:07:23 2013 From: daniel.smith at oracle.com (Dan Smith) Date: Thu, 3 Jan 2013 14:07:23 -0700 Subject: Function type naming conventions In-Reply-To: References: <50E48919.2060408@oracle.com> Message-ID: On Jan 2, 2013, at 12:23 PM, Brian Goetz wrote: > Function T -> R The fact that this is not "Function" makes me extremely happy. :-) On Jan 2, 2013, at 4:32 PM, Tim Peierls wrote: > I'm sure I'll never remember whether IntFunction is int -> int, T -> int, or int -> T, so it'll trip me up a little each time I read it. Stephen Colebourne makes a similar comment in the comments list. His suggestion is to use a different base name for functions that return primitives -- we already have "Predicate" and "Block"; now we just need "IntThingy" and "DoubleThingy". Stephen suggests "CalcInt", which I don't love, but maybe there's a word out there that nicely conveys the concept much like "Predicate"? ?Dan From joe.bowbeer at gmail.com Thu Jan 3 13:37:51 2013 From: joe.bowbeer at gmail.com (Joe Bowbeer) Date: Thu, 3 Jan 2013 13:37:51 -0800 Subject: Function type naming conventions In-Reply-To: References: <50E48919.2060408@oracle.com> Message-ID: IntFun Int is already an abbreviation, so combining Int with another abbreviation is consistent. Fun or Func; Op or Oper On Thu, Jan 3, 2013 at 1:07 PM, Dan Smith wrote: > On Jan 2, 2013, at 12:23 PM, Brian Goetz wrote: > > > Function T -> R > > The fact that this is not "Function" makes me extremely happy. :-) > > On Jan 2, 2013, at 4:32 PM, Tim Peierls wrote: > > > I'm sure I'll never remember whether IntFunction is int -> int, T -> > int, or int -> T, so it'll trip me up a little each time I read it. > > Stephen Colebourne makes a similar comment in the comments list. His > suggestion is to use a different base name for functions that return > primitives -- we already have "Predicate" and "Block"; now we just need > "IntThingy" and "DoubleThingy". Stephen suggests "CalcInt", which I don't > love, but maybe there's a word out there that nicely conveys the concept > much like "Predicate"? > > ?Dan From ali.ebrahimi1781 at gmail.com Thu Jan 3 14:13:08 2013 From: ali.ebrahimi1781 at gmail.com (Ali Ebrahimi) Date: Fri, 4 Jan 2013 02:43:08 +0430 Subject: Function type naming conventions In-Reply-To: <50E48919.2060408@oracle.com> References: <50E48919.2060408@oracle.com> Message-ID: Hi, FunctionInt T -> int IntFunction int -> R SupplierInt () -> int BiFunctionInt (T,U) -> int IntTBiFunctionInt (int,T) -> int TIntBiFunctionInt (T,int) -> int IntBinaryOperatorInt(IntIntFunctionInt) (int,int) -> int IntBlock int -> void just as OptionalInt. This is just general convention that is consistent and predictable and readable. But other option: Function.OfT2Int T -> int Function.OfInt2R int -> R Supplier.Of2Int () -> int BiFunction.OfTU2Int (T,U) -> int BiFunction.OfIntT2Int (int,T) -> int BiFunction.OfTInt2Int (T,int) -> int BinaryOperator.OfInt2Int (int,int) -> int Block.OfInt int -> void Regards, Ali On 1/2/13, Brian Goetz wrote: > Just to recap where we are in function type naming, we've got a few base > types: > > Block T -> void > Function T -> R > Predicate T -> boolean > Supplier () -> T > > Plus some derived convenience types: > > UnaryOperator extends Function > BinaryOperator extends BiFunction > > We have arity prefixes to modify the natural arity of these: > > BiFunction (T,U) -> R > BiPredicate (T,U) -> boolean > > Presumably we'll be forced into TriXxx and worse at some point, but so > far we've been able to avoid that. > > We have a primitive specialization convention that lets you prefix > {Int,Long,Double,Boolean} on front of one of these (possibly multiple > times), and this gobbles the type argument that appears in the return > position, or, if the return is not generic, the first type argument: > > IntFunction T -> int > IntSupplier () -> int > IntBiFunction (T,U) -> int > IntBinaryOperator (int,int) -> int > IntBlock int -> void > > So far we've got about 30 defined SAMs, and this has been mostly enough > to implement our libraries, including primitive specializations. > > This convention is an uneasy compromise that is part and parcel of the > compromise/commitment we made to nominal function types. Its a bit > annoying in places, but the rules above are consistent, and the names > read mostly reasonably in APIs, and better than most of the alternatives > proposed, especially the fully general Function$FromIntAndDouble$ToDouble. > > In fact, the only type we've come across repeatedly that wants a "nice" > name that doesn't fit into the above scheme is: > > int -> T > > which shows up in quite a few places. There are probably a few others > but this is the biggest. (We also came across a desire for (T,int)->T > in the reducer work, but that shows up in only one place.) > > We could treat int -> R as a specialization of Function, and extend > the naming convention to handle it, or we could try to come up with a > new top-level name for it. > > In the latter category, IndexedSupplier would work for some of the > use cases (Arrays.fill, array suppliers) but not terribly well for > IntStream.map(int -> T). > > Other ideas? > From brian.goetz at oracle.com Thu Jan 3 14:32:50 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 03 Jan 2013 17:32:50 -0500 Subject: Fwd: Forms for reduce() -- part 2 In-Reply-To: <50C65712.3070606@briangoetz.com> References: <50C65712.3070606@briangoetz.com> Message-ID: <50E60712.6010209@oracle.com> This got caught in the filters -- its old and probably out of date, but should go onto the record. -------- Original Message -------- Subject: Forms for reduce() -- part 2 Date: Mon, 10 Dec 2012 16:41:38 -0500 From: Brian Goetz To: lambda-libs-spec-experts at openjdk.java.net This second note is about the proposed overhaul of groupBy/reduceBy. I've already outlined why I don't like these methods -- they both tie the API to Map/Collection, while at the same time giving users relatively limited ability to handle more than a few simple cases. We don't *need* groupBy/reduceBy, since groupBy is just reduceBy (with the row reducer being reduce(ArrayList::new, ArrayList::add)) and reduceBy is just reduce, with a suitably complicated implementation of the reducer functions. But we don't want users to have to build their own groupBy/reduceBy out of pure reduce -- it's too much work, too error-prone, etc. We should provide canned versions. The current versions are an example of what we might do, but I think we can do much better. There is also the issue that I don't think we've correctly surfaced the issues surrounding when encounter order must be maintained, and when we can relax these for better performance. Currently, we guess whether a stream has a defined encounter order or not based on its source; Lists do, non-sorted-Sets don't. But this is only indirectly coupled to whether the user *cares* about this order or not. The "unordered()" hack is helpful, but its a hack. The second factor here is associativity. If your reducing function is merely associative, which is the only reasonable assumption, then you MUST care about order. But many are also commutative (sum, min, max). The user knows this, but the framework currently does not, so it has to be conservative. The two ways to do a mutable reduce are: - Use mutable containers for each leaf, and merge them as you go up the tree; - Use a single, shared, concurrent mutable container (like a CHM.) IF you don't care about encounter order, OR your reduction is commutative, you can use the latter approach -- which is more like a forEach than a reduce. Currently some ops have two implementations, a merging one and a contenting one, keyed off of ORDERED. This is both complex for us to maintain and also not always what the user wants. Better to let the user just say. Since all problems are solved by another layer of indirection, I will propose a new abstraction -- "tabulation". Tabulation is the process of creating a structured description of an input stream. Tabulations could describe: - groupBy -- create a Map> given a classifying function T->U - reduceBy -- create a Map given a classifying function T->U and a reducer that reduces a stream of T to V - partition -- partition elements into two lists/arrays/etc based on some predicate - materialized function application / joins -- given a function T->U, produce a Map whose keys are the values the stream and whose values are the result of applying the function - more sophistication combinations of the above, such as a two-level groupBy or reduceBy (Map>) The latter cannot be handled at all with our current tools, nor can the current tools produce a Guava Multimap instead of a HashMap, or do Don's "groupByMulti". The only benefit to the current groupBy/reduceBy tools is that they automate some of the mechanics of producing fake multimaps. And even this advantage mostly goes away with the addition of Map.{merge,compute,computeIfAbsent}. Use cases -- tabulation ----------------------- Given the following domain model, here are some use cases for tabulation: class Document { Author author(); Editor editor(); int pages(); } 1. Group documents by author -- given a Stream, produce a MapLike>. Here, the user may want control over what kind of MapLike and what kind of CollectionLike to use. 2. Group documents by author and editor -- given a Stream, produce a Map>. 3. Partition documents into collections of "shorter than 50 pages" and "longer than 50 pages". 4. Count of documents by author -- produce a Map. 5. Longest doc by author -- produce a Map. 6. Sum of pages by author -- produce a Map. 7. Authors of documents joined with additional data -- produce a Map for some function Author->V. Our current stuff can do (1), (4), (5), and (6), but not (2), (3), or (7) easily. Let's assume we have an interface that describes Reducer (takes a set of T values and produces an R.) Then we can create canned reducers, such as the Average from the previous note: static final Reducer AVERAGE = reducer(...) Then we define: interface MutableTabulator extends Reducer { } And we define combinators for tabulators like: // traditional groupBy // will want versions to default to HashMap/ArrayList static , M extends Map> Tabulator groupBy(Function classifier, Supplier mapFactory, Supplier rowFactory) { ... } // nested groupBy static > Tabulator groupBy(Function classifier, Supplier mapFactory, Tabulator downstreamTabulator) { ... } // Take a Stream and a Function and create Map static > Tabulator mappedTo(Function mapper, Supplier mapFactory) { ... } // What we were calling reduceBy // Will want other reduce variants static> Tabulator groupedReduce(Function classifier, Supplier baseFactory, BiBlock acceptElement, BiBlock combiner) { } These are easy to define and users can define their own. We'll have a dozen or two tabulator factories and combinators, not so bad, and if we forget one, no worry. Guava can easily define a groupBy that groups to a MultiMap. Etc. So, our use cases using these: 1. Group documents by author Map> m = docs.tabulate(groupBy(Document::author, HashMap::new, ArrayList::new)); 2. Group documents by author and editor Map>> m = docs.tabulate(groupBy(Document::author, HashMap::new, groupBy(Document::editor)); 3. Partition documents into collections of "shorter than 50 pages" and "longer than 50 pages" List> l = docs.tabulate(partitionBy(d -> d.pages() <= 50), ArrayList::new, ArrayList::add); 4. Count of documents by author -- produce a Map. Map m = docs.tabulate(groupedReduce(Document::author, () -> 0, (s, d) -> s + 1 Integer::plus)); 5. Longest doc by author -- produce a Map. Map m = docs.tabulate(groupedReduce(Document::author, (d1, d2) -> (d1.pages() >= d2.pages()) ? d1 : d2, (d1, d2) -> (d1.pages() >= d2.pages()) ? d1 : d2) 6. Sum of pages by author -- produce a Map. Map m = docs.tabulate(groupedReduce(Document::author, () -> 0, (s, d) -> s + d.pages() Integer::plus)); 7. Authors of documents joined with additional data -- produce a Map for some function Author->V. Map m = documents.map(Document::author).uniqueElements() .tabulate(mapJoin(a -> f(a)); Overall, I think these forms are fairly usable. There are a pile of factories and combinators for reducers/tabulators, which can be combined to do whatever you want. Users can create new ones, or can compose them into canned tabulators. The last bit is the separation of fold-style functional merging from contention-based "toss it in a big ConcurrentHashMap." I think the answer here is to have two forms of tabulators. Let's call them Tabulator and ConcurrentTabulator for sake of discussion. For each canned tabulator form, there'd be a merging and a concurrent form. Now the choice is in the user's hands; if they don't care about encounter order or have a better-than-associative combining function, they can select the latter, which is more like a forEach than a reduce. It sounds like a lot of new surface area, but it really isn't. We need a few forms of reduce, an abstraction for Reducer, an abstraction for Tabulator, and some factories and combinators which are individually trivial to write. It move a lot of the "can't define your own stream ops" problems into a domain where users can define their own reducers and tabulators. From joe.bowbeer at gmail.com Thu Jan 3 14:52:21 2013 From: joe.bowbeer at gmail.com (Joe Bowbeer) Date: Thu, 3 Jan 2013 14:52:21 -0800 Subject: random streams In-Reply-To: <50E1DC68.8070405@oracle.com> References: <50E1DC68.8070405@oracle.com> Message-ID: I don't want this ints() to be confused with the one you want to add to generators. So I would name it intStream() or randomInts(). On Dec 31, 2012 10:41 AM, "Brian Goetz" wrote: > On the list of requested stream sources is 'stream of random numbers'. > > Here's a one-line addition to Random: > > public IntStream ints() { > return PrimitiveStreams.repeatedly(**this::nextInt); > } > > Certainly the implementation is straightforward enough (modulo renaming of > PrimitiveStreams and repeatedly, which are not yet nailed down.) > > Any objections here? Clearly we'd want to support streams of ints, longs, > and doubles, so just calling it stream() is wrong; should they be called > random.ints(), or random.intStream()? > > > From kasperni at gmail.com Fri Jan 4 03:14:09 2013 From: kasperni at gmail.com (Kasper Nielsen) Date: Fri, 4 Jan 2013 12:14:09 +0100 Subject: Primitive streams In-Reply-To: <50DF344D.90208@oracle.com> References: <50DDDD0C.7030700@oracle.com> <50DF2FB3.6050405@univ-mlv.fr> <50DF344D.90208@oracle.com> Message-ID: On Sat, Dec 29, 2012 at 7:19 PM, Brian Goetz wrote: > >> > you can't define sum() on Stream > you could if you defined some kind of java.lang.Addable interface similar to java.lang.Comparable. *public* *interface* *class* Addable> { T add(T other); default T addAll(Iterable iterable) { Iterator i = iterable.iterator(); T result = i.next();// must have at least one element *while* (i.hasNext()) { result = result.add(i.next()); } *return* result; } } You could then let BigInteger, Integer, String?, ComplexNumber, AtomicLong, .... implement it like this public class Integer implements Comparable, Addable { Integer add(Integer other) { *return* *new* Integer(i + other.i); } Integer addAll(Iterable< Integer > iterable) { Iterator< Integer > i = iterable.iterator(); *int* result = i.next().i; *while* (i.hasNext()) { result += i.next().i; } *return* *new* Integer(result); } } On Stream you could then have @throws ClassCastException if elements are not mutable addable @throws NullPointerException if encountering a null Optional sum(); It would be really useful especially when working with something like BigInteger and BigDecimal. Where the addAll() could speed up adding lots of BigIntegers considerable. In a crazy world you could even use it to provide operator overloading of +. From paul.sandoz at oracle.com Fri Jan 4 04:59:17 2013 From: paul.sandoz at oracle.com (Paul Sandoz) Date: Fri, 4 Jan 2013 13:59:17 +0100 Subject: Generators In-Reply-To: <50E5F172.7040707@oracle.com> References: <50E5E0AC.7050701@oracle.com> <50E5EF09.6030008@oracle.com> <50E5F172.7040707@oracle.com> Message-ID: <84416AC7-A2AB-45F9-8D37-ED2AFE7999F7@oracle.com> On Jan 3, 2013, at 10:00 PM, Brian Goetz wrote: >> To be clear: range and ints() are the only remaining int generators? > > Ranges: there's a "stepping" version of range too. Should the upper bound of range be open or closed? For the current implementation it is open but means one can never iterate to MAX_VALUE. May be stating the obvious here but we also need to support negative ranges and step values. Paul. From joe.bowbeer at gmail.com Fri Jan 4 05:37:48 2013 From: joe.bowbeer at gmail.com (Joe Bowbeer) Date: Fri, 4 Jan 2013 05:37:48 -0800 Subject: Generators In-Reply-To: <84416AC7-A2AB-45F9-8D37-ED2AFE7999F7@oracle.com> References: <50E5E0AC.7050701@oracle.com> <50E5EF09.6030008@oracle.com> <50E5F172.7040707@oracle.com> <84416AC7-A2AB-45F9-8D37-ED2AFE7999F7@oracle.com> Message-ID: I was assuming an open range: ints().limit(25) == range(0, 25) But good point regarding MAX_VALUE. Because of this, a closed range may be a better idea? It would permit the generation of MAX_VALUE, avoiding failures like the following: range(0, Integer.MAX_VALUE + 1); // ?! Is range(0, 0) spec'd? Is range(0, -1) spec'd? On Fri, Jan 4, 2013 at 4:59 AM, Paul Sandoz wrote: > > On Jan 3, 2013, at 10:00 PM, Brian Goetz wrote: > > >> To be clear: range and ints() are the only remaining int generators? > > > > Ranges: there's a "stepping" version of range too. > > Should the upper bound of range be open or closed? For the current > implementation it is open but means one can never iterate to MAX_VALUE. > > May be stating the obvious here but we also need to support negative > ranges and step values. > > Paul. From paul.sandoz at oracle.com Fri Jan 4 05:44:52 2013 From: paul.sandoz at oracle.com (Paul Sandoz) Date: Fri, 4 Jan 2013 14:44:52 +0100 Subject: Generators In-Reply-To: References: <50E5E0AC.7050701@oracle.com> <50E5EF09.6030008@oracle.com> <50E5F172.7040707@oracle.com> <84416AC7-A2AB-45F9-8D37-ED2AFE7999F7@oracle.com> Message-ID: <69CD03CE-7D6D-41BB-BC1E-B1CA809AF1B4@oracle.com> On Jan 4, 2013, at 2:37 PM, Joe Bowbeer wrote: > I was assuming an open range: > > ints().limit(25) == range(0, 25) > Me too. > > But good point regarding MAX_VALUE. Because of this, a closed range may be > a better idea? > I prefer the correlation with limit. Perhaps we need another method rangeClosed? > It would permit the generation of MAX_VALUE, avoiding failures like the > following: > > range(0, Integer.MAX_VALUE + 1); // ?! > > > Is range(0, 0) spec'd? Returns an empty stream? > Is range(0, -1) spec'd? > Negative step value of -1 inferred? Paul. > > On Fri, Jan 4, 2013 at 4:59 AM, Paul Sandoz wrote: > >> >> On Jan 3, 2013, at 10:00 PM, Brian Goetz wrote: >> >>>> To be clear: range and ints() are the only remaining int generators? >>> >>> Ranges: there's a "stepping" version of range too. >> >> Should the upper bound of range be open or closed? For the current >> implementation it is open but means one can never iterate to MAX_VALUE. >> >> May be stating the obvious here but we also need to support negative >> ranges and step values. >> >> Paul. From dl at cs.oswego.edu Fri Jan 4 07:17:10 2013 From: dl at cs.oswego.edu (Doug Lea) Date: Fri, 04 Jan 2013 10:17:10 -0500 Subject: Map.asFunction Message-ID: <50E6F276.8050002@cs.oswego.edu> There is no Map interface method: default Function asFunction() { return { k -> get(k) }; } Did we decide not to do this or just forget to do it? -Doug From brian.goetz at oracle.com Fri Jan 4 07:29:29 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 04 Jan 2013 10:29:29 -0500 Subject: Map.asFunction In-Reply-To: <50E6F276.8050002@cs.oswego.edu> References: <50E6F276.8050002@cs.oswego.edu> Message-ID: <50E6F559.6000007@oracle.com> We didn't decide not to. (Similarly Set.asPredicate()) No objection (the body can be this::get). No objection. On 1/4/2013 10:17 AM, Doug Lea wrote: > > There is no Map interface method: > > default Function asFunction() { return { k -> get(k) }; } > > Did we decide not to do this or just forget to do it? > > -Doug From forax at univ-mlv.fr Fri Jan 4 09:17:04 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Fri, 04 Jan 2013 18:17:04 +0100 Subject: Map.asFunction In-Reply-To: <50E6F559.6000007@oracle.com> References: <50E6F276.8050002@cs.oswego.edu> <50E6F559.6000007@oracle.com> Message-ID: <50E70E90.4080706@univ-mlv.fr> On 01/04/2013 04:29 PM, Brian Goetz wrote: > We didn't decide not to. (Similarly Set.asPredicate()) No objection > (the body can be this::get). No objection. > > On 1/4/2013 10:17 AM, Doug Lea wrote: >> >> There is no Map interface method: >> >> default Function asFunction() { return { k -> get(k) }; } >> >> Did we decide not to do this or just forget to do it? >> >> -Doug Not sure it worth a method as it can be written map::get or set::contains. R?mi From dl at cs.oswego.edu Fri Jan 4 10:21:53 2013 From: dl at cs.oswego.edu (Doug Lea) Date: Fri, 04 Jan 2013 13:21:53 -0500 Subject: Map.asFunction In-Reply-To: <50E70E90.4080706@univ-mlv.fr> References: <50E6F276.8050002@cs.oswego.edu> <50E6F559.6000007@oracle.com> <50E70E90.4080706@univ-mlv.fr> Message-ID: <50E71DC1.5070002@cs.oswego.edu> On 01/04/13 12:17, Remi Forax wrote: >>> default Function asFunction() { return { k -> get(k) }; } >>> >>> Did we decide not to do this or just forget to do it? >>> > Not sure it worth a method as it can be written map::get or set::contains. > Good point. Assuming that matching and overload resolution become a little more deterministic soon :-), there's no reason to encourage people to use an explicit method for these purposes. -Doug From forax at univ-mlv.fr Fri Jan 4 10:44:13 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Fri, 04 Jan 2013 19:44:13 +0100 Subject: Map.asFunction In-Reply-To: <50E71DC1.5070002@cs.oswego.edu> References: <50E6F276.8050002@cs.oswego.edu> <50E6F559.6000007@oracle.com> <50E70E90.4080706@univ-mlv.fr> <50E71DC1.5070002@cs.oswego.edu> Message-ID: <50E722FD.2050207@univ-mlv.fr> On 01/04/2013 07:21 PM, Doug Lea wrote: > On 01/04/13 12:17, Remi Forax wrote: > >>>> default Function asFunction() { return { k -> get(k) }; } >>>> >>>> Did we decide not to do this or just forget to do it? >>>> >> Not sure it worth a method as it can be written map::get or >> set::contains. >> > > Good point. Assuming that matching and overload resolution become > a little more deterministic soon :-), always failing is deterministic :) That's said, I've not found the time to read the latest draft sent by Dan. > there's no reason to encourage > people to use an explicit method for these purposes. > > -Doug > R?mi From daniel.smith at oracle.com Fri Jan 4 14:46:35 2013 From: daniel.smith at oracle.com (Dan Smith) Date: Fri, 4 Jan 2013 15:46:35 -0700 Subject: Function type naming conventions In-Reply-To: References: <50E48919.2060408@oracle.com> Message-ID: <07F4CED6-B457-4573-B111-C7547FD15692@oracle.com> On Jan 3, 2013, at 2:07 PM, Dan Smith wrote: > I'm sure I'll never remember whether IntFunction is int -> int, T -> int, or int -> T, so it'll trip me up a little each time I read it. > > Stephen Colebourne makes a similar comment in the comments list. His suggestion is to use a different base name for functions that return primitives -- we already have "Predicate" and "Block"; now we just need "IntThingy" and "DoubleThingy". Stephen suggests "CalcInt", which I don't love, but maybe there's a word out there that nicely conveys the concept much like "Predicate"? I did a little Googling: "predicate" is a.k.a. "boolean-valued function". "Real-valued function" and "integer-valued function" are also fairly widely-used terms. But I didn't come across any more concise terms for those... ?Dan From Donald.Raab at gs.com Fri Jan 4 20:46:34 2013 From: Donald.Raab at gs.com (Raab, Donald) Date: Fri, 4 Jan 2013 23:46:34 -0500 Subject: Function type naming conventions In-Reply-To: <50E48919.2060408@oracle.com> References: <50E48919.2060408@oracle.com> Message-ID: <6712820CB52CFB4D842561213A77C05404C0C18119@GSCMAMP09EX.firmwide.corp.gs.com> My thought experiment from a month or so ago: https://github.com/goldmansachs/gs-collections-kata/tree/solutions-java8-jcf/src/test/java/blocks What we have in production today: https://github.com/goldmansachs/gs-collections/tree/master/collections-api/src/main/java/com/gs/collections/api/block The number of interfaces are growing substantially in GSC 3.0 as we finish up primitive lists, sets, bags, stacks and maps. > > Other ideas? From joe.bowbeer at gmail.com Sun Jan 6 16:06:41 2013 From: joe.bowbeer at gmail.com (Joe Bowbeer) Date: Sun, 6 Jan 2013 16:06:41 -0800 Subject: flatMap In-Reply-To: <50D21936.2020803@univ-mlv.fr> References: <50CFE070.5060705@oracle.com> <50D14A86.9050102@oracle.com> <50D21664.5090106@univ-mlv.fr> <50D2178F.5000009@oracle.com> <50D21936.2020803@univ-mlv.fr> Message-ID: Regarding flatMap makeover, I finally went through Donald's Java8 solutions for gs-collections-kata and the only thing that really confused me was flatMap, with its FlatMapper and its sink argument -- which is then used as the target for an (unordered?) forEach... Regarding the discussion above, I'm not enamored with the mapMulti or the yield names. 1. mapMulti: I think it's a disadvantage that mapMulti sounds like MultiMap. I'd rather a name that contained a term like "expand" or "explode" which describes the operation. Suggestions: mapExpand (I kind of like the cute false-symmetry with mapReduce...) mapFlatten 2. yield: AFAIK, this term is only used in the JDK in the control sense, as in Thread.yield. I think we can find a better term to indicate what is meant here, which is more like produce or supply or send. Suggestions: transfer produce --Joe On Wed, Dec 19, 2012 at 11:44 AM, Remi Forax wrote: > On 12/19/2012 08:37 PM, Brian Goetz wrote: > >> I think we should not use the name 'yield' because it can be a keyword >>> added later if we introduce coroutine, generator, etc. >>> >> >> I thought of that. Then I realized we already have "Thread.yield" among >> others in the libraries, so there's no reason to use a suboptimal name just >> to avoid using the y-word. >> >> Are you saying that yieldInto is actually a *better* name? Or are you >> just trying to avoid a possible collision? >> >> >> > I like yieldInto, now there is perhaps a better name. > > R?mi > > From forax at univ-mlv.fr Mon Jan 7 10:04:02 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Mon, 07 Jan 2013 19:04:02 +0100 Subject: Name of lambda parameters Message-ID: <50EB0E12.40301@univ-mlv.fr> (CC Alex Buckley) As noticed recently on lambda-dev mailing list, there is no way to get the parameter names of a lambda because there is no way to do reflection on a lambda (an object implementing a functional interface) to get the corresponding declared method. do we need a static method Parameter[] Method.getLambdaParameters(Object lambdaProxy) ? The fact that it takes an object and not stable value (like a class, etc) doesn't seem a good idea This may require bookkeeping when doing the functional interface conversion. By example, the generated lambda proxy can implement a non-public interface to extract this information and for the method handle proxy, it can use the internal API used by the metafactory to crack the method handle. So, like serialization, may we need to have a special empty interface NamedParameterLambda to tag these specific lambdas ? In that case, the method getLambdaParameters can be a static method of NamedParameterLambda, Parameter[] getLambdaParameters(NamedParameterLambda lambdaProxy). R?mi From brian.goetz at oracle.com Mon Jan 7 10:16:24 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 07 Jan 2013 13:16:24 -0500 Subject: Name of lambda parameters In-Reply-To: <50EB0E12.40301@univ-mlv.fr> References: <50EB0E12.40301@univ-mlv.fr> Message-ID: <50EB10F8.3020509@oracle.com> I think this goes into the general bucket of "you can't do reflection below the method level in Java", and lambda expressions are expressions and therefore below the method level. Is lambda somehow special here? On 1/7/2013 1:04 PM, Remi Forax wrote: > (CC Alex Buckley) > > As noticed recently on lambda-dev mailing list, > there is no way to get the parameter names of a lambda because > there is no way to do reflection on a lambda (an object implementing a > functional interface) to get the corresponding declared method. > > do we need a static method Parameter[] Method.getLambdaParameters(Object > lambdaProxy) ? > The fact that it takes an object and not stable value (like a class, > etc) doesn't seem a good idea > > This may require bookkeeping when doing the functional interface > conversion. > By example, the generated lambda proxy can implement a non-public > interface to extract this information and for the method handle proxy, > it can use the internal API used by the metafactory to crack the method > handle. > > So, like serialization, may we need to have a special empty interface > NamedParameterLambda to tag these specific lambdas ? In that case, the > method getLambdaParameters can be a static method of NamedParameterLambda, > Parameter[] getLambdaParameters(NamedParameterLambda lambdaProxy). > > R?mi > From jfinn at boxoftricks.org Mon Jan 7 10:29:48 2013 From: jfinn at boxoftricks.org (Jonathan Finn) Date: Mon, 7 Jan 2013 18:29:48 +0000 Subject: Function type naming conventions Message-ID: <9CB408A0-0AB3-4CEC-80B2-1539E46A838D@boxoftricks.org> On 01/03/2013 12:32 AM, Tim Peierls wrote: > Unvarnished reactions: I still hate "Block" -- doesn't mean anything > to me I know the name Block had a lot of discussion before, but I've just realised why it bothers me (and I think this point is a new one). Hope it's not completely set in stone yet... Block seems to be a category mistake: Function, Predicate etc. refer to concepts in the real world/logic/maths. But to me, 'block' means something like 'some source code between braces', so in my head it refers not to a concept, but to how the concept is implemented - and at the source code level too. LinesOfCode, anyone? 8^) As for a better name: Action was suggested before and seems good to me. An objection was it clashes with javax.swing.Action, but we already have java.awt.List and the world hasn't come to an end. Jonathan Jonathan Finn From forax at univ-mlv.fr Mon Jan 7 14:33:15 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Mon, 07 Jan 2013 23:33:15 +0100 Subject: Name of lambda parameters In-Reply-To: <50EB10F8.3020509@oracle.com> References: <50EB0E12.40301@univ-mlv.fr> <50EB10F8.3020509@oracle.com> Message-ID: <50EB4D2B.5080403@univ-mlv.fr> On 01/07/2013 07:16 PM, Brian Goetz wrote: > I think this goes into the general bucket of "you can't do reflection > below the method level in Java", and lambda expressions are > expressions and therefore below the method level. Is lambda somehow > special here? Lambda are anonymous functions, so yes, they are special. They can be called, have parameters, like methods so reflection should work otherwise you can't specify meta-protocol using lambdas and having JEE being able to use lambdas seems a good idea. R?mi > > On 1/7/2013 1:04 PM, Remi Forax wrote: >> (CC Alex Buckley) >> >> As noticed recently on lambda-dev mailing list, >> there is no way to get the parameter names of a lambda because >> there is no way to do reflection on a lambda (an object implementing a >> functional interface) to get the corresponding declared method. >> >> do we need a static method Parameter[] Method.getLambdaParameters(Object >> lambdaProxy) ? >> The fact that it takes an object and not stable value (like a class, >> etc) doesn't seem a good idea >> >> This may require bookkeeping when doing the functional interface >> conversion. >> By example, the generated lambda proxy can implement a non-public >> interface to extract this information and for the method handle proxy, >> it can use the internal API used by the metafactory to crack the method >> handle. >> >> So, like serialization, may we need to have a special empty interface >> NamedParameterLambda to tag these specific lambdas ? In that case, the >> method getLambdaParameters can be a static method of >> NamedParameterLambda, >> Parameter[] getLambdaParameters(NamedParameterLambda lambdaProxy). >> >> R?mi >> From tim at peierls.net Mon Jan 7 15:16:24 2013 From: tim at peierls.net (Tim Peierls) Date: Mon, 7 Jan 2013 18:16:24 -0500 Subject: Name of lambda parameters In-Reply-To: <50EB4D2B.5080403@univ-mlv.fr> References: <50EB0E12.40301@univ-mlv.fr> <50EB10F8.3020509@oracle.com> <50EB4D2B.5080403@univ-mlv.fr> Message-ID: On Mon, Jan 7, 2013 at 5:33 PM, Remi Forax wrote: > On 01/07/2013 07:16 PM, Brian Goetz wrote: > >> I think this goes into the general bucket of "you can't do reflection >> below the method level in Java", and lambda expressions are expressions and >> therefore below the method level. Is lambda somehow special here? >> > > Lambda are anonymous functions, so yes, they are special. They can be > called, have parameters, like methods so reflection should work otherwise > you can't specify meta-protocol using lambdas and having JEE being able to > use lambdas seems a good idea. > What does "you can't specify meta-protocol using lambdas" mean, and why is it a reason to add further complexity? Reflection for lambdas sounds like a bad idea to me. --tim From daniel.smith at oracle.com Mon Jan 7 16:28:32 2013 From: daniel.smith at oracle.com (Dan Smith) Date: Mon, 7 Jan 2013 17:28:32 -0700 Subject: Name of lambda parameters In-Reply-To: <50EB0E12.40301@univ-mlv.fr> References: <50EB0E12.40301@univ-mlv.fr> Message-ID: <0B780337-CCF2-41F9-97C4-13EDF3AE046C@oracle.com> On Jan 7, 2013, at 11:04 AM, Remi Forax wrote: > (CC Alex Buckley) > > As noticed recently on lambda-dev mailing list, > there is no way to get the parameter names of a lambda because > there is no way to do reflection on a lambda (an object implementing a functional interface) to get the corresponding declared method. Note that it _is_ possible to get to the function descriptor: Method descriptor = lambdaObj.getClass().getInterfaces()[0].getDeclaredMethods()[0]; // horribly oversimplified What you want, I gather, is to get to the "method" that corresponds to the "lambda object" itself. But note that there need not exist any such method in general. Block b1 = (String arg1) -> {}; Block b2 = (String arg2) -> {}; assert b1 == b2; // this assertion might succeed // possible implementation: class EmptyBlock implements Block { void run(String arg3) {} } Here, what is the "parameter name of" b2? That fact that the current javac implementation has a one-to-one mapping between lambda expressions and compiled methods is an implementation detail. (High-level point which is getting muddled here: lambdas are not objects that get converted to functional interface types. Lambdas are expressions that evaluate to instances of functional interfaces.) ?Dan From brian.goetz at oracle.com Mon Jan 7 17:31:42 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 07 Jan 2013 20:31:42 -0500 Subject: Yet another run at reduce Message-ID: <50EB76FE.8020505@oracle.com> I think Doug and I made some progress on reduce forms today. Recap: there are two ways to do a reduction to a mutable data structure like a Map; a classic fold (where you create lots of little maps, one per leaf of the computation tree, accumulate leaf data into them in a thread-confined manner, then merge their contents up the tree into one big map), and a concurrent bombardment (where you create one big concurrent-safe map, and blast elements in from many threads.) Call these "A" and "B". The requirement for A is that: - your combining functions are associative - you can create multiple containers - you can incorporate a new element into a container - you can merge containers in a way that satisfies the obvious distributive requirement If you meet these requirements, you can do a parallel A reduce that respects encounter order. The requirement for B is that: - your combining functions are associative and commutative - you can incorporate a new element into a container - your container support concurrent incorporation If you meet these requirements, you can do a parallel B reduce that does NOT respect order, but may be faster (or slower, if contention is high enough.) Key observation: A "B" reducer can become an "A" reducer simply by adding the merge ability, which is no harder than for regular A reducers. So rather than have A reducers and B reducers, we can have A reducers and A+B reducers. It's only marginally more work for B reducer writers. So... public interface Reducer { boolean isConcurrent(); R makeResult(); void accumulate(R result, T value); R combine(R result, R other); } A reducers return 'false' for isConcurrent; B reducers return 'true'. What was Concurrent{Reducer,Tabulator,Accumulator} goes away. Now, in addition to the various forms of reduce(), we add overloaded: reduce(Reducer) reduceUnordered(Reducer) You will get a B reduction if (a) you selected reduceUnordered and (b) your reducer is concurrent. Otherwise you will get an A reduction. This is nice because the knowledge of properties of "user doesn't care about order" and "target supports concurrent insertion" naturally live in different places; this separates them properly. The latter is a property of the reducer implementation; the former only the user knows about and therefore should live at the call site. Each contributes their bit and can mostly remain ignorant of the other; the library combines these bits, and if both are present, you get a B reduction. The reduceUnordered() method can be cast as an optimization to reduce(); it is always correct to use reduce(), but may be faster to use reduceUnordered(), as long as you are willing to forgo order and the reducer is coooperative. In neither case (assuming a properly implemented reducer) will you ever get a thread-unsafe result; if you ask for an unordered reduction with a nonconcurrent reducer, you get a safe ordered reduction instead, which might be slower (or faster.) Pros: - ConcurrentReducer goes away - .unordered() goes away - Properly separates order-sensitivity from concurrency Cons: - Two variations on various aspects of reducing are surfaced in the API (unordered and concurrent) that will make users say "huh?" (but if you get it wrong /choose to stay ignorant you still get the right answer.) Implementation coming. From forax at univ-mlv.fr Tue Jan 8 00:14:22 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Tue, 08 Jan 2013 09:14:22 +0100 Subject: Name of lambda parameters In-Reply-To: References: <50EB0E12.40301@univ-mlv.fr> <50EB10F8.3020509@oracle.com> <50EB4D2B.5080403@univ-mlv.fr> Message-ID: <50EBD55E.90003@univ-mlv.fr> On 01/08/2013 12:16 AM, Tim Peierls wrote: > On Mon, Jan 7, 2013 at 5:33 PM, Remi Forax > wrote: > > On 01/07/2013 07:16 PM, Brian Goetz wrote: > > I think this goes into the general bucket of "you can't do > reflection below the method level in Java", and lambda > expressions are expressions and therefore below the method > level. Is lambda somehow special here? > > > Lambda are anonymous functions, so yes, they are special. They can > be called, have parameters, like methods so reflection should work > otherwise you can't specify meta-protocol using lambdas and having > JEE being able to use lambdas seems a good idea. > > > What does "you can't specify meta-protocol using lambdas" mean, and > why is it a reason to add further complexity? In JEE context, a meta-protocols is specified usually by adding annotations classes or methods or method parameters to add dependency injection, SQL query mapping, JSON mapping, etc. By example, with lambdas you can write an easy to use API that let you decode JSON in a 'streamy' way, If I want to parse: { "firstName": "John", "lastName": "Smith", "age": 25, "address": { "streetAddress": "21 2nd Street", "city": "New York", "state": "NY", "postalCode": 10021 }, "phoneNumber": [ { "type": "home", "number": "212 555-1234" }, { "type": "fax", "number": "646 555-4567" } ] } JSONStream stream = ... (outputStream); stream.parse((String firstName, int age, JSONStream phoneNumber) -> { System.out.println(firstName + " " + age); phoneNumber.parse((String type, String number) -> { System.out.println(number); } }); for that, you need a way to do reflection on parameter names (and annotations). > --tim R?mi From forax at univ-mlv.fr Tue Jan 8 00:22:12 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Tue, 08 Jan 2013 09:22:12 +0100 Subject: Name of lambda parameters In-Reply-To: <0B780337-CCF2-41F9-97C4-13EDF3AE046C@oracle.com> References: <50EB0E12.40301@univ-mlv.fr> <0B780337-CCF2-41F9-97C4-13EDF3AE046C@oracle.com> Message-ID: <50EBD734.5010604@univ-mlv.fr> On 01/08/2013 01:28 AM, Dan Smith wrote: > On Jan 7, 2013, at 11:04 AM, Remi Forax wrote: > >> (CC Alex Buckley) >> >> As noticed recently on lambda-dev mailing list, >> there is no way to get the parameter names of a lambda because >> there is no way to do reflection on a lambda (an object implementing a functional interface) to get the corresponding declared method. > Note that it _is_ possible to get to the function descriptor: > > Method descriptor = lambdaObj.getClass().getInterfaces()[0].getDeclaredMethods()[0]; // horribly oversimplified > > What you want, I gather, is to get to the "method" that corresponds to the "lambda object" itself. I don't want a j.l.r.Method object, just a way to do reflection of lambda parameters. > But note that there need not exist any such method in general. > > Block b1 = (String arg1) -> {}; > Block b2 = (String arg2) -> {}; > assert b1 == b2; // this assertion might succeed > > // possible implementation: class EmptyBlock implements Block { void run(String arg3) {} } > > Here, what is the "parameter name of" b2? No, name. That's why I think that we need a marker interface to say to the compiler/runtime, I want to keep and be able to do reflection on parameter names. > > That fact that the current javac implementation has a one-to-one mapping between lambda expressions and compiled methods is an implementation detail. Yes, for default lambda. For serializable lambda it's more complicated, you can only share inside the same method. For lambda that expose parameter names, the compiler may have to maintain a 1 to 1 mapping. > > (High-level point which is getting muddled here: lambdas are not objects that get converted to functional interface types. Lambdas are expressions that evaluate to instances of functional interfaces.) Yes, exactly like methods in Java are not object but there a way to reflectively access to the parameter names. The reflection should be able to provide parameter names of the expression. > > ?Dan R?mi From tim at peierls.net Tue Jan 8 06:15:17 2013 From: tim at peierls.net (Tim Peierls) Date: Tue, 8 Jan 2013 09:15:17 -0500 Subject: Name of lambda parameters In-Reply-To: <50EBD55E.90003@univ-mlv.fr> References: <50EB0E12.40301@univ-mlv.fr> <50EB10F8.3020509@oracle.com> <50EB4D2B.5080403@univ-mlv.fr> <50EBD55E.90003@univ-mlv.fr> Message-ID: On Tue, Jan 8, 2013 at 3:14 AM, Remi Forax wrote: > On 01/07/2013 07:16 PM, Brian Goetz wrote: > >> I think this goes into the general bucket of "you can't do reflection >> below the method level in Java", and lambda expressions are expressions and >> therefore below the method level. Is lambda somehow special here? >> >> On Mon, Jan 7, 2013 at 5:33 PM, Remi Forax forax at univ-mlv.fr>> wrote: > >> Lambda are anonymous functions, so yes, they are special. They can be >> called, have parameters, like methods so reflection should work otherwise >> you can't specify meta-protocol using lambdas and having JEE being able to >> use lambdas seems a good idea. >> >> On 01/08/2013 12:16 AM, Tim Peierls wrote: > >> What does "you can't specify meta-protocol using lambdas" mean, and why >> is it a reason to add further complexity? >> > > In JEE context, a meta-protocols is specified usually by adding > annotations classes or methods or method parameters to add dependency > injection, SQL query mapping, JSON mapping, etc. > > By example, with lambdas you can write an easy to use API that let you > decode JSON in a 'streamy' way, If I want to parse: [...some JSON with firstName, age, phoneNumber, etc. > ...] > JSONStream stream = ... (outputStream); > stream.parse((String firstName, int age, JSONStream phoneNumber) -> { > System.out.println(firstName + " " + age); > phoneNumber.parse((String type, String number) -> { > System.out.println(number); > } > }); > > for that, you need a way to do reflection on parameter names (and > annotations). > If Java had function types, this would make more sense. Sounds like you want reflection to help address what you think of as a deficiency of the language. It's OK if there are still some things that can't be written more compactly with lambdas. You can already implement something like this in Java 7: stream.parse(new Object() { String firstName; int age; void parse(JSONStream phoneNumber) { System.out.println(firstName + " " + age); phoneNumber.parse(new Object() { String type; String number; void parse() { System.out.println(number); } }); } }); I don't think the added complexity of specifying reflection for lambdas justifies the marginal (at best) improvement in readability that lambdas provide in this example. --tim From tim at peierls.net Tue Jan 8 06:41:18 2013 From: tim at peierls.net (Tim Peierls) Date: Tue, 8 Jan 2013 09:41:18 -0500 Subject: Yet another run at reduce In-Reply-To: <50EB76FE.8020505@oracle.com> References: <50EB76FE.8020505@oracle.com> Message-ID: Sounds great -- I'd be interested in seeing some basic examples of both types of reducer. --tim On Mon, Jan 7, 2013 at 8:31 PM, Brian Goetz wrote: > I think Doug and I made some progress on reduce forms today. > > Recap: there are two ways to do a reduction to a mutable data structure > like a Map; a classic fold (where you create lots of little maps, one per > leaf of the computation tree, accumulate leaf data into them in a > thread-confined manner, then merge their contents up the tree into one big > map), and a concurrent bombardment (where you create one big > concurrent-safe map, and blast elements in from many threads.) Call these > "A" and "B". > > The requirement for A is that: > - your combining functions are associative > - you can create multiple containers > - you can incorporate a new element into a container > - you can merge containers in a way that satisfies the obvious > distributive requirement > > If you meet these requirements, you can do a parallel A reduce that > respects encounter order. > > The requirement for B is that: > - your combining functions are associative and commutative > - you can incorporate a new element into a container > - your container support concurrent incorporation > > If you meet these requirements, you can do a parallel B reduce that does > NOT respect order, but may be faster (or slower, if contention is high > enough.) > > Key observation: A "B" reducer can become an "A" reducer simply by adding > the merge ability, which is no harder than for regular A reducers. > > So rather than have A reducers and B reducers, we can have A reducers and > A+B reducers. It's only marginally more work for B reducer writers. > > So... > > public interface Reducer { > boolean isConcurrent(); > R makeResult(); > void accumulate(R result, T value); > R combine(R result, R other); > } > > A reducers return 'false' for isConcurrent; B reducers return 'true'. > > What was Concurrent{Reducer,Tabulator,**Accumulator} goes away. > > Now, in addition to the various forms of reduce(), we add overloaded: > > reduce(Reducer) > reduceUnordered(Reducer) > > You will get a B reduction if (a) you selected reduceUnordered and (b) > your reducer is concurrent. Otherwise you will get an A reduction. > > This is nice because the knowledge of properties of "user doesn't care > about order" and "target supports concurrent insertion" naturally live in > different places; this separates them properly. The latter is a property > of the reducer implementation; the former only the user knows about and > therefore should live at the call site. Each contributes their bit and can > mostly remain ignorant of the other; the library combines these bits, and > if both are present, you get a B reduction. > > The reduceUnordered() method can be cast as an optimization to reduce(); > it is always correct to use reduce(), but may be faster to use > reduceUnordered(), as long as you are willing to forgo order and the > reducer is coooperative. > > In neither case (assuming a properly implemented reducer) will you ever > get a thread-unsafe result; if you ask for an unordered reduction with a > nonconcurrent reducer, you get a safe ordered reduction instead, which > might be slower (or faster.) > > Pros: > - ConcurrentReducer goes away > - .unordered() goes away > - Properly separates order-sensitivity from concurrency > > Cons: > - Two variations on various aspects of reducing are surfaced in the API > (unordered and concurrent) that will make users say "huh?" (but if you get > it wrong /choose to stay ignorant you still get the right answer.) > > Implementation coming. > > From forax at univ-mlv.fr Tue Jan 8 06:40:37 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Tue, 08 Jan 2013 15:40:37 +0100 Subject: Name of lambda parameters In-Reply-To: References: <50EB0E12.40301@univ-mlv.fr> <50EB10F8.3020509@oracle.com> <50EB4D2B.5080403@univ-mlv.fr> <50EBD55E.90003@univ-mlv.fr> Message-ID: <50EC2FE5.7090601@univ-mlv.fr> On 01/08/2013 03:15 PM, Tim Peierls wrote: > On Tue, Jan 8, 2013 at 3:14 AM, Remi Forax > wrote: > > On 01/07/2013 07:16 PM, Brian Goetz wrote: > > I think this goes into the general bucket of "you can't > do reflection below the method level in Java", and > lambda expressions are expressions and therefore below the > method level. Is lambda somehow special here? > > On Mon, Jan 7, 2013 at 5:33 PM, Remi Forax >> wrote: > > Lambda are anonymous functions, so yes, they are special. They > can be called, have parameters, like methods so reflection > should work otherwise you can't specify meta-protocol using > lambdas and having JEE being able to use lambdas seems a good > idea. > > On 01/08/2013 12:16 AM, Tim Peierls wrote: > > What does "you can't specify meta-protocol using lambdas" > mean, and why is it a reason to add further complexity? > > > In JEE context, a meta-protocols is specified usually by adding > annotations classes or methods or method parameters to add > dependency injection, SQL query mapping, JSON mapping, etc. > > By example, with lambdas you can write an easy to use API that let > you decode JSON in a 'streamy' way, > > If I want to parse: [...some JSON with firstName, age, > phoneNumber, etc. ...] > JSONStream stream = ... (outputStream); > stream.parse((String firstName, int age, JSONStream phoneNumber) -> { > System.out.println(firstName + " " + age); > phoneNumber.parse((String type, String number) -> { > System.out.println(number); > } > }); > > for that, you need a way to do reflection on parameter names (and > annotations). > > > If Java had function types, this would make more sense. Sounds like > you want reflection to help address what you think of as a deficiency > of the language. No, it has nothing to do with function type. > > It's OK if there are still some things that can't be written more > compactly with lambdas. You can already implement something like this > in Java 7: > > stream.parse(new Object() { > String firstName; > int age; > void parse(JSONStream phoneNumber) { > System.out.println(firstName + " " + age); > phoneNumber.parse(new Object() { > String type; > String number; > void parse() { > System.out.println(number); > } > }); > } > }); Your example doesn't work if you want the address too (more than one sub item), and will create too many objects. > > I don't think the added complexity of specifying reflection for > lambdas justifies the marginal (at best) improvement in readability > that lambdas provide in this example. We should have a way to get lambda parameter names and parameter annotations at least just for completeness. > > --tim R?mi From dl at cs.oswego.edu Tue Jan 8 06:59:14 2013 From: dl at cs.oswego.edu (Doug Lea) Date: Tue, 08 Jan 2013 09:59:14 -0500 Subject: Yet another run at reduce In-Reply-To: <50EB76FE.8020505@oracle.com> References: <50EB76FE.8020505@oracle.com> Message-ID: <50EC3442.4090608@cs.oswego.edu> On 01/07/13 20:31, Brian Goetz wrote: > > The requirement for B is that: - your combining functions are associative and > commutative We will someday need to put out a clear story about this here and elsewhere: The requirement is that you do not care about order. Which may arise because source is unordered, destination is unordered, the op is commutative, or just because you just don't care. For example, collecting/merging elements using list-concat may be fine to do this way if you need a List but don't care about the order of elements in that list. -Doug From brian.goetz at oracle.com Tue Jan 8 07:15:26 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 08 Jan 2013 10:15:26 -0500 Subject: Yet another run at reduce In-Reply-To: References: <50EB76FE.8020505@oracle.com> Message-ID: <50EC380E.40602@oracle.com> Here's a regular reducer that does a two-level groupBy with a downstream reducer: static > Reducer groupBy(Function classifier, Supplier mapFactory, Reducer downstream) { return new MergingMapReducer(mapFactory, downstream::combine) { @Override public void accumulate(M map, T value) { K key = Objects.requireNonNull(classifier.apply(value), "element cannot be mapped to a null key"); downstream.accumulate(map.computeIfAbsent(key, k -> downstream.makeResult()), value); } }; } where this helper class defines the stuff common to all merging-map reducers: private static abstract class MergingMapReducer> implements Reducer { private final Supplier mapFactory; private final BinaryOperator mergeFunction; protected MergingMapReducer(Supplier mapFactory, BinaryOperator mergeFunction) { this.mapFactory = mapFactory; this.mergeFunction = mergeFunction; } @Override public M makeResult() { return mapFactory.get(); } protected void accumulate(M map, K key, V newValue) { map.merge(key, newValue, mergeFunction); } @Override public M combine(M map, M other) { for (Map.Entry e : other.entrySet()) map.merge(e.getKey(), e.getValue(), mergeFunction); return map; } } And here's the concurrent version: static > Reducer groupBy(Function classifier, Supplier mapFactory, Reducer downstream) { return new ConcurrentMapReducer(mapFactory, downstream::combine) { @Override public void accumulate(M map, T t) { D container = map.computeIfAbsent(classifier.apply(t), k -> downstream.makeResult()); if (downstream.isConcurrent()) { downstream.accumulate(container, t); } else { synchronized (container) { downstream.accumulate(container, t); } } } }; } Or by examples, did you mean use examples? On 1/8/2013 9:41 AM, Tim Peierls wrote: > Sounds great -- I'd be interested in seeing some basic examples of both > types of reducer. > > --tim > > On Mon, Jan 7, 2013 at 8:31 PM, Brian Goetz > wrote: > > I think Doug and I made some progress on reduce forms today. > > Recap: there are two ways to do a reduction to a mutable data > structure like a Map; a classic fold (where you create lots of > little maps, one per leaf of the computation tree, accumulate leaf > data into them in a thread-confined manner, then merge their > contents up the tree into one big map), and a concurrent bombardment > (where you create one big concurrent-safe map, and blast elements in > from many threads.) Call these "A" and "B". > > The requirement for A is that: > - your combining functions are associative > - you can create multiple containers > - you can incorporate a new element into a container > - you can merge containers in a way that satisfies the obvious > distributive requirement > > If you meet these requirements, you can do a parallel A reduce that > respects encounter order. > > The requirement for B is that: > - your combining functions are associative and commutative > - you can incorporate a new element into a container > - your container support concurrent incorporation > > If you meet these requirements, you can do a parallel B reduce that > does NOT respect order, but may be faster (or slower, if contention > is high enough.) > > Key observation: A "B" reducer can become an "A" reducer simply by > adding the merge ability, which is no harder than for regular A > reducers. > > So rather than have A reducers and B reducers, we can have A > reducers and A+B reducers. It's only marginally more work for B > reducer writers. > > So... > > public interface Reducer { > boolean isConcurrent(); > R makeResult(); > void accumulate(R result, T value); > R combine(R result, R other); > } > > A reducers return 'false' for isConcurrent; B reducers return 'true'. > > What was Concurrent{Reducer,Tabulator,__Accumulator} goes away. > > Now, in addition to the various forms of reduce(), we add overloaded: > > reduce(Reducer) > reduceUnordered(Reducer) > > You will get a B reduction if (a) you selected reduceUnordered and > (b) your reducer is concurrent. Otherwise you will get an A reduction. > > This is nice because the knowledge of properties of "user doesn't > care about order" and "target supports concurrent insertion" > naturally live in different places; this separates them properly. > The latter is a property of the reducer implementation; the former > only the user knows about and therefore should live at the call > site. Each contributes their bit and can mostly remain ignorant of > the other; the library combines these bits, and if both are present, > you get a B reduction. > > The reduceUnordered() method can be cast as an optimization to > reduce(); it is always correct to use reduce(), but may be faster to > use reduceUnordered(), as long as you are willing to forgo order and > the reducer is coooperative. > > In neither case (assuming a properly implemented reducer) will you > ever get a thread-unsafe result; if you ask for an unordered > reduction with a nonconcurrent reducer, you get a safe ordered > reduction instead, which might be slower (or faster.) > > Pros: > - ConcurrentReducer goes away > - .unordered() goes away > - Properly separates order-sensitivity from concurrency > > Cons: > - Two variations on various aspects of reducing are surfaced in > the API (unordered and concurrent) that will make users say "huh?" > (but if you get it wrong /choose to stay ignorant you still get the > right answer.) > > Implementation coming. > > From brian.goetz at oracle.com Tue Jan 8 07:17:07 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 08 Jan 2013 10:17:07 -0500 Subject: Yet another run at reduce In-Reply-To: <50EC3442.4090608@cs.oswego.edu> References: <50EB76FE.8020505@oracle.com> <50EC3442.4090608@cs.oswego.edu> Message-ID: <50EC3873.8010304@oracle.com> Yes, and I think this captures all the reasons why you would use an unordered reduce. Having one abstraction as we do (Stream) presents a documentation challenge, because a lot of the important concepts (stream flags, ordering, non-interference) don't have a really obvious place to be documented. But we'll have to do it anyway. On 1/8/2013 9:59 AM, Doug Lea wrote: > On 01/07/13 20:31, Brian Goetz wrote: >> >> The requirement for B is that: - your combining functions are >> associative and >> commutative > > We will someday need to put out a clear story about this > here and elsewhere: The requirement is that you do not care about > order. Which may arise because source is unordered, destination > is unordered, the op is commutative, or just because you just > don't care. For example, collecting/merging elements using > list-concat may be fine to do this way if you need a List but > don't care about the order of elements in that list. > > > -Doug > > From tim at peierls.net Tue Jan 8 07:32:41 2013 From: tim at peierls.net (Tim Peierls) Date: Tue, 8 Jan 2013 10:32:41 -0500 Subject: Yet another run at reduce In-Reply-To: <50EC380E.40602@oracle.com> References: <50EB76FE.8020505@oracle.com> <50EC380E.40602@oracle.com> Message-ID: On Tue, Jan 8, 2013 at 10:15 AM, Brian Goetz wrote: > Here's a regular reducer that does a two-level groupBy with a downstream > reducer: > Thanks. I was kind of hoping for a longer on-ramp, but I can (just barely) follow this. :-) > Or by examples, did you mean use examples? No, I wanted to see an impl. --tim From brian.goetz at oracle.com Tue Jan 8 08:18:49 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 08 Jan 2013 11:18:49 -0500 Subject: Yet another run at reduce In-Reply-To: References: <50EB76FE.8020505@oracle.com> <50EC380E.40602@oracle.com> Message-ID: <50EC46E9.9030907@oracle.com> OK, here's a longer on-ramp. The basic form of groupBy is: Map> groupBy(Function classifier) Which we treat as a special case of the groupBy form that lets you provide factories for the result (a Map) and the per-key containers (Collection): groupBy(classifier, HashMap::new, ArrayList::new) Which we treat as a special case of a cascaded reduce, where the downstream reducer creates and inserts into collections using the provided factories: Reducer groupBy(Function classifier, Supplier mapFactory, Supplier rowFactory) { return groupBy(classifier, mapFactory, intoCollection(rowFactory)); } Where intoCollection is a simple Reducer: Reducer intoCollection(Supplier collectionFactory) { return leftCombiningReducer(collectionFactory, Collection::add, Collection::addAll); } Which we feed into the cascaded reduce that I posted in the last one. I like to think of this in terms of the "marble maze" toys we played with as kids. I had one with a cool "black box" where marbles would go in the top slot and would come out one of several holes at the bottom based on internal flip-flops. The groupBy reducer is like one of those; think of that black box as the classifying function, and hook up each of the output holes to another downstream reducing box. If you have enough boxes (and enough gravitational potential) you can keep cascading these. Said boxes can do classification into Maps, partitioning, etc. At the bottom, the simplest form of reducing box is an actual box which collects the marbles. Which is what intoCollection() does. (Marble mazes had both List and Set forms for collecting marbles too.) On 1/8/2013 10:32 AM, Tim Peierls wrote: > On Tue, Jan 8, 2013 at 10:15 AM, Brian Goetz > wrote: > > Here's a regular reducer that does a two-level groupBy with a > downstream reducer: > > > Thanks. I was kind of hoping for a longer on-ramp, but I can (just > barely) follow this. :-) > > Or by examples, did you mean use examples? > > > No, I wanted to see an impl. > > --tim From tim at peierls.net Tue Jan 8 08:37:56 2013 From: tim at peierls.net (Tim Peierls) Date: Tue, 8 Jan 2013 11:37:56 -0500 Subject: Yet another run at reduce In-Reply-To: <50EC46E9.9030907@oracle.com> References: <50EB76FE.8020505@oracle.com> <50EC380E.40602@oracle.com> <50EC46E9.9030907@oracle.com> Message-ID: On Tue, Jan 8, 2013 at 11:18 AM, Brian Goetz wrote: > Where intoCollection is a simple Reducer: > > Reducer intoCollection(Supplier collectionFactory) { > return leftCombiningReducer(**collectionFactory, Collection::add, > Collection::addAll); > } > And leftCombiningReducer builds a reducer out of its arguments, which correspond to makeResult, accumulate, and combine? --tim From brian.goetz at oracle.com Tue Jan 8 08:41:37 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 08 Jan 2013 11:41:37 -0500 Subject: Yet another run at reduce In-Reply-To: References: <50EB76FE.8020505@oracle.com> <50EC380E.40602@oracle.com> <50EC46E9.9030907@oracle.com> Message-ID: <50EC4C41.5090704@oracle.com> Yep. The "leftCombining" part is simply adapting a BiBlock to a BiFunction: (t, u) -> { block.accept(t,u); return t; } This particular rough edge is one that maybe we can file down. On 1/8/2013 11:37 AM, Tim Peierls wrote: > On Tue, Jan 8, 2013 at 11:18 AM, Brian Goetz > wrote: > > Where intoCollection is a simple Reducer: > > Reducer intoCollection(Supplier collectionFactory) { > return leftCombiningReducer(__collectionFactory, > Collection::add, Collection::addAll); > } > > > And leftCombiningReducer builds a reducer out of its arguments, which > correspond to makeResult, accumulate, and combine? > > --tim From tim at peierls.net Tue Jan 8 08:43:22 2013 From: tim at peierls.net (Tim Peierls) Date: Tue, 8 Jan 2013 11:43:22 -0500 Subject: Name of lambda parameters In-Reply-To: <50EC2FE5.7090601@univ-mlv.fr> References: <50EB0E12.40301@univ-mlv.fr> <50EB10F8.3020509@oracle.com> <50EB4D2B.5080403@univ-mlv.fr> <50EBD55E.90003@univ-mlv.fr> <50EC2FE5.7090601@univ-mlv.fr> Message-ID: On Tue, Jan 8, 2013 at 9:40 AM, Remi Forax wrote: > We should have a way to get lambda parameter names and parameter > annotations at least just for completeness. > I disagree. "Just for completeness" is not compelling. --tim From brian.goetz at oracle.com Tue Jan 8 08:53:01 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 08 Jan 2013 11:53:01 -0500 Subject: Name of lambda parameters In-Reply-To: References: <50EB0E12.40301@univ-mlv.fr> <50EB10F8.3020509@oracle.com> <50EB4D2B.5080403@univ-mlv.fr> <50EBD55E.90003@univ-mlv.fr> <50EC2FE5.7090601@univ-mlv.fr> Message-ID: <50EC4EED.9070705@oracle.com> Also, this doesn't really "complete" anything. All it does is kick the "why can't we" questions down the road -- "why can't we annotate lambdas, we can annotate methods?" (And, if we try to "complete" that, it will be "why are lambda expressions special, and not all expressions?") This creates more incompleteness than it addresses. On 1/8/2013 11:43 AM, Tim Peierls wrote: > On Tue, Jan 8, 2013 at 9:40 AM, Remi Forax > wrote: > > We should have a way to get lambda parameter names and parameter > annotations at least just for completeness. > > > I disagree. "Just for completeness" is not compelling. > > --tim From brian.goetz at oracle.com Tue Jan 8 09:08:14 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 08 Jan 2013 12:08:14 -0500 Subject: Yet another run at reduce In-Reply-To: References: <50EB76FE.8020505@oracle.com> <50EC380E.40602@oracle.com> <50EC46E9.9030907@oracle.com> Message-ID: <50EC527E.8000003@oracle.com> And then... into(collection) becomes reduce(intoList()) reduce(intoSet()) reduce(intoCollection(ArrayList::new)) where intoList() could be as simple as: public static Reducer> intoList() { return intoCollection(ArrayList::new); } and intoCollection is as shown below. Then into() goes away. Then sequential() goes away. On 1/8/2013 11:37 AM, Tim Peierls wrote: > On Tue, Jan 8, 2013 at 11:18 AM, Brian Goetz > wrote: > > Where intoCollection is a simple Reducer: > > Reducer intoCollection(Supplier collectionFactory) { > return leftCombiningReducer(__collectionFactory, > Collection::add, Collection::addAll); > } > > > And leftCombiningReducer builds a reducer out of its arguments, which > correspond to makeResult, accumulate, and combine? > > --tim From brian.goetz at oracle.com Tue Jan 8 09:12:04 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 08 Jan 2013 12:12:04 -0500 Subject: mappedTo -> join? Message-ID: <50EC5364.7090803@oracle.com> One of the reducers we've got takes a Stream and a function T->U and creates a Map. Currently we call this "mappedTo". Should this be called "join" instead? Or mapJoin? Map map = people.reduce(mappedTo(Person::getAge))); vs Map map = people.reduce(join(Person::getAge))); Map map = people.reduce(mapJoin(Person::getAge))); From dl at cs.oswego.edu Tue Jan 8 09:13:04 2013 From: dl at cs.oswego.edu (Doug Lea) Date: Tue, 08 Jan 2013 12:13:04 -0500 Subject: Yet another run at reduce In-Reply-To: <50EC4C41.5090704@oracle.com> References: <50EB76FE.8020505@oracle.com> <50EC380E.40602@oracle.com> <50EC46E9.9030907@oracle.com> <50EC4C41.5090704@oracle.com> Message-ID: <50EC53A0.1010603@cs.oswego.edu> On 01/08/13 11:41, Brian Goetz wrote: > Yep. The "leftCombining" part is simply adapting a BiBlock to a BiFunction: > > (t, u) -> { block.accept(t,u); return t; } Deja vu from last week with CompletableFuture where I ended up supporting multiple distinct forms. Doing which depends on your audience's tolerance for inescapable goopy adapters vs adding multiple versions of a method that avoid the need for them. For j.u.c audience it wasn't a hard decision, and people told me that the resulting API is a little more user-friendly/decodable anyway. -Doug From forax at univ-mlv.fr Tue Jan 8 09:48:36 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Tue, 08 Jan 2013 18:48:36 +0100 Subject: Name of lambda parameters In-Reply-To: <50EC4EED.9070705@oracle.com> References: <50EB0E12.40301@univ-mlv.fr> <50EB10F8.3020509@oracle.com> <50EB4D2B.5080403@univ-mlv.fr> <50EBD55E.90003@univ-mlv.fr> <50EC2FE5.7090601@univ-mlv.fr> <50EC4EED.9070705@oracle.com> Message-ID: <50EC5BF4.5070103@univ-mlv.fr> On 01/08/2013 05:53 PM, Brian Goetz wrote: > Also, this doesn't really "complete" anything. JDK8 allows to do reflection on method parameters but not on lambda parameters, looks like a hole in the spec. > All it does is kick the "why can't we" questions down the road -- "why > can't we annotate lambdas, we can annotate methods?" "why lambdas we already have inner classes" ... And I don't propose to annotate lambda parameters. They should be annotated even if there is no way have the annotation at runtime. Otherwise, most all type checkers of JSR 308 that relies on the fact that you always can annotate the variable/parameter definitions will break. > (And, if we try to "complete" that, it will be "why are lambda > expressions special, and not all expressions?") This creates more > incompleteness than it addresses. because there is two parts in a lambda, the external part is an expression and should not be annotatable, the internal part is an anonymous function and like any other way to declare parameters in Java, should be annotatable. R?mi > > On 1/8/2013 11:43 AM, Tim Peierls wrote: >> On Tue, Jan 8, 2013 at 9:40 AM, Remi Forax > > wrote: >> >> We should have a way to get lambda parameter names and parameter >> annotations at least just for completeness. >> >> >> I disagree. "Just for completeness" is not compelling. >> >> --tim From forax at univ-mlv.fr Tue Jan 8 10:11:36 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Tue, 08 Jan 2013 19:11:36 +0100 Subject: mappedTo -> join? In-Reply-To: <50EC5364.7090803@oracle.com> References: <50EC5364.7090803@oracle.com> Message-ID: <50EC6158.3000000@univ-mlv.fr> On 01/08/2013 06:12 PM, Brian Goetz wrote: > One of the reducers we've got takes a Stream and a function T->U > and creates a Map. Currently we call this "mappedTo". Should > this be called "join" instead? Or mapJoin? > > Map map = people.reduce(mappedTo(Person::getAge))); > vs > Map map = people.reduce(join(Person::getAge))); > Map map = people.reduce(mapJoin(Person::getAge))); or just toMap, Map map = people.reduce(Reducer.toMap(Person::getAge))); R?mi From tim at peierls.net Tue Jan 8 10:19:06 2013 From: tim at peierls.net (Tim Peierls) Date: Tue, 8 Jan 2013 13:19:06 -0500 Subject: mappedTo -> join? In-Reply-To: <50EC6158.3000000@univ-mlv.fr> References: <50EC5364.7090803@oracle.com> <50EC6158.3000000@univ-mlv.fr> Message-ID: toMap works for me. join is already too overloaded. On Tue, Jan 8, 2013 at 1:11 PM, Remi Forax wrote: > On 01/08/2013 06:12 PM, Brian Goetz wrote: > >> One of the reducers we've got takes a Stream and a function T->U and >> creates a Map. Currently we call this "mappedTo". Should this be >> called "join" instead? Or mapJoin? >> >> Map map = people.reduce(mappedTo(Person:**:getAge))); >> vs >> Map map = people.reduce(join(Person::**getAge))); >> Map map = people.reduce(mapJoin(Person::**getAge))); >> > > or just toMap, > Map map = people.reduce(Reducer.toMap(** > Person::getAge))); > > R?mi > > From misterm at gmail.com Tue Jan 8 10:21:54 2013 From: misterm at gmail.com (Michael Nascimento) Date: Tue, 8 Jan 2013 16:21:54 -0200 Subject: mappedTo -> join? In-Reply-To: <50EC6158.3000000@univ-mlv.fr> References: <50EC5364.7090803@oracle.com> <50EC6158.3000000@univ-mlv.fr> Message-ID: Do these Reducers include the opposite, i.e., a Map? This is a most common business operations in the code I've been trying to migrate to lambda use. Regards, Michael On Tue, Jan 8, 2013 at 4:11 PM, Remi Forax wrote: > On 01/08/2013 06:12 PM, Brian Goetz wrote: >> >> One of the reducers we've got takes a Stream and a function T->U and >> creates a Map. Currently we call this "mappedTo". Should this be >> called "join" instead? Or mapJoin? >> >> Map map = people.reduce(mappedTo(Person::getAge))); >> vs >> Map map = people.reduce(join(Person::getAge))); >> Map map = people.reduce(mapJoin(Person::getAge))); > > > or just toMap, > Map map = people.reduce(Reducer.toMap(Person::getAge))); > > R?mi > From forax at univ-mlv.fr Tue Jan 8 10:19:30 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Tue, 08 Jan 2013 19:19:30 +0100 Subject: Yet another run at reduce In-Reply-To: <50EC527E.8000003@oracle.com> References: <50EB76FE.8020505@oracle.com> <50EC380E.40602@oracle.com> <50EC46E9.9030907@oracle.com> <50EC527E.8000003@oracle.com> Message-ID: <50EC6332.8090701@univ-mlv.fr> On 01/08/2013 06:08 PM, Brian Goetz wrote: > And then... > > into(collection) > > becomes > > reduce(intoList()) > reduce(intoSet()) I think, I prefer, reduce(toList()) and reduce(toSet()), because their semantics is slightly different than into. > reduce(intoCollection(ArrayList::new)) > > where intoList() could be as simple as: > > public static > Reducer> intoList() { > return intoCollection(ArrayList::new); > } > > and intoCollection is as shown below. intoCollection should specifies that the supplier can be called several times, so it should always returns a new collection, i.e. this doesn't work: public static Reducer> intoList() { ArrayList list = new ArrayList<>(); return intoCollection(() -> list); } I fear that people will write something like that and only test with a serial stream. > > Then into() goes away. > > Then sequential() goes away. yes. R?mi > > > > > On 1/8/2013 11:37 AM, Tim Peierls wrote: >> On Tue, Jan 8, 2013 at 11:18 AM, Brian Goetz > > wrote: >> >> Where intoCollection is a simple Reducer: >> >> Reducer intoCollection(Supplier collectionFactory) { >> return leftCombiningReducer(__collectionFactory, >> Collection::add, Collection::addAll); >> } >> >> >> And leftCombiningReducer builds a reducer out of its arguments, which >> correspond to makeResult, accumulate, and combine? >> >> --tim From brian.goetz at oracle.com Tue Jan 8 10:40:02 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 08 Jan 2013 13:40:02 -0500 Subject: Yet another run at reduce In-Reply-To: <50EC6332.8090701@univ-mlv.fr> References: <50EB76FE.8020505@oracle.com> <50EC380E.40602@oracle.com> <50EC46E9.9030907@oracle.com> <50EC527E.8000003@oracle.com> <50EC6332.8090701@univ-mlv.fr> Message-ID: <50EC6802.3040109@oracle.com> >> And then... >> >> into(collection) >> >> becomes >> >> reduce(intoList()) >> reduce(intoSet()) > > I think, I prefer, reduce(toList()) and reduce(toSet()), > because their semantics is slightly different than into. That's reasonable. >> reduce(intoCollection(ArrayList::new)) >> >> where intoList() could be as simple as: >> >> public static >> Reducer> intoList() { >> return intoCollection(ArrayList::new); >> } >> >> and intoCollection is as shown below. > > intoCollection should specifies that the supplier can be called several > times, so it should always returns a new collection, i.e. this doesn't > work: Right. Needs to be doc'ed. > public static Reducer> intoList() { > ArrayList list = new ArrayList<>(); > return intoCollection(() -> list); > } > > I fear that people will write something like that and only test with a > serial stream. A valid fear :( From joe.darcy at oracle.com Tue Jan 8 12:04:02 2013 From: joe.darcy at oracle.com (Joe Darcy) Date: Tue, 08 Jan 2013 12:04:02 -0800 Subject: Request for review: proposal for @FunctionalInterface checking In-Reply-To: <50DE1171.9000909@oracle.com> References: <50DDFAC7.4030206@oracle.com> <7523190D-4DB3-48DA-B71A-A9EB6233D8E6@sampullara.com> <50DE1171.9000909@oracle.com> Message-ID: <50EC7BB2.6040702@oracle.com> Hello, While there were some reservations expressed about this proposal, there were no strong objections so I'm going forward with adding this annotation type to the platform, with a somewhat refined specification. Thanks, -Joe On 12/28/2012 1:38 PM, Joe Darcy wrote: > Hi Sam, > > On 12/28/2012 12:28 PM, Sam Pullara wrote: >> On Dec 28, 2012, at 3:02 PM, Joe Darcy > > wrote: >>> has exactly one abstract method. Since default methods are not >>> abstract, any default methods declared in an interface do not >>> contribute to its abstract method count. If an interface declares a >>> method overriding one of the public methods of java.lang.Object, >>> that also does not count toward the abstract method count. >> >> This is pretty murky. This works: >> >> interface Foo { >> @Override >> boolean equals(Object other); >> } >> >> but if you try this >> >> interface Foo { >> @Override >> default boolean equals(Object other) { >> return false; >> } >> } >> >> it does give an error that says I can't override: >> >> *java: default method equals in interface spullara.Foo overrides a >> member of java.lang.Object* >> >> Seems like "override" is the wrong word to use and will likely be >> confusing since we are explicitly disallowing the second one. >> >> > > Yes, I was contemplating whether "override" was the best phrasing to > use in the wording above because of this sort of wrinkle. Instead > "*abstract* method overriding one of the ..." might help distinguish > this particular case. > > -Joe From ali.ebrahimi1781 at gmail.com Tue Jan 8 14:07:48 2013 From: ali.ebrahimi1781 at gmail.com (Ali Ebrahimi) Date: Wed, 9 Jan 2013 02:37:48 +0430 Subject: Request for review: proposal for @FunctionalInterface checking In-Reply-To: <50EC7BB2.6040702@oracle.com> References: <50DDFAC7.4030206@oracle.com> <7523190D-4DB3-48DA-B71A-A9EB6233D8E6@sampullara.com> <50DE1171.9000909@oracle.com> <50EC7BB2.6040702@oracle.com> Message-ID: Hi joe, does this mean if functional interfaces do not be annotated with @FunctionalInterface annotation can not be used in lambda expressions. general question: annotating of functional interfaces is required or optional? I ask this for this purpose that there is huge number of functional interfaces in java legacy third parties code base that all are candidate for use in lambda expressions and can not be retrofired to annotated with this annotation. Ali On Wed, Jan 9, 2013 at 12:34 AM, Joe Darcy wrote: > Hello, > > While there were some reservations expressed about this proposal, there > were no strong objections so I'm going forward with adding this annotation > type to the platform, with a somewhat refined specification. > > Thanks, > > -Joe > > > On 12/28/2012 1:38 PM, Joe Darcy wrote: > >> Hi Sam, >> >> On 12/28/2012 12:28 PM, Sam Pullara wrote: >> >>> On Dec 28, 2012, at 3:02 PM, Joe Darcy >> joe.darcy at oracle.com>> wrote: >>> >>>> has exactly one abstract method. Since default methods are not >>>> abstract, any default methods declared in an interface do not contribute to >>>> its abstract method count. If an interface declares a method overriding >>>> one of the public methods of java.lang.Object, that also does not >>>> count toward the abstract method count. >>>> >>> >>> This is pretty murky. This works: >>> >>> interface Foo { >>> @Override >>> boolean equals(Object other); >>> } >>> >>> but if you try this >>> >>> interface Foo { >>> @Override >>> default boolean equals(Object other) { >>> return false; >>> } >>> } >>> >>> it does give an error that says I can't override: >>> >>> *java: default method equals in interface spullara.Foo overrides a >>> member of java.lang.Object* >>> >>> Seems like "override" is the wrong word to use and will likely be >>> confusing since we are explicitly disallowing the second one. >>> >>> >>> >> Yes, I was contemplating whether "override" was the best phrasing to use >> in the wording above because of this sort of wrinkle. Instead "*abstract* >> method overriding one of the ..." might help distinguish this particular >> case. >> >> -Joe >> > > From joe.darcy at oracle.com Tue Jan 8 14:09:55 2013 From: joe.darcy at oracle.com (Joe Darcy) Date: Tue, 08 Jan 2013 14:09:55 -0800 Subject: Request for review: proposal for @FunctionalInterface checking In-Reply-To: References: <50DDFAC7.4030206@oracle.com> <7523190D-4DB3-48DA-B71A-A9EB6233D8E6@sampullara.com> <50DE1171.9000909@oracle.com> <50EC7BB2.6040702@oracle.com> Message-ID: <50EC9933.2070000@oracle.com> Hello Ali, Use of @FunctionalInterface is optional and is just done to allow stricter compile-time checking. If a functional interface is *not* annotated with @FunctionalInterface, it can still be used for lambda expressions, etc. Cheers, -Joe On 01/08/2013 02:07 PM, Ali Ebrahimi wrote: > Hi joe, > > does this mean if functional interfaces do not be annotated with > @FunctionalInterface annotation can not be used in lambda expressions. > > general question: annotating of functional interfaces is required or > optional? > I ask this for this purpose that there is huge number of functional > interfaces in java legacy third parties code base that all are > candidate for use in lambda expressions and can not be retrofired to > annotated with this annotation. > > Ali > > On Wed, Jan 9, 2013 at 12:34 AM, Joe Darcy > wrote: > > Hello, > > While there were some reservations expressed about this proposal, > there were no strong objections so I'm going forward with adding > this annotation type to the platform, with a somewhat refined > specification. > > Thanks, > > -Joe > > > On 12/28/2012 1:38 PM, Joe Darcy wrote: > > Hi Sam, > > On 12/28/2012 12:28 PM, Sam Pullara wrote: > > On Dec 28, 2012, at 3:02 PM, Joe Darcy > > >> wrote: > > has exactly one abstract method. Since default > methods are not abstract, any default methods declared > in an interface do not contribute to its abstract > method count. If an interface declares a method > overriding one of the public methods of > java.lang.Object, that also does not count > toward the abstract method count. > > > This is pretty murky. This works: > > interface Foo { > @Override > boolean equals(Object other); > } > > but if you try this > > interface Foo { > @Override > default boolean equals(Object other) { > return false; > } > } > > it does give an error that says I can't override: > > *java: default method equals in interface spullara.Foo > overrides a member of java.lang.Object* > > Seems like "override" is the wrong word to use and will > likely be confusing since we are explicitly disallowing > the second one. > > > > Yes, I was contemplating whether "override" was the best > phrasing to use in the wording above because of this sort of > wrinkle. Instead "*abstract* method overriding one of the ..." > might help distinguish this particular case. > > -Joe > > > From ali.ebrahimi1781 at gmail.com Tue Jan 8 14:15:44 2013 From: ali.ebrahimi1781 at gmail.com (Ali Ebrahimi) Date: Wed, 9 Jan 2013 02:45:44 +0430 Subject: Request for review: proposal for @FunctionalInterface checking In-Reply-To: <50EC9933.2070000@oracle.com> References: <50DDFAC7.4030206@oracle.com> <7523190D-4DB3-48DA-B71A-A9EB6233D8E6@sampullara.com> <50DE1171.9000909@oracle.com> <50EC7BB2.6040702@oracle.com> <50EC9933.2070000@oracle.com> Message-ID: Thanks joe. Ali On Wed, Jan 9, 2013 at 2:39 AM, Joe Darcy wrote: > Hello Ali, > > Use of @FunctionalInterface is optional and is just done to allow stricter > compile-time checking. If a functional interface is *not* annotated with > @FunctionalInterface, it can still be used for lambda expressions, etc. > > Cheers, > > -Joe > > > On 01/08/2013 02:07 PM, Ali Ebrahimi wrote: > > Hi joe, > > does this mean if functional interfaces do not be annotated with > @FunctionalInterface annotation can not be used in lambda expressions. > > general question: annotating of functional interfaces is required or > optional? > I ask this for this purpose that there is huge number of functional > interfaces in java legacy third parties code base that all are candidate > for use in lambda expressions and can not be retrofired to annotated with > this annotation. > > Ali > > On Wed, Jan 9, 2013 at 12:34 AM, Joe Darcy wrote: > >> Hello, >> >> While there were some reservations expressed about this proposal, there >> were no strong objections so I'm going forward with adding this annotation >> type to the platform, with a somewhat refined specification. >> >> Thanks, >> >> -Joe >> >> >> On 12/28/2012 1:38 PM, Joe Darcy wrote: >> >>> Hi Sam, >>> >>> On 12/28/2012 12:28 PM, Sam Pullara wrote: >>> >>>> On Dec 28, 2012, at 3:02 PM, Joe Darcy >>> joe.darcy at oracle.com>> wrote: >>>> >>>>> has exactly one abstract method. Since default methods are not >>>>> abstract, any default methods declared in an interface do not contribute to >>>>> its abstract method count. If an interface declares a method overriding >>>>> one of the public methods of java.lang.Object, that also does not >>>>> count toward the abstract method count. >>>>> >>>> >>>> This is pretty murky. This works: >>>> >>>> interface Foo { >>>> @Override >>>> boolean equals(Object other); >>>> } >>>> >>>> but if you try this >>>> >>>> interface Foo { >>>> @Override >>>> default boolean equals(Object other) { >>>> return false; >>>> } >>>> } >>>> >>>> it does give an error that says I can't override: >>>> >>>> *java: default method equals in interface spullara.Foo overrides a >>>> member of java.lang.Object* >>>> >>>> Seems like "override" is the wrong word to use and will likely be >>>> confusing since we are explicitly disallowing the second one. >>>> >>>> >>>> >>> Yes, I was contemplating whether "override" was the best phrasing to use >>> in the wording above because of this sort of wrinkle. Instead "*abstract* >>> method overriding one of the ..." might help distinguish this particular >>> case. >>> >>> -Joe >>> >> >> > > From Donald.Raab at gs.com Tue Jan 8 18:38:49 2013 From: Donald.Raab at gs.com (Raab, Donald) Date: Tue, 8 Jan 2013 21:38:49 -0500 Subject: mappedTo -> join? In-Reply-To: References: <50EC5364.7090803@oracle.com> <50EC6158.3000000@univ-mlv.fr> Message-ID: <6712820CB52CFB4D842561213A77C05404C0C182D3@GSCMAMP09EX.firmwide.corp.gs.com> +1 toMap works for me. join is already too overloaded. On Tue, Jan 8, 2013 at 1:11 PM, Remi Forax > wrote: On 01/08/2013 06:12 PM, Brian Goetz wrote: One of the reducers we've got takes a Stream and a function T->U and creates a Map. Currently we call this "mappedTo". Should this be called "join" instead? Or mapJoin? Map map = people.reduce(mappedTo(Person::getAge))); vs Map map = people.reduce(join(Person::getAge))); Map map = people.reduce(mapJoin(Person::getAge))); or just toMap, Map map = people.reduce(Reducer.toMap(Person::getAge))); R?mi From joe.bowbeer at gmail.com Tue Jan 8 22:47:10 2013 From: joe.bowbeer at gmail.com (Joe Bowbeer) Date: Tue, 8 Jan 2013 22:47:10 -0800 Subject: ConcurrentHashMap/ConcurrentMap/Map.compute In-Reply-To: <50CF1483.50509@cs.oswego.edu> References: <50BF406D.5000805@cs.oswego.edu> <50BFB835.6090707@cs.oswego.edu> <50BFBD90.4030701@cs.oswego.edu> <50C19840.6070909@oracle.com> <50C1E7FF.8040307@cs.oswego.edu> <50C1F344.3020905@oracle.com> <50C1F538.2010006@univ-mlv.fr> <50C1F8F1.6050701@cs.oswego.edu> <50C1FB99.5060103@oracle.com> <50C20286.90002@cs.oswego.edu> <50C21662.8050308@cs.oswego.edu> <50CB3D47.5050106@cs.oswego.edu> <50CB4DB4.5090000@cs.oswego.edu> <50CB5A69.8020706@oracle.com> <50CB6835.4000203@cs.oswego.edu> <50CB69BC.3010202@oracle.com> <50CB6DF9.1090700@cs.oswego.edu> <50CB70A0.3000606@oracle.com> <50CB83DC.8050505@cs.oswego.edu> <50CB85B0.70505@oracle.com> <50CBC6DB.509@cs.oswego.edu> <50CBC8A6.4080409@oracle.com> <50CBCB4D.7030209@cs.oswego.edu> <50CF1483.50509@cs.oswego.edu> Message-ID: In the javadoc for Map.compute in b72, there is an error in the sample snippet: http://download.java.net/lambda/b72/docs/api/java/util/Map.html Attempts to compute a mapping for the specified key and its current mapped > value (or null if there is no current mapping). For example, to either > create or append a String msg to a value mapping: > map.compute(key, (k, v, msg) -> (v == null) ? msg : v.concat(msg)) Error: BiFunction does not accept three arguments. In particular, msg is extraneous. It should be defined in the lexical scope? Btw, I pondered how to efficiently use compute() or merge() to simulate a multi-map and I eventually gave up. I eventually wrote the following, which accumulates lists of anagrams, given a list of words: r.lines().forEach(s -> map.computeIfAbsent(key(s), k -> new ArrayList<>()).add(s)); Where key() returns the canonical key for a given word, and r is a BufferedReader for the dictionary file. The following line prints the lists of anagrams: map.values().stream().filter(v -> v.size() > 1).forEach(v -> System.out.println(v)); On Mon, Dec 17, 2012 at 4:48 AM, Doug Lea
wrote: > > A fixed-up successfully jdk8-javadoc'ed version of Map is now at > http://gee.cs.oswego.edu/dl/**wwwtmp/apis/Map.java > with displayable javadoc at: > http://gee.cs.oswego.edu/dl/**wwwtmp/apis/java/util/Map.html > > Comments? Complaints? If not, feel free to integrate. > > (Arne: thanks for pointing out that "merge" was mangled.) > > Side note about this and a few other upcoming cases: > We must keep j.u.c sources independent, so normally commit > to jsr166 and then integrate into openjdk. But ad-hoc > mechanics seem the only way to deal with updates to > existing files. Handing off is likely nicer than directly > committing into lambda. > > -Doug > > From paul.sandoz at oracle.com Wed Jan 9 01:15:58 2013 From: paul.sandoz at oracle.com (Paul Sandoz) Date: Wed, 9 Jan 2013 10:15:58 +0100 Subject: ConcurrentHashMap/ConcurrentMap/Map.compute In-Reply-To: References: <50BF406D.5000805@cs.oswego.edu> <50BFB835.6090707@cs.oswego.edu> <50BFBD90.4030701@cs.oswego.edu> <50C19840.6070909@oracle.com> <50C1E7FF.8040307@cs.oswego.edu> <50C1F344.3020905@oracle.com> <50C1F538.2010006@univ-mlv.fr> <50C1F8F1.6050701@cs.oswego.edu> <50C1FB99.5060103@oracle.com> <50C20286.90002@cs.oswego.edu> <50C21662.8050308@cs.oswego.edu> <50CB3D47.5050106@cs.oswego.edu> <50CB4DB4.5090000@cs.oswego.edu> <50CB5A69.8020706@oracle.com> <50CB6835.4000203@cs.oswego.edu> <50CB69BC.3010202@oracle.com> <50CB6DF9.1090700@cs.oswego.edu> <50CB70A0.3000606@oracle.com> <50CB83DC.8050505@cs.oswego.edu> <50CB85B0.70505@oracle.com> <50CBC6DB.509@cs.oswego.edu> <50CBC8A6.4080409@oracle.com> <50CBCB4D.7030209@cs.oswego.edu> <50CF1483.50509@cs.oswego.edu> Message-ID: <45CB96BF-27EC-45AA-9E82-C06D1145DB5B@oracle.com> On Jan 9, 2013, at 7:47 AM, Joe Bowbeer wrote: > In the javadoc for Map.compute in b72, there is an error in the sample > snippet: > > http://download.java.net/lambda/b72/docs/api/java/util/Map.html > > Attempts to compute a mapping for the specified key and its current mapped >> value (or null if there is no current mapping). For example, to either >> create or append a String msg to a value mapping: >> map.compute(key, (k, v, msg) -> (v == null) ? msg : v.concat(msg)) > > > Error: BiFunction does not accept three arguments. In particular, msg is > extraneous. It should be defined in the lexical scope? > > > Btw, I pondered how to efficiently use compute() or merge() to simulate a > multi-map and I eventually gave up. > > I eventually wrote the following, which accumulates lists of anagrams, > given a list of words: > > r.lines().forEach(s -> map.computeIfAbsent(key(s), k -> new > ArrayList<>()).add(s)); > Have you tried using the latest groupBy functionality? e.g.: Map> anagrams = r.lines().parallel().reduce(groupBy(s -> key(s))); It would be interesting to compare performance of reduce vs. reduceUnordered and the concurrent groupBy leveraging CHM. > > Where key() returns the canonical key for a given word, and r is a > BufferedReader for the dictionary file. > > The following line prints the lists of anagrams: > > map.values().stream().filter(v -> v.size() > 1).forEach(v -> > System.out.println(v)); > Little style tip: forEach(System.out::println) Paul. From dl at cs.oswego.edu Wed Jan 9 03:12:46 2013 From: dl at cs.oswego.edu (Doug Lea) Date: Wed, 09 Jan 2013 06:12:46 -0500 Subject: Yet another run at reduce (collect) In-Reply-To: <50EC527E.8000003@oracle.com> References: <50EB76FE.8020505@oracle.com> <50EC380E.40602@oracle.com> <50EC46E9.9030907@oracle.com> <50EC527E.8000003@oracle.com> Message-ID: <50ED50AE.8020803@cs.oswego.edu> While scoping out this form of solution, Brian noticed that the new collecting form of reduce is overload-hostile to one of the existing reduce methods. (that is, has the same arity and has mutually-confusable lambda args.) Having been traumatized by this problem last week, I think we need to do something about this. He is not yet convinced that we will need to change this. I am. I propose renaming this form "collect", and the corresponding interface to Collector. The underlying concept here is, in Fortress-ese, catamorphic reduction, but the closest non-weird term is "collect". Much closer than "accumulate". And further, I think this is a net positive win in terms of understandability. For example: reduce(intoList()) => collect(intoList()) The name provides a pleasant and appropriate clue that it is something often applicable to Collections. Try it out on other examples. I think you will like it better unless you are Brian :-) And if we don't do this, people will be getting mile-long overload ambiguity messages from Javac in surprising contexts. -Doug From forax at univ-mlv.fr Wed Jan 9 03:19:24 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Wed, 09 Jan 2013 12:19:24 +0100 Subject: Yet another run at reduce (collect) In-Reply-To: <50ED50AE.8020803@cs.oswego.edu> References: <50EB76FE.8020505@oracle.com> <50EC380E.40602@oracle.com> <50EC46E9.9030907@oracle.com> <50EC527E.8000003@oracle.com> <50ED50AE.8020803@cs.oswego.edu> Message-ID: <50ED523C.9090708@univ-mlv.fr> On 01/09/2013 12:12 PM, Doug Lea wrote: > > While scoping out this form of solution, Brian noticed that the > new collecting form of reduce is overload-hostile to > one of the existing reduce methods. (that is, has the same arity > and has mutually-confusable lambda args.) Having been traumatized > by this problem last week, I think we need to do something about this. > He is not yet convinced that we will need to change this. I am. > > I propose renaming this form "collect", and the corresponding > interface to Collector. The underlying concept here is, > in Fortress-ese, catamorphic reduction, but the closest > non-weird term is "collect". Much closer than "accumulate". > > And further, I think this is a net positive win in terms of > understandability. For example: > > reduce(intoList()) => collect(intoList()) > > The name provides a pleasant and appropriate clue that it is > something often applicable to Collections. > > Try it out on other examples. > I think you will like it better unless you are Brian :-) > > And if we don't do this, people will be getting mile-long > overload ambiguity messages from Javac in surprising > contexts. > > -Doug > > collect is fine is there are side effects, reduce is fine if there is a return value. Given that we want a unified concept for both, i'm fine with collect. R?mi From tim at peierls.net Wed Jan 9 05:53:10 2013 From: tim at peierls.net (Tim Peierls) Date: Wed, 9 Jan 2013 08:53:10 -0500 Subject: Yet another run at reduce (collect) In-Reply-To: <50ED50AE.8020803@cs.oswego.edu> References: <50EB76FE.8020505@oracle.com> <50EC380E.40602@oracle.com> <50EC46E9.9030907@oracle.com> <50EC527E.8000003@oracle.com> <50ED50AE.8020803@cs.oswego.edu> Message-ID: Yes! "collect" is good. I had been translating "reduce" into "collect" already in my head. Of course then I'd want Reducers (Collectors?) like "toMap" and "toList" to be "intoMap" and "intoList". --tim On Wed, Jan 9, 2013 at 6:12 AM, Doug Lea
wrote: > > While scoping out this form of solution, Brian noticed that the > new collecting form of reduce is overload-hostile to > one of the existing reduce methods. (that is, has the same arity > and has mutually-confusable lambda args.) Having been traumatized > by this problem last week, I think we need to do something about this. > He is not yet convinced that we will need to change this. I am. > > I propose renaming this form "collect", and the corresponding > interface to Collector. The underlying concept here is, > in Fortress-ese, catamorphic reduction, but the closest > non-weird term is "collect". Much closer than "accumulate". > > And further, I think this is a net positive win in terms of > understandability. For example: > > reduce(intoList()) => collect(intoList()) > > The name provides a pleasant and appropriate clue that it is > something often applicable to Collections. > > Try it out on other examples. > I think you will like it better unless you are Brian :-) > > And if we don't do this, people will be getting mile-long > overload ambiguity messages from Javac in surprising > contexts. > > -Doug > > > From brian.goetz at oracle.com Wed Jan 9 06:35:31 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 09 Jan 2013 09:35:31 -0500 Subject: Yet another run at reduce (collect) In-Reply-To: <50ED50AE.8020803@cs.oswego.edu> References: <50EB76FE.8020505@oracle.com> <50EC380E.40602@oracle.com> <50EC46E9.9030907@oracle.com> <50EC527E.8000003@oracle.com> <50ED50AE.8020803@cs.oswego.edu> Message-ID: <50ED8033.4080104@oracle.com> > And further, I think this is a net positive win in terms of > understandability. For example: > > reduce(intoList()) => collect(intoList()) > > The name provides a pleasant and appropriate clue that it is > something often applicable to Collections. > > Try it out on other examples. collect(groupBy(...)) collect(partition(...)) collect(mappedTo / toMap)) From brian.goetz at oracle.com Wed Jan 9 10:56:42 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 09 Jan 2013 13:56:42 -0500 Subject: Yet another run at reduce (collect) In-Reply-To: <50ED50AE.8020803@cs.oswego.edu> References: <50EB76FE.8020505@oracle.com> <50EC380E.40602@oracle.com> <50EC46E9.9030907@oracle.com> <50EC527E.8000003@oracle.com> <50ED50AE.8020803@cs.oswego.edu> Message-ID: <50EDBD6A.1040902@oracle.com> I've pushed a rename to collect(). Check it out. Also into() is gone. Replaced with collect(Collectors.into{List,Set,Collection}). On 1/9/2013 6:12 AM, Doug Lea wrote: > > While scoping out this form of solution, Brian noticed that the > new collecting form of reduce is overload-hostile to > one of the existing reduce methods. (that is, has the same arity > and has mutually-confusable lambda args.) Having been traumatized > by this problem last week, I think we need to do something about this. > He is not yet convinced that we will need to change this. I am. > > I propose renaming this form "collect", and the corresponding > interface to Collector. The underlying concept here is, > in Fortress-ese, catamorphic reduction, but the closest > non-weird term is "collect". Much closer than "accumulate". > > And further, I think this is a net positive win in terms of > understandability. For example: > > reduce(intoList()) => collect(intoList()) > > The name provides a pleasant and appropriate clue that it is > something often applicable to Collections. > > Try it out on other examples. > I think you will like it better unless you are Brian :-) > > And if we don't do this, people will be getting mile-long > overload ambiguity messages from Javac in surprising > contexts. > > -Doug > > From dl at cs.oswego.edu Wed Jan 9 11:25:45 2013 From: dl at cs.oswego.edu (Doug Lea) Date: Wed, 09 Jan 2013 14:25:45 -0500 Subject: Yet another run at reduce (collect) In-Reply-To: <50EDBD6A.1040902@oracle.com> References: <50EB76FE.8020505@oracle.com> <50EC380E.40602@oracle.com> <50EC46E9.9030907@oracle.com> <50EC527E.8000003@oracle.com> <50ED50AE.8020803@cs.oswego.edu> <50EDBD6A.1040902@oracle.com> Message-ID: <50EDC439.9060201@cs.oswego.edu> On 01/09/13 13:56, Brian Goetz wrote: > I've pushed a rename to collect(). Check it out. Yay! Thanks! -Doug From brian.goetz at oracle.com Wed Jan 9 11:44:01 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 09 Jan 2013 14:44:01 -0500 Subject: Cancelable streams Message-ID: <50EDC881.4040804@oracle.com> I've pushed a stream op "forEachUntil(Block, BooleanSupplier)". After offline discussion with Doug, this seemed to be an acceptable alternative to having no cancelation support at all. From brian.goetz at oracle.com Wed Jan 9 11:46:56 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 09 Jan 2013 14:46:56 -0500 Subject: Resource release Message-ID: <50EDC930.9020806@oracle.com> There have been a few requests for better treatment of resource release. While a full-blown treatment (e.g., .onException() handlers) is not in the cards here, one of my team is working on a CloseableStream (implements Stream and Closeable) and using this for various IO usages, and this seems promising for this limited use area. From brian.goetz at oracle.com Wed Jan 9 11:50:59 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 09 Jan 2013 14:50:59 -0500 Subject: Other renames Message-ID: <50EDCA23.7060408@oracle.com> In recent pushes, also did: MultiFunction -> Multifunction MultiFunction.Collector -> Multifunction.Downstream From forax at univ-mlv.fr Wed Jan 9 11:56:30 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Wed, 09 Jan 2013 20:56:30 +0100 Subject: Cancelable streams In-Reply-To: <50EDC881.4040804@oracle.com> References: <50EDC881.4040804@oracle.com> Message-ID: <50EDCB6E.1070509@univ-mlv.fr> On 01/09/2013 08:44 PM, Brian Goetz wrote: > I've pushed a stream op "forEachUntil(Block, BooleanSupplier)". After > offline discussion with Doug, this seemed to be an acceptable > alternative to having no cancelation support at all. > Sorry, I've forgotten to answer. While I agree with the concept, I think forEachUntil has the wrong signature, the issue I see is that some time you want to stop depending on a value which is a property of an element, but here the BooleanSupplier doesn't have access to the current element. I would prefer void forEachUntil(Predicate condition) so one can write stream.forEachUntil(e -> e.accuracy <= 0.5) cheers, R?mi From brian.goetz at oracle.com Wed Jan 9 12:18:32 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 09 Jan 2013 15:18:32 -0500 Subject: Cancelable streams In-Reply-To: <50EDCB6E.1070509@univ-mlv.fr> References: <50EDC881.4040804@oracle.com> <50EDCB6E.1070509@univ-mlv.fr> Message-ID: <50EDD098.2040003@oracle.com> That's a different feature, and one that is more complex to spec (and this complexity is what almost pushed us to just give up on cancelation.) The complexity comes from the parallel case, in the interaction with encounter order. Users might do forEachUntil(e -> e.equals("3")) on a stream of 1, 2, 3, 4, 5, 6 and be very surprised that on completion, elements 1, 3, and 6 have been processed. They'll wonder "what happened to 2, it was before 3", and similarly wonder "why 6, it is after 3." On 1/9/2013 2:56 PM, Remi Forax wrote: > On 01/09/2013 08:44 PM, Brian Goetz wrote: >> I've pushed a stream op "forEachUntil(Block, BooleanSupplier)". After >> offline discussion with Doug, this seemed to be an acceptable >> alternative to having no cancelation support at all. >> > > Sorry, I've forgotten to answer. > While I agree with the concept, I think forEachUntil has the wrong > signature, the issue I see is that some time you want to stop depending > on a value which is a property of an element, but here the > BooleanSupplier doesn't have access to the current element. > > I would prefer > void forEachUntil(Predicate condition) > > so one can write > stream.forEachUntil(e -> e.accuracy <= 0.5) > > cheers, > R?mi > From aleksey.shipilev at oracle.com Wed Jan 9 13:10:16 2013 From: aleksey.shipilev at oracle.com (Aleksey Shipilev) Date: Thu, 10 Jan 2013 01:10:16 +0400 Subject: Yet another run at reduce (collect) In-Reply-To: <50EDC439.9060201@cs.oswego.edu> References: <50EB76FE.8020505@oracle.com> <50EC380E.40602@oracle.com> <50EC46E9.9030907@oracle.com> <50EC527E.8000003@oracle.com> <50ED50AE.8020803@cs.oswego.edu> <50EDBD6A.1040902@oracle.com> <50EDC439.9060201@cs.oswego.edu> Message-ID: <50EDDCB8.8090201@oracle.com> On 01/09/2013 11:25 PM, Doug Lea wrote: > On 01/09/13 13:56, Brian Goetz wrote: >> I've pushed a rename to collect(). Check it out. > > Yay! Thanks! Ok, this is quite an intrusive change to adapt for my performance tests. Can you please add more Javadocs for Collector? In fact, I need more details on: a) Should makeResult() return the new result on every call? b) Can combine(result, other) return updated $result, or it should return distinct collectable? -Aleksey. From aleksey.shipilev at oracle.com Wed Jan 9 13:13:12 2013 From: aleksey.shipilev at oracle.com (Aleksey Shipilev) Date: Thu, 10 Jan 2013 01:13:12 +0400 Subject: Yet another run at reduce (collect) In-Reply-To: <50EDDCB8.8090201@oracle.com> References: <50EB76FE.8020505@oracle.com> <50EC380E.40602@oracle.com> <50EC46E9.9030907@oracle.com> <50EC527E.8000003@oracle.com> <50ED50AE.8020803@cs.oswego.edu> <50EDBD6A.1040902@oracle.com> <50EDC439.9060201@cs.oswego.edu> <50EDDCB8.8090201@oracle.com> Message-ID: <50EDDD68.9040708@oracle.com> On 01/10/2013 01:10 AM, Aleksey Shipilev wrote: > On 01/09/2013 11:25 PM, Doug Lea wrote: >> On 01/09/13 13:56, Brian Goetz wrote: >>> I've pushed a rename to collect(). Check it out. >> >> Yay! Thanks! > > Ok, this is quite an intrusive change to adapt for my performance tests. > Can you please add more Javadocs for Collector? In fact, I need more > details on: > a) Should makeResult() return the new result on every call? > b) Can combine(result, other) return updated $result, or it should > return distinct collectable? Also, is there a bulk accumulate method (guess that requires the exposure of the backing tree)? It gets interesting when you do collecting in e.g. AtomicX, and want to collect the values into simple scalar, only updating the costly structure afterwards. -Aleksey. From brian.goetz at oracle.com Wed Jan 9 13:13:33 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 09 Jan 2013 16:13:33 -0500 Subject: Yet another run at reduce (collect) In-Reply-To: <50EDDCB8.8090201@oracle.com> References: <50EB76FE.8020505@oracle.com> <50EC380E.40602@oracle.com> <50EC46E9.9030907@oracle.com> <50EC527E.8000003@oracle.com> <50ED50AE.8020803@cs.oswego.edu> <50EDBD6A.1040902@oracle.com> <50EDC439.9060201@cs.oswego.edu> <50EDDCB8.8090201@oracle.com> Message-ID: <50EDDD7D.5090503@oracle.com> The simple rule is: - makeResult should return a new result container for every call - combine may return one of its arguments, or a new result On 1/9/2013 4:10 PM, Aleksey Shipilev wrote: > On 01/09/2013 11:25 PM, Doug Lea wrote: >> On 01/09/13 13:56, Brian Goetz wrote: >>> I've pushed a rename to collect(). Check it out. >> >> Yay! Thanks! > > Ok, this is quite an intrusive change to adapt for my performance tests. > Can you please add more Javadocs for Collector? In fact, I need more > details on: > a) Should makeResult() return the new result on every call? > b) Can combine(result, other) return updated $result, or it should > return distinct collectable? > > -Aleksey. > From forax at univ-mlv.fr Wed Jan 9 13:19:58 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Wed, 09 Jan 2013 22:19:58 +0100 Subject: Cancelable streams In-Reply-To: <50EDD098.2040003@oracle.com> References: <50EDC881.4040804@oracle.com> <50EDCB6E.1070509@univ-mlv.fr> <50EDD098.2040003@oracle.com> Message-ID: <50EDDEFE.8070605@univ-mlv.fr> On 01/09/2013 09:18 PM, Brian Goetz wrote: > That's a different feature, and one that is more complex to spec (and > this complexity is what almost pushed us to just give up on cancelation.) > > The complexity comes from the parallel case, in the interaction with > encounter order. Users might do > > forEachUntil(e -> e.equals("3")) > > on a stream of 1, 2, 3, 4, 5, 6 > > and be very surprised that on completion, elements 1, 3, and 6 have > been processed. They'll wonder "what happened to 2, it was before 3", > and similarly wonder "why 6, it is after 3." given that he asks for something parallel, he gets what he asks, no ? R?mi > > > > On 1/9/2013 2:56 PM, Remi Forax wrote: >> On 01/09/2013 08:44 PM, Brian Goetz wrote: >>> I've pushed a stream op "forEachUntil(Block, BooleanSupplier)". After >>> offline discussion with Doug, this seemed to be an acceptable >>> alternative to having no cancelation support at all. >>> >> >> Sorry, I've forgotten to answer. >> While I agree with the concept, I think forEachUntil has the wrong >> signature, the issue I see is that some time you want to stop depending >> on a value which is a property of an element, but here the >> BooleanSupplier doesn't have access to the current element. >> >> I would prefer >> void forEachUntil(Predicate condition) >> >> so one can write >> stream.forEachUntil(e -> e.accuracy <= 0.5) >> >> cheers, >> R?mi >> From aleksey.shipilev at oracle.com Wed Jan 9 14:50:03 2013 From: aleksey.shipilev at oracle.com (Aleksey Shipilev) Date: Thu, 10 Jan 2013 02:50:03 +0400 Subject: Yet another run at reduce (collect) In-Reply-To: <50EDDD7D.5090503@oracle.com> References: <50EB76FE.8020505@oracle.com> <50EC380E.40602@oracle.com> <50EC46E9.9030907@oracle.com> <50EC527E.8000003@oracle.com> <50ED50AE.8020803@cs.oswego.edu> <50EDBD6A.1040902@oracle.com> <50EDC439.9060201@cs.oswego.edu> <50EDDCB8.8090201@oracle.com> <50EDDD7D.5090503@oracle.com> Message-ID: <50EDF41B.8090602@oracle.com> Thanks. Another question: it seems to me that collector passed into collect(...) can actually be reused, since I would expect the library will call makeResult() at least once to bootstrap the collector? Or, is the library reusing the one passed via the parameter? I.e. instead of: stream().collect(new MyCollector()).getResult(); class MyCollector implements Collector { public MyCollector makeResult() { return new MyCollector(); } public void accumulate(MyCollector result, Long value) { result.result += value; } public MyCollector combine(MyCollector result, SumLongSink other) { result.result += other.result; return result; } public long getResult() { return result; } } ...can I do this? static final MyCollector MY_COLLECTOR = new MyCollector() (...) stream().collect(MY_COLLECTOR).getResult(); -Aleksey. On 01/10/2013 01:13 AM, Brian Goetz wrote: > The simple rule is: > - makeResult should return a new result container for every call > - combine may return one of its arguments, or a new result > > > > On 1/9/2013 4:10 PM, Aleksey Shipilev wrote: >> On 01/09/2013 11:25 PM, Doug Lea wrote: >>> On 01/09/13 13:56, Brian Goetz wrote: >>>> I've pushed a rename to collect(). Check it out. >>> >>> Yay! Thanks! >> >> Ok, this is quite an intrusive change to adapt for my performance tests. >> Can you please add more Javadocs for Collector? In fact, I need more >> details on: >> a) Should makeResult() return the new result on every call? >> b) Can combine(result, other) return updated $result, or it should >> return distinct collectable? >> >> -Aleksey. >> From brian.goetz at oracle.com Wed Jan 9 15:10:23 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 09 Jan 2013 18:10:23 -0500 Subject: Yet another run at reduce (collect) In-Reply-To: <50EDF41B.8090602@oracle.com> References: <50EB76FE.8020505@oracle.com> <50EC380E.40602@oracle.com> <50EC46E9.9030907@oracle.com> <50EC527E.8000003@oracle.com> <50ED50AE.8020803@cs.oswego.edu> <50EDBD6A.1040902@oracle.com> <50EDC439.9060201@cs.oswego.edu> <50EDDCB8.8090201@oracle.com> <50EDDD7D.5090503@oracle.com> <50EDF41B.8090602@oracle.com> Message-ID: <50EDF8DF.6070304@oracle.com> We have tried to follow the practice of treating all objects passed to stream methods as values. So collectors can be reused. You can singleton them as you suggest. On 1/9/2013 5:50 PM, Aleksey Shipilev wrote: > Thanks. Another question: it seems to me that collector passed into > collect(...) can actually be reused, since I would expect the library > will call makeResult() at least once to bootstrap the collector? Or, is > the library reusing the one passed via the parameter? I.e. instead of: > > stream().collect(new MyCollector()).getResult(); > > class MyCollector implements Collector { > public MyCollector makeResult() { > return new MyCollector(); > } > > public void accumulate(MyCollector result, Long value) { > result.result += value; > } > > public MyCollector combine(MyCollector result, SumLongSink other) { > result.result += other.result; > return result; > } > > public long getResult() { > return result; > } > } > > ...can I do this? > > static final MyCollector MY_COLLECTOR = new MyCollector() > (...) > stream().collect(MY_COLLECTOR).getResult(); > > > -Aleksey. > > On 01/10/2013 01:13 AM, Brian Goetz wrote: >> The simple rule is: >> - makeResult should return a new result container for every call >> - combine may return one of its arguments, or a new result >> >> >> >> On 1/9/2013 4:10 PM, Aleksey Shipilev wrote: >>> On 01/09/2013 11:25 PM, Doug Lea wrote: >>>> On 01/09/13 13:56, Brian Goetz wrote: >>>>> I've pushed a rename to collect(). Check it out. >>>> >>>> Yay! Thanks! >>> >>> Ok, this is quite an intrusive change to adapt for my performance tests. >>> Can you please add more Javadocs for Collector? In fact, I need more >>> details on: >>> a) Should makeResult() return the new result on every call? >>> b) Can combine(result, other) return updated $result, or it should >>> return distinct collectable? >>> >>> -Aleksey. >>> > From brian.goetz at oracle.com Thu Jan 10 07:10:43 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 10 Jan 2013 10:10:43 -0500 Subject: One more pass on flatMap/mapMulti Message-ID: <50EED9F3.3040600@oracle.com> One more simplification: we don't really need a custom type SAM for Multifunction. We can rewrite mapMulti as: mapMulti(BiBlock, T>)) and move the Downstream class declaration to Stream, and XxxMultifunction go away. mapMulti is still not a great name. How about "explode" ? From forax at univ-mlv.fr Thu Jan 10 07:35:27 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Thu, 10 Jan 2013 16:35:27 +0100 Subject: One more pass on flatMap/mapMulti In-Reply-To: <50EED9F3.3040600@oracle.com> References: <50EED9F3.3040600@oracle.com> Message-ID: <50EEDFBF.8020204@univ-mlv.fr> On 01/10/2013 04:10 PM, Brian Goetz wrote: > One more simplification: we don't really need a custom type SAM for > Multifunction. We can rewrite mapMulti as: > > mapMulti(BiBlock, T>)) > > and move the Downstream class declaration to Stream, and > XxxMultifunction go away. what Downstream is ? > > > mapMulti is still not a great name. How about "explode" ? > yes, explode is better. R?mi From dl at cs.oswego.edu Thu Jan 10 07:42:41 2013 From: dl at cs.oswego.edu (Doug Lea) Date: Thu, 10 Jan 2013 10:42:41 -0500 Subject: One more pass on flatMap/mapMulti In-Reply-To: <50EEDFBF.8020204@univ-mlv.fr> References: <50EED9F3.3040600@oracle.com> <50EEDFBF.8020204@univ-mlv.fr> Message-ID: <50EEE171.3080005@cs.oswego.edu> On 01/10/13 10:35, Remi Forax wrote: >> >> mapMulti is still not a great name. How about "explode" ? >> > > yes, explode is better. > ... because that is what will happen with people's memory consumption if they don't do the right thing and merge while collecting :-) -Doug From tim at peierls.net Thu Jan 10 07:45:10 2013 From: tim at peierls.net (Tim Peierls) Date: Thu, 10 Jan 2013 10:45:10 -0500 Subject: One more pass on flatMap/mapMulti In-Reply-To: <50EED9F3.3040600@oracle.com> References: <50EED9F3.3040600@oracle.com> Message-ID: On Thu, Jan 10, 2013 at 10:10 AM, Brian Goetz wrote: > One more simplification: we don't really need a custom type SAM for > Multifunction. We can rewrite mapMulti as: > > mapMulti(BiBlock**, T>)) > > and move the Downstream class declaration to Stream, and XxxMultifunction > go away. > > mapMulti is still not a great name. How about "explode" ? > Um, the not-greatness is in the names BiBlock and Downstream. I have the vague idea that BiBlock is a void procedure taking 2 arguments, and I know you renamed something to Downstream, but the combination of these two into the signature above is impenetrable. mapMulti itself isn't so bad. I can guess at what that might be. --tim From brian.goetz at oracle.com Thu Jan 10 07:52:50 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 10 Jan 2013 10:52:50 -0500 Subject: One more pass on flatMap/mapMulti In-Reply-To: References: <50EED9F3.3040600@oracle.com> Message-ID: <50EEE3D2.4080706@oracle.com> The idea behind "Downstream" is that it represents sending results downstream. Multifunction and mapMulti conjures up the image that I'm mapping a single element to an aggregate, which makes people expect Function> (or to Stream). But that's not what's going on here. Instead, you get an element, and you get to send zero or more results downstream. I think "explode" conjures up more an image of "send fragments in this direction", which is more like what's going on here. On 1/10/2013 10:45 AM, Tim Peierls wrote: > On Thu, Jan 10, 2013 at 10:10 AM, Brian Goetz > wrote: > > One more simplification: we don't really need a custom type SAM for > Multifunction. We can rewrite mapMulti as: > > mapMulti(BiBlock__, T>)) > > and move the Downstream class declaration to Stream, and > XxxMultifunction go away. > > mapMulti is still not a great name. How about "explode" ? > > > Um, the not-greatness is in the names BiBlock and Downstream. I have the > vague idea that BiBlock is a void procedure taking 2 arguments, and I > know you renamed something to Downstream, but the combination of these > two into the signature above is impenetrable. > > mapMulti itself isn't so bad. I can guess at what that might be. > > --tim > From brian.goetz at oracle.com Thu Jan 10 07:54:27 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 10 Jan 2013 10:54:27 -0500 Subject: One more pass on flatMap/mapMulti In-Reply-To: <50EEDFBF.8020204@univ-mlv.fr> References: <50EED9F3.3040600@oracle.com> <50EEDFBF.8020204@univ-mlv.fr> Message-ID: <50EEE433.40502@oracle.com> Downstream: /** A collector for values associated with a given input. Values can be * yielded individually, or in aggregates such as collections, arrays, or * streams; aggregates are flattened, so that yielding an array containing * [1, 2] is equivalent to yield(1); yield(2). */ interface Downstream { void yield(U element); default void yield(Collection collection) { for (U u : collection) yield(u); } default void yield(U[] array) { for (U u : array) yield(u); } default void yield(Stream stream) { stream.forEach(this::yield); } } The basic idea is that this is a collector for values. It was at one point called "collector" but now we have something else called Collector. On 1/10/2013 10:35 AM, Remi Forax wrote: > On 01/10/2013 04:10 PM, Brian Goetz wrote: >> One more simplification: we don't really need a custom type SAM for >> Multifunction. We can rewrite mapMulti as: >> >> mapMulti(BiBlock, T>)) >> >> and move the Downstream class declaration to Stream, and >> XxxMultifunction go away. > > what Downstream is ? > >> >> >> mapMulti is still not a great name. How about "explode" ? >> > > yes, explode is better. > > R?mi > From joe.bowbeer at gmail.com Thu Jan 10 07:59:40 2013 From: joe.bowbeer at gmail.com (Joe Bowbeer) Date: Thu, 10 Jan 2013 07:59:40 -0800 Subject: One more pass on flatMap/mapMulti In-Reply-To: <50EEE3D2.4080706@oracle.com> References: <50EED9F3.3040600@oracle.com> <50EEE3D2.4080706@oracle.com> Message-ID: I'm OK with explode; it feels familiar in this context of streams and functions. Alternatives: mapFlatten mapExpand mapExplode I don't like map and multi together except to form the term multimap. On Jan 10, 2013 7:53 AM, "Brian Goetz" wrote: > The idea behind "Downstream" is that it represents sending results > downstream. Multifunction and mapMulti conjures up the image that I'm > mapping a single element to an aggregate, which makes people expect > Function> (or to Stream). But that's not what's going > on here. Instead, you get an element, and you get to send zero or more > results downstream. I think "explode" conjures up more an image of "send > fragments in this direction", which is more like what's going on here. > > > > On 1/10/2013 10:45 AM, Tim Peierls wrote: > >> On Thu, Jan 10, 2013 at 10:10 AM, Brian Goetz > > wrote: >> >> One more simplification: we don't really need a custom type SAM for >> Multifunction. We can rewrite mapMulti as: >> >> mapMulti(BiBlock**__, T>)) >> >> and move the Downstream class declaration to Stream, and >> XxxMultifunction go away. >> >> mapMulti is still not a great name. How about "explode" ? >> >> >> Um, the not-greatness is in the names BiBlock and Downstream. I have the >> vague idea that BiBlock is a void procedure taking 2 arguments, and I >> know you renamed something to Downstream, but the combination of these >> two into the signature above is impenetrable. >> >> mapMulti itself isn't so bad. I can guess at what that might be. >> >> --tim >> >> From tim at peierls.net Thu Jan 10 08:01:06 2013 From: tim at peierls.net (Tim Peierls) Date: Thu, 10 Jan 2013 11:01:06 -0500 Subject: One more pass on flatMap/mapMulti In-Reply-To: <50EEE433.40502@oracle.com> References: <50EED9F3.3040600@oracle.com> <50EEDFBF.8020204@univ-mlv.fr> <50EEE433.40502@oracle.com> Message-ID: How about Acceptor? On Thu, Jan 10, 2013 at 10:54 AM, Brian Goetz wrote: > Downstream: > > /** A collector for values associated with a given input. Values can > be > * yielded individually, or in aggregates such as collections, arrays, > or > * streams; aggregates are flattened, so that yielding an array > containing > * [1, 2] is equivalent to yield(1); yield(2). > */ > interface Downstream { > void yield(U element); > > default void yield(Collection collection) { > for (U u : collection) > yield(u); > } > > default void yield(U[] array) { > for (U u : array) > yield(u); > } > > default void yield(Stream stream) { > stream.forEach(this::yield); > } > } > > The basic idea is that this is a collector for values. It was at one > point called "collector" but now we have something else called Collector. From joe.bowbeer at gmail.com Thu Jan 10 08:14:17 2013 From: joe.bowbeer at gmail.com (Joe Bowbeer) Date: Thu, 10 Jan 2013 08:14:17 -0800 Subject: One more pass on flatMap/mapMulti In-Reply-To: References: <50EED9F3.3040600@oracle.com> <50EEDFBF.8020204@univ-mlv.fr> <50EEE433.40502@oracle.com> Message-ID: I don't mind Downstream, but something like Acceptor might be better. However, yield should be renamed in either case. receive? downstreamReceive? We are going to run out of names fast! Why not a convention for these names to avoid this? One way to disambiguate and relieve the name stress (and stress of naming) is to apt adopt this convention: downstreamApply That is, prepend interface name to method name. In this case all the method names could be simply 'apply' because we already know the interrace names are unique. I don't have a dog in that hunt. Joe On Jan 10, 2013 8:01 AM, "Tim Peierls" wrote: > How about Acceptor? > > On Thu, Jan 10, 2013 at 10:54 AM, Brian Goetz wrote: > >> Downstream: >> >> /** A collector for values associated with a given input. Values can >> be >> * yielded individually, or in aggregates such as collections, >> arrays, or >> * streams; aggregates are flattened, so that yielding an array >> containing >> * [1, 2] is equivalent to yield(1); yield(2). >> */ >> interface Downstream { >> void yield(U element); >> >> default void yield(Collection collection) { >> for (U u : collection) >> yield(u); >> } >> >> default void yield(U[] array) { >> for (U u : array) >> yield(u); >> } >> >> default void yield(Stream stream) { >> stream.forEach(this::yield); >> } >> } >> >> The basic idea is that this is a collector for values. It was at one >> point called "collector" but now we have something else called Collector. > > > > From tim at peierls.net Thu Jan 10 08:18:36 2013 From: tim at peierls.net (Tim Peierls) Date: Thu, 10 Jan 2013 11:18:36 -0500 Subject: One more pass on flatMap/mapMulti In-Reply-To: References: <50EED9F3.3040600@oracle.com> <50EEDFBF.8020204@univ-mlv.fr> <50EEE433.40502@oracle.com> Message-ID: Acceptor.accept? On Thu, Jan 10, 2013 at 11:14 AM, Joe Bowbeer wrote: > I don't mind Downstream, but something like Acceptor might be better. > > However, yield should be renamed in either case. > > receive? > > downstreamReceive? > > We are going to run out of names fast! > > Why not a convention for these names to avoid this? > > One way to disambiguate and relieve the name stress (and stress of naming) > is to apt adopt this convention: > > downstreamApply > > That is, prepend interface name to method name. In this case all the > method names could be simply 'apply' because we already know the interrace > names are unique. I don't have a dog in that hunt. > > Joe > On Jan 10, 2013 8:01 AM, "Tim Peierls" wrote: > >> How about Acceptor? >> >> On Thu, Jan 10, 2013 at 10:54 AM, Brian Goetz wrote: >> >>> Downstream: >>> >>> /** A collector for values associated with a given input. Values >>> can be >>> * yielded individually, or in aggregates such as collections, >>> arrays, or >>> * streams; aggregates are flattened, so that yielding an array >>> containing >>> * [1, 2] is equivalent to yield(1); yield(2). >>> */ >>> interface Downstream { >>> void yield(U element); >>> >>> default void yield(Collection collection) { >>> for (U u : collection) >>> yield(u); >>> } >>> >>> default void yield(U[] array) { >>> for (U u : array) >>> yield(u); >>> } >>> >>> default void yield(Stream stream) { >>> stream.forEach(this::yield); >>> } >>> } >>> >>> The basic idea is that this is a collector for values. It was at one >>> point called "collector" but now we have something else called Collector. >> >> >> >> From brian.goetz at oracle.com Thu Jan 10 08:19:54 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 10 Jan 2013 11:19:54 -0500 Subject: One more pass on flatMap/mapMulti In-Reply-To: References: <50EED9F3.3040600@oracle.com> <50EEDFBF.8020204@univ-mlv.fr> <50EEE433.40502@oracle.com> Message-ID: <50EEEA2A.2060708@oracle.com> DownstreamCollector? On 1/10/2013 11:01 AM, Tim Peierls wrote: > How about Acceptor? > > On Thu, Jan 10, 2013 at 10:54 AM, Brian Goetz > wrote: > > Downstream: > > /** A collector for values associated with a given input. > Values can be > * yielded individually, or in aggregates such as collections, > arrays, or > * streams; aggregates are flattened, so that yielding an array > containing > * [1, 2] is equivalent to yield(1); yield(2). > */ > interface Downstream { > void yield(U element); > > default void yield(Collection collection) { > for (U u : collection) > yield(u); > } > > default void yield(U[] array) { > for (U u : array) > yield(u); > } > > default void yield(Stream stream) { > stream.forEach(this::yield); > } > } > > The basic idea is that this is a collector for values. It was at > one point called "collector" but now we have something else called > Collector. > > > From joe.bowbeer at gmail.com Thu Jan 10 08:24:01 2013 From: joe.bowbeer at gmail.com (Joe Bowbeer) Date: Thu, 10 Jan 2013 08:24:01 -0800 Subject: One more pass on flatMap/mapMulti In-Reply-To: <50EEEA2A.2060708@oracle.com> References: <50EED9F3.3040600@oracle.com> <50EEDFBF.8020204@univ-mlv.fr> <50EEE433.40502@oracle.com> <50EEEA2A.2060708@oracle.com> Message-ID: +1 DownsteamCollector I hope you can find a replacement for yield. On Jan 10, 2013 8:20 AM, "Brian Goetz" wrote: > DownstreamCollector? > > On 1/10/2013 11:01 AM, Tim Peierls wrote: > >> How about Acceptor? >> >> On Thu, Jan 10, 2013 at 10:54 AM, Brian Goetz > > wrote: >> >> Downstream: >> >> /** A collector for values associated with a given input. >> Values can be >> * yielded individually, or in aggregates such as collections, >> arrays, or >> * streams; aggregates are flattened, so that yielding an array >> containing >> * [1, 2] is equivalent to yield(1); yield(2). >> */ >> interface Downstream { >> void yield(U element); >> >> default void yield(Collection collection) { >> for (U u : collection) >> yield(u); >> } >> >> default void yield(U[] array) { >> for (U u : array) >> yield(u); >> } >> >> default void yield(Stream stream) { >> stream.forEach(this::yield); >> } >> } >> >> The basic idea is that this is a collector for values. It was at >> one point called "collector" but now we have something else called >> Collector. >> >> >> >> From brian.goetz at oracle.com Thu Jan 10 08:35:52 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 10 Jan 2013 11:35:52 -0500 Subject: One more pass on flatMap/mapMulti In-Reply-To: References: <50EED9F3.3040600@oracle.com> <50EEDFBF.8020204@univ-mlv.fr> <50EEE433.40502@oracle.com> <50EEEA2A.2060708@oracle.com> Message-ID: <50EEEDE8.8000503@oracle.com> downstream.send? On 1/10/2013 11:24 AM, Joe Bowbeer wrote: > +1 DownsteamCollector > > I hope you can find a replacement for yield. > > On Jan 10, 2013 8:20 AM, "Brian Goetz" > wrote: > > DownstreamCollector? > > On 1/10/2013 11:01 AM, Tim Peierls wrote: > > How about Acceptor? > > On Thu, Jan 10, 2013 at 10:54 AM, Brian Goetz > > __>> wrote: > > Downstream: > > /** A collector for values associated with a given input. > Values can be > * yielded individually, or in aggregates such as > collections, > arrays, or > * streams; aggregates are flattened, so that yielding > an array > containing > * [1, 2] is equivalent to yield(1); yield(2). > */ > interface Downstream { > void yield(U element); > > default void yield(Collection collection) { > for (U u : collection) > yield(u); > } > > default void yield(U[] array) { > for (U u : array) > yield(u); > } > > default void yield(Stream stream) { > stream.forEach(this::yield); > } > } > > The basic idea is that this is a collector for values. It > was at > one point called "collector" but now we have something else > called > Collector. > > > From joe.bowbeer at gmail.com Thu Jan 10 08:49:53 2013 From: joe.bowbeer at gmail.com (Joe Bowbeer) Date: Thu, 10 Jan 2013 08:49:53 -0800 Subject: One more pass on flatMap/mapMulti In-Reply-To: <50EEEDE8.8000503@oracle.com> References: <50EED9F3.3040600@oracle.com> <50EEDFBF.8020204@univ-mlv.fr> <50EEE433.40502@oracle.com> <50EEEA2A.2060708@oracle.com> <50EEEDE8.8000503@oracle.com> Message-ID: downstream.send works for me. On Jan 10, 2013 8:36 AM, "Brian Goetz" wrote: > downstream.send? > > > On 1/10/2013 11:24 AM, Joe Bowbeer wrote: > >> +1 DownsteamCollector >> >> I hope you can find a replacement for yield. >> >> On Jan 10, 2013 8:20 AM, "Brian Goetz" > > wrote: >> >> DownstreamCollector? >> >> On 1/10/2013 11:01 AM, Tim Peierls wrote: >> >> How about Acceptor? >> >> On Thu, Jan 10, 2013 at 10:54 AM, Brian Goetz >> >> > __>> wrote: >> >> Downstream: >> >> /** A collector for values associated with a given >> input. >> Values can be >> * yielded individually, or in aggregates such as >> collections, >> arrays, or >> * streams; aggregates are flattened, so that yielding >> an array >> containing >> * [1, 2] is equivalent to yield(1); yield(2). >> */ >> interface Downstream { >> void yield(U element); >> >> default void yield(Collection collection) { >> for (U u : collection) >> yield(u); >> } >> >> default void yield(U[] array) { >> for (U u : array) >> yield(u); >> } >> >> default void yield(Stream stream) { >> stream.forEach(this::yield); >> } >> } >> >> The basic idea is that this is a collector for values. It >> was at >> one point called "collector" but now we have something else >> called >> Collector. >> >> >> >> From brian.goetz at oracle.com Thu Jan 10 08:59:39 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 10 Jan 2013 11:59:39 -0500 Subject: One more pass on flatMap/mapMulti In-Reply-To: References: <50EED9F3.3040600@oracle.com> <50EEDFBF.8020204@univ-mlv.fr> <50EEE433.40502@oracle.com> <50EEEA2A.2060708@oracle.com> <50EEEDE8.8000503@oracle.com> Message-ID: <50EEF37B.10206@oracle.com> Checkpointing: pushed new version with: - Multifunction and IntMultifunction gone; - MF.Collector moved to {Stream,IntStream}.Downstream - Downstream methods renamed to 'send' - mapMulti renamed to 'explode' Bikeshed discussions can continue. On 1/10/2013 11:49 AM, Joe Bowbeer wrote: > downstream.send works for me. > > On Jan 10, 2013 8:36 AM, "Brian Goetz" > wrote: > > downstream.send? > > > On 1/10/2013 11:24 AM, Joe Bowbeer wrote: > > +1 DownsteamCollector > > I hope you can find a replacement for yield. > > On Jan 10, 2013 8:20 AM, "Brian Goetz" > __>> wrote: > > DownstreamCollector? > > On 1/10/2013 11:01 AM, Tim Peierls wrote: > > How about Acceptor? > > On Thu, Jan 10, 2013 at 10:54 AM, Brian Goetz > > __> > > __>__>> wrote: > > Downstream: > > /** A collector for values associated with a > given input. > Values can be > * yielded individually, or in aggregates such as > collections, > arrays, or > * streams; aggregates are flattened, so that > yielding > an array > containing > * [1, 2] is equivalent to yield(1); yield(2). > */ > interface Downstream { > void yield(U element); > > default void yield(Collection > collection) { > for (U u : collection) > yield(u); > } > > default void yield(U[] array) { > for (U u : array) > yield(u); > } > > default void yield(Stream stream) { > stream.forEach(this::yield); > } > } > > The basic idea is that this is a collector for > values. It > was at > one point called "collector" but now we have > something else > called > Collector. > > > From tim at peierls.net Thu Jan 10 09:28:02 2013 From: tim at peierls.net (Tim Peierls) Date: Thu, 10 Jan 2013 12:28:02 -0500 Subject: One more pass on flatMap/mapMulti In-Reply-To: <50EEF37B.10206@oracle.com> References: <50EED9F3.3040600@oracle.com> <50EEDFBF.8020204@univ-mlv.fr> <50EEE433.40502@oracle.com> <50EEEA2A.2060708@oracle.com> <50EEEDE8.8000503@oracle.com> <50EEF37B.10206@oracle.com> Message-ID: On Thu, Jan 10, 2013 at 11:59 AM, Brian Goetz wrote: > Bikeshed discussions can continue. OK, then! :-) The method name "send" is OK, but you wouldn't be seeing it in the signature of explode. What you would see is Downstream, which still bugs me. The word "downstream" is an adverb or adjective, not a noun. Things are sent downstream, they aren't sent *to* a downstream. Was Acceptor.accept proposed and rejected already? If so, why? I would understand explode(BiBlock**, T>)) --tim From brian.goetz at oracle.com Thu Jan 10 09:35:51 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 10 Jan 2013 12:35:51 -0500 Subject: One more pass on flatMap/mapMulti In-Reply-To: References: <50EED9F3.3040600@oracle.com> <50EEDFBF.8020204@univ-mlv.fr> <50EEE433.40502@oracle.com> <50EEEA2A.2060708@oracle.com> <50EEEDE8.8000503@oracle.com> <50EEF37B.10206@oracle.com> Message-ID: <50EEFBF7.9050900@oracle.com> I like the idea that whatever is passed to the block describes the downstream; the role of the block is to emit values downstream. I think that makes things clearer about what's going on, which is good because what is going on is already not all that clear. I buy that "Downstream" could use some nounification. (Calvin say: verbing weirds language.) DownstreamCollector DownstreamAcceptor DownstreamHandler accept() is OK, and we can make it extend Block. On 1/10/2013 12:28 PM, Tim Peierls wrote: > On Thu, Jan 10, 2013 at 11:59 AM, Brian Goetz > wrote: > > Bikeshed discussions can continue. > > > OK, then! :-) > > The method name "send" is OK, but you wouldn't be seeing it in the > signature of explode. What you would see is Downstream, which still > bugs me. The word "downstream" is an adverb or adjective, not a noun. > Things are sent downstream, they aren't sent *to* a downstream. > > Was Acceptor.accept proposed and rejected already? If so, why? I would > understand > > explode(BiBlock__, T>)) > > --tim From tim at peierls.net Thu Jan 10 09:41:00 2013 From: tim at peierls.net (Tim Peierls) Date: Thu, 10 Jan 2013 12:41:00 -0500 Subject: One more pass on flatMap/mapMulti In-Reply-To: <50EEFBF7.9050900@oracle.com> References: <50EED9F3.3040600@oracle.com> <50EEDFBF.8020204@univ-mlv.fr> <50EEE433.40502@oracle.com> <50EEEA2A.2060708@oracle.com> <50EEEDE8.8000503@oracle.com> <50EEF37B.10206@oracle.com> <50EEFBF7.9050900@oracle.com> Message-ID: Any of those three are an improvement, and extending Block seems reasonable. (Though I still hesitate every time I see "Block" or "BiBlock" -- the latter looks like "bib lock", something you would use to prevent an infant from removing protective clothing.) On Thu, Jan 10, 2013 at 12:35 PM, Brian Goetz wrote: > I like the idea that whatever is passed to the block describes the > downstream; the role of the block is to emit values downstream. I think > that makes things clearer about what's going on, which is good because what > is going on is already not all that clear. > > I buy that "Downstream" could use some nounification. (Calvin say: > verbing weirds language.) > > DownstreamCollector > DownstreamAcceptor > DownstreamHandler > > accept() is OK, and we can make it extend Block. > > > On 1/10/2013 12:28 PM, Tim Peierls wrote: > >> On Thu, Jan 10, 2013 at 11:59 AM, Brian Goetz > > wrote: >> >> Bikeshed discussions can continue. >> >> >> OK, then! :-) >> >> The method name "send" is OK, but you wouldn't be seeing it in the >> signature of explode. What you would see is Downstream, which still >> bugs me. The word "downstream" is an adverb or adjective, not a noun. >> Things are sent downstream, they aren't sent *to* a downstream. >> >> >> Was Acceptor.accept proposed and rejected already? If so, why? I would >> understand >> >> explode(BiBlock__, T>)) >> >> --tim >> > From forax at univ-mlv.fr Thu Jan 10 09:43:04 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Thu, 10 Jan 2013 18:43:04 +0100 Subject: One more pass on flatMap/mapMulti In-Reply-To: References: <50EED9F3.3040600@oracle.com> <50EEDFBF.8020204@univ-mlv.fr> <50EEE433.40502@oracle.com> <50EEEA2A.2060708@oracle.com> <50EEEDE8.8000503@oracle.com> <50EEF37B.10206@oracle.com> Message-ID: <50EEFDA8.8010605@univ-mlv.fr> On 01/10/2013 06:28 PM, Tim Peierls wrote: > On Thu, Jan 10, 2013 at 11:59 AM, Brian Goetz > wrote: > > Bikeshed discussions can continue. > > > OK, then! :-) > > The method name "send" is OK, but you wouldn't be seeing it in the > signature of explode. What you would see is Downstream, which still > bugs me. The word "downstream" is an adverb or adjective, not a noun. > Things are sent downstream, they aren't sent *to* a downstream. > > Was Acceptor.accept proposed and rejected already? If so, why? I would > understand > > explode(BiBlock, T>)) wildcard intervention: explode(BiBlock, ? super T>)) > > --tim R?mi From forax at univ-mlv.fr Thu Jan 10 09:47:30 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Thu, 10 Jan 2013 18:47:30 +0100 Subject: One more pass on flatMap/mapMulti In-Reply-To: <50EEE171.3080005@cs.oswego.edu> References: <50EED9F3.3040600@oracle.com> <50EEDFBF.8020204@univ-mlv.fr> <50EEE171.3080005@cs.oswego.edu> Message-ID: <50EEFEB2.400@univ-mlv.fr> On 01/10/2013 04:42 PM, Doug Lea wrote: > On 01/10/13 10:35, Remi Forax wrote: > >>> >>> mapMulti is still not a great name. How about "explode" ? >>> >> >> yes, explode is better. >> > > ... because that is what will happen with people's memory consumption > if they don't do the right thing and merge while collecting :-) I think that the implementation of the pipeline can guarantee that in the worst case, you don't have more than the memory needed to store the generated elements for one element (for each core :( ). > > -Doug > > R?mi From joe.bowbeer at gmail.com Thu Jan 10 10:28:14 2013 From: joe.bowbeer at gmail.com (Joe Bowbeer) Date: Thu, 10 Jan 2013 10:28:14 -0800 Subject: Anagrams Kata (Was: ConcurrentHashMap/ConcurrentMap/Map.compute) Message-ID: Thanks for the suggestions Paul. After updating to jdk8lambda b72, the anagram collector is now expressed as: map = r.lines().accumulate(Accumulators.groupBy(Anagrams::key)); Notes: 1. Adding .parallel() after lines() works, but in my tests on a dual-core laptop, it increased the accumulate time by an order of magnitude. 2. The Accumulators.<> type specifier is currently required, unfortunately. The complete source (all 31 lines) is at bitbucket.org/joebowbeer/anagrams/src/ In my trials, I'm using the long list of single words from http://icon.shef.ac.uk/Moby/mwords.html Joe On Wed, Jan 9, 2013 at 1:15 AM, Paul Sandoz wrote: > > On Jan 9, 2013, at 7:47 AM, Joe Bowbeer wrote: > > > In the javadoc for Map.compute in b72, there is an error in the sample > > snippet: > > > > http://download.java.net/lambda/b72/docs/api/java/util/Map.html > > > > Attempts to compute a mapping for the specified key and its current > mapped > >> value (or null if there is no current mapping). For example, to either > >> create or append a String msg to a value mapping: > >> map.compute(key, (k, v, msg) -> (v == null) ? msg : v.concat(msg)) > > > > > > Error: BiFunction does not accept three arguments. In particular, msg is > > extraneous. It should be defined in the lexical scope? > > > > > > Btw, I pondered how to efficiently use compute() or merge() to simulate a > > multi-map and I eventually gave up. > > > > I eventually wrote the following, which accumulates lists of anagrams, > > given a list of words: > > > > r.lines().forEach(s -> map.computeIfAbsent(key(s), k -> new > > ArrayList<>()).add(s)); > > > > Have you tried using the latest groupBy functionality? e.g.: > > Map> anagrams = > r.lines().parallel().reduce(groupBy(s -> key(s))); > > It would be interesting to compare performance of reduce vs. > reduceUnordered and the concurrent groupBy leveraging CHM. > > > > > > Where key() returns the canonical key for a given word, and r is a > > BufferedReader for the dictionary file. > > > > The following line prints the lists of anagrams: > > > > map.values().stream().filter(v -> v.size() > 1).forEach(v -> > > System.out.println(v)); > > > > Little style tip: > > forEach(System.out::println) > > Paul. From paul.sandoz at oracle.com Thu Jan 10 12:55:37 2013 From: paul.sandoz at oracle.com (Paul Sandoz) Date: Thu, 10 Jan 2013 21:55:37 +0100 Subject: Anagrams Kata (Was: ConcurrentHashMap/ConcurrentMap/Map.compute) In-Reply-To: References: Message-ID: <9C7AA102-1BE1-40FC-AE7F-6A711E29489C@oracle.com> On Jan 10, 2013, at 7:28 PM, Joe Bowbeer wrote: > Thanks for the suggestions Paul. > > After updating to jdk8lambda b72, the anagram collector is now expressed as: > > map = r.lines().accumulate(Accumulators.groupBy(Anagrams::key)); > > Notes: > > 1. Adding .parallel() after lines() works, but in my tests on a dual-core laptop, it increased the accumulate time by an order of magnitude. > I would need to look at this more closely tomorrow but i suspect it is due to two things: 1) an terribly unbalanced tree is created (using a spliterator from an iterator since there is no size information); and 2) the map merging are both contributing to the slow down. Need to try the concurrent groupBy. Things are moving so fast i am forgetting if b72 has that, oh for the need of continuous builds! If there are 354,984 words then that will make a tree of at least 354,984/1024 leaf nodes with the ~ the same depth using the current spliterator from iterator technique. A simple verification is to load up the words into memory as a list and use that list as the source of words. Paul. > 2. The Accumulators.<> type specifier is currently required, unfortunately. > > The complete source (all 31 lines) is at bitbucket.org/joebowbeer/anagrams/src/ > > In my trials, I'm using the long list of single words from http://icon.shef.ac.uk/Moby/mwords.html > > Joe > > On Wed, Jan 9, 2013 at 1:15 AM, Paul Sandoz wrote: > > On Jan 9, 2013, at 7:47 AM, Joe Bowbeer wrote: > > > In the javadoc for Map.compute in b72, there is an error in the sample > > snippet: > > > > http://download.java.net/lambda/b72/docs/api/java/util/Map.html > > > > Attempts to compute a mapping for the specified key and its current mapped > >> value (or null if there is no current mapping). For example, to either > >> create or append a String msg to a value mapping: > >> map.compute(key, (k, v, msg) -> (v == null) ? msg : v.concat(msg)) > > > > > > Error: BiFunction does not accept three arguments. In particular, msg is > > extraneous. It should be defined in the lexical scope? > > > > > > Btw, I pondered how to efficiently use compute() or merge() to simulate a > > multi-map and I eventually gave up. > > > > I eventually wrote the following, which accumulates lists of anagrams, > > given a list of words: > > > > r.lines().forEach(s -> map.computeIfAbsent(key(s), k -> new > > ArrayList<>()).add(s)); > > > > Have you tried using the latest groupBy functionality? e.g.: > > Map> anagrams = r.lines().parallel().reduce(groupBy(s -> key(s))); > > It would be interesting to compare performance of reduce vs. reduceUnordered and the concurrent groupBy leveraging CHM. > > > > > > Where key() returns the canonical key for a given word, and r is a > > BufferedReader for the dictionary file. > > > > The following line prints the lists of anagrams: > > > > map.values().stream().filter(v -> v.size() > 1).forEach(v -> > > System.out.println(v)); > > > > Little style tip: > > forEach(System.out::println) > > Paul. > From joe.bowbeer at gmail.com Thu Jan 10 16:24:41 2013 From: joe.bowbeer at gmail.com (Joe Bowbeer) Date: Thu, 10 Jan 2013 16:24:41 -0800 Subject: Anagrams Kata (Was: ConcurrentHashMap/ConcurrentMap/Map.compute) In-Reply-To: <9C7AA102-1BE1-40FC-AE7F-6A711E29489C@oracle.com> References: <9C7AA102-1BE1-40FC-AE7F-6A711E29489C@oracle.com> Message-ID: Thanks Paul. To investigate, I pre-read all the lines: List lines = Files.readAllLines(path, Charset.defaultCharset()); and then profiled the three variants below on a dual-core desktop: 1. map = lines.stream().accumulate(Accumulators.groupBy(Anagrams::key)); 2. map = lines.parallelStream().accumulate(Accumulators.groupBy(Anagrams::key)); 3. map = lines.stream().parallel().accumulate(Accumulators.groupBy(Anagrams::key)); Results: 1. stream accumulate = 260ms sort = 80ms toLowercase = 20ms 2. parallelStream accumulate = 1342ms sort = 283ms toLowercase = 108ms 3. stream().parallel() accumulate = 1186ms sort = 629ms toLowercase = 826ms This is a slow-down (5x more work), though not as dramatic as what happened in the original case: Originally, the expression was one of: 1. map = r.lines().accumulate(Accumulators.groupBy(Anagrams::key)); 2. map = r.lines().parallel().accumulate(Accumulators.groupBy(Anagrams::key)); Results: 1. lines() accumulate = 396ms sort = 121ms toLowercase = 76ms 2. lines().parallel() accumulate = 20010ms! sort = 801ms toLowercase = ~ms --Joe On Thu, Jan 10, 2013 at 12:55 PM, Paul Sandoz wrote: > > On Jan 10, 2013, at 7:28 PM, Joe Bowbeer wrote: > > > Thanks for the suggestions Paul. > > > > After updating to jdk8lambda b72, the anagram collector is now expressed > as: > > > > map = r.lines().accumulate(Accumulators. String>groupBy(Anagrams::key)); > > > > Notes: > > > > 1. Adding .parallel() after lines() works, but in my tests on a > dual-core laptop, it increased the accumulate time by an order of magnitude. > > > > I would need to look at this more closely tomorrow but i suspect it is due > to two things: 1) an terribly unbalanced tree is created (using a > spliterator from an iterator since there is no size information); and 2) > the map merging are both contributing to the slow down. > > Need to try the concurrent groupBy. Things are moving so fast i am > forgetting if b72 has that, oh for the need of continuous builds! > > If there are 354,984 words then that will make a tree of at least > 354,984/1024 leaf nodes with the ~ the same depth using the current > spliterator from iterator technique. > > A simple verification is to load up the words into memory as a list and > use that list as the source of words. > > Paul. > > > 2. The Accumulators.<> type specifier is currently required, > unfortunately. > > > > The complete source (all 31 lines) is at > bitbucket.org/joebowbeer/anagrams/src/ > > > > In my trials, I'm using the long list of single words from > http://icon.shef.ac.uk/Moby/mwords.html > > > > Joe > > > > On Wed, Jan 9, 2013 at 1:15 AM, Paul Sandoz > wrote: > > > > On Jan 9, 2013, at 7:47 AM, Joe Bowbeer wrote: > > > > > In the javadoc for Map.compute in b72, there is an error in the sample > > > snippet: > > > > > > http://download.java.net/lambda/b72/docs/api/java/util/Map.html > > > > > > Attempts to compute a mapping for the specified key and its current > mapped > > >> value (or null if there is no current mapping). For example, to either > > >> create or append a String msg to a value mapping: > > >> map.compute(key, (k, v, msg) -> (v == null) ? msg : v.concat(msg)) > > > > > > > > > Error: BiFunction does not accept three arguments. In particular, msg > is > > > extraneous. It should be defined in the lexical scope? > > > > > > > > > Btw, I pondered how to efficiently use compute() or merge() to > simulate a > > > multi-map and I eventually gave up. > > > > > > I eventually wrote the following, which accumulates lists of anagrams, > > > given a list of words: > > > > > > r.lines().forEach(s -> map.computeIfAbsent(key(s), k -> new > > > ArrayList<>()).add(s)); > > > > > > > Have you tried using the latest groupBy functionality? e.g.: > > > > Map> anagrams = > r.lines().parallel().reduce(groupBy(s -> key(s))); > > > > It would be interesting to compare performance of reduce vs. > reduceUnordered and the concurrent groupBy leveraging CHM. > > > > > > > > > > Where key() returns the canonical key for a given word, and r is a > > > BufferedReader for the dictionary file. > > > > > > The following line prints the lists of anagrams: > > > > > > map.values().stream().filter(v -> v.size() > 1).forEach(v -> > > > System.out.println(v)); > > > > > > > Little style tip: > > > > forEach(System.out::println) > > > > Paul. > > > > From brian.goetz at oracle.com Thu Jan 10 16:29:10 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 10 Jan 2013 19:29:10 -0500 Subject: Anagrams Kata (Was: ConcurrentHashMap/ConcurrentMap/Map.compute) In-Reply-To: References: <9C7AA102-1BE1-40FC-AE7F-6A711E29489C@oracle.com> Message-ID: <50EF5CD6.6020607@oracle.com> The sorts are all serial. So what is happening there is that instead of sorting one big thing, you are sorting lots of small things. The break-even on parallel groupBy may be high because of merging overhead, especially if we've got our split thresholds tuned too low, in which case we create lots of little maps and spend a lot of time merging them. On 1/10/2013 7:24 PM, Joe Bowbeer wrote: > Thanks Paul. > > To investigate, I pre-read all the lines: > > List lines = Files.readAllLines(path, Charset.defaultCharset()); > > and then profiled the three variants below on a dual-core desktop: > > 1. map = lines.stream().accumulate(Accumulators. String>groupBy(Anagrams::key)); > 2. map = lines.parallelStream().accumulate(Accumulators. String>groupBy(Anagrams::key)); > 3. map = lines.stream().parallel().accumulate(Accumulators. String>groupBy(Anagrams::key)); > > Results: > > 1. stream > > accumulate = 260ms > sort = 80ms > toLowercase = 20ms > > 2. parallelStream > > accumulate = 1342ms > sort = 283ms > toLowercase = 108ms > > 3. stream().parallel() > > accumulate = 1186ms > sort = 629ms > toLowercase = 826ms > > > This is a slow-down (5x more work), though not as dramatic as what > happened in the original case: > > Originally, the expression was one of: > > 1. map = r.lines().accumulate(Accumulators. String>groupBy(Anagrams::key)); > 2. map = r.lines().parallel().accumulate(Accumulators. String>groupBy(Anagrams::key)); > > Results: > > 1. lines() > > accumulate = 396ms > sort = 121ms > toLowercase = 76ms > > 2. lines().parallel() > > accumulate = 20010ms! > sort = 801ms > toLowercase = ~ms > > > --Joe > > > On Thu, Jan 10, 2013 at 12:55 PM, Paul Sandoz > wrote: > > > On Jan 10, 2013, at 7:28 PM, Joe Bowbeer > wrote: > > > Thanks for the suggestions Paul. > > > > After updating to jdk8lambda b72, the anagram collector is now > expressed as: > > > > map = r.lines().accumulate(Accumulators. String>groupBy(Anagrams::key)); > > > > Notes: > > > > 1. Adding .parallel() after lines() works, but in my tests on a > dual-core laptop, it increased the accumulate time by an order of > magnitude. > > > > I would need to look at this more closely tomorrow but i suspect it > is due to two things: 1) an terribly unbalanced tree is created > (using a spliterator from an iterator since there is no size > information); and 2) the map merging are both contributing to the > slow down. > > Need to try the concurrent groupBy. Things are moving so fast i am > forgetting if b72 has that, oh for the need of continuous builds! > > If there are 354,984 words then that will make a tree of at least > 354,984/1024 leaf nodes with the ~ the same depth using the current > spliterator from iterator technique. > > A simple verification is to load up the words into memory as a list > and use that list as the source of words. > > Paul. > > > 2. The Accumulators.<> type specifier is currently required, > unfortunately. > > > > The complete source (all 31 lines) is at > bitbucket.org/joebowbeer/anagrams/src/ > > > > > In my trials, I'm using the long list of single words from > http://icon.shef.ac.uk/Moby/mwords.html > > > > Joe > > > > On Wed, Jan 9, 2013 at 1:15 AM, Paul Sandoz > > wrote: > > > > On Jan 9, 2013, at 7:47 AM, Joe Bowbeer > wrote: > > > > > In the javadoc for Map.compute in b72, there is an error in the > sample > > > snippet: > > > > > > http://download.java.net/lambda/b72/docs/api/java/util/Map.html > > > > > > Attempts to compute a mapping for the specified key and its > current mapped > > >> value (or null if there is no current mapping). For example, > to either > > >> create or append a String msg to a value mapping: > > >> map.compute(key, (k, v, msg) -> (v == null) ? msg : v.concat(msg)) > > > > > > > > > Error: BiFunction does not accept three arguments. In > particular, msg is > > > extraneous. It should be defined in the lexical scope? > > > > > > > > > Btw, I pondered how to efficiently use compute() or merge() to > simulate a > > > multi-map and I eventually gave up. > > > > > > I eventually wrote the following, which accumulates lists of > anagrams, > > > given a list of words: > > > > > > r.lines().forEach(s -> map.computeIfAbsent(key(s), k -> new > > > ArrayList<>()).add(s)); > > > > > > > Have you tried using the latest groupBy functionality? e.g.: > > > > Map> anagrams = > r.lines().parallel().reduce(groupBy(s -> key(s))); > > > > It would be interesting to compare performance of reduce vs. > reduceUnordered and the concurrent groupBy leveraging CHM. > > > > > > > > > > Where key() returns the canonical key for a given word, and r is a > > > BufferedReader for the dictionary file. > > > > > > The following line prints the lists of anagrams: > > > > > > map.values().stream().filter(v -> v.size() > 1).forEach(v -> > > > System.out.println(v)); > > > > > > > Little style tip: > > > > forEach(System.out::println) > > > > Paul. > > > > From joe.bowbeer at gmail.com Thu Jan 10 16:46:36 2013 From: joe.bowbeer at gmail.com (Joe Bowbeer) Date: Thu, 10 Jan 2013 16:46:36 -0800 Subject: Anagrams Kata (Was: ConcurrentHashMap/ConcurrentMap/Map.compute) In-Reply-To: <50EF5CD6.6020607@oracle.com> References: <9C7AA102-1BE1-40FC-AE7F-6A711E29489C@oracle.com> <50EF5CD6.6020607@oracle.com> Message-ID: Note that the toLowercase/sort work is contained in the groupBy classifier. The classifier groups each word according to its characters. Even in the ideal parallel execution, all of the sorts are performed on small arrays of characters. On Thu, Jan 10, 2013 at 4:29 PM, Brian Goetz wrote: > The sorts are all serial. So what is happening there is that instead of > sorting one big thing, you are sorting lots of small things. > > The break-even on parallel groupBy may be high because of merging > overhead, especially if we've got our split thresholds tuned too low, in > which case we create lots of little maps and spend a lot of time merging > them. > > > > > On 1/10/2013 7:24 PM, Joe Bowbeer wrote: > >> Thanks Paul. >> >> To investigate, I pre-read all the lines: >> >> List lines = Files.readAllLines(path, Charset.defaultCharset()); >> >> and then profiled the three variants below on a dual-core desktop: >> >> 1. map = lines.stream().accumulate(**Accumulators.> String>groupBy(Anagrams::key))**; >> 2. map = lines.parallelStream().**accumulate(Accumulators.<**String, >> String>groupBy(Anagrams::key))**; >> 3. map = lines.stream().parallel().**accumulate(Accumulators.<**String, >> String>groupBy(Anagrams::key))**; >> >> Results: >> >> 1. stream >> >> accumulate = 260ms >> sort = 80ms >> toLowercase = 20ms >> >> 2. parallelStream >> >> accumulate = 1342ms >> sort = 283ms >> toLowercase = 108ms >> >> 3. stream().parallel() >> >> accumulate = 1186ms >> sort = 629ms >> toLowercase = 826ms >> >> >> This is a slow-down (5x more work), though not as dramatic as what >> happened in the original case: >> >> Originally, the expression was one of: >> >> 1. map = r.lines().accumulate(**Accumulators.> String>groupBy(Anagrams::key))**; >> 2. map = r.lines().parallel().**accumulate(Accumulators.<**String, >> String>groupBy(Anagrams::key))**; >> >> Results: >> >> 1. lines() >> >> accumulate = 396ms >> sort = 121ms >> toLowercase = 76ms >> >> 2. lines().parallel() >> >> accumulate = 20010ms! >> sort = 801ms >> toLowercase = ~ms >> >> >> --Joe >> >> >> On Thu, Jan 10, 2013 at 12:55 PM, Paul Sandoz > > wrote: >> >> >> On Jan 10, 2013, at 7:28 PM, Joe Bowbeer > **> wrote: >> >> > Thanks for the suggestions Paul. >> > >> > After updating to jdk8lambda b72, the anagram collector is now >> expressed as: >> > >> > map = r.lines().accumulate(**Accumulators.> String>groupBy(Anagrams::key))**; >> > >> > Notes: >> > >> > 1. Adding .parallel() after lines() works, but in my tests on a >> dual-core laptop, it increased the accumulate time by an order of >> magnitude. >> > >> >> I would need to look at this more closely tomorrow but i suspect it >> is due to two things: 1) an terribly unbalanced tree is created >> (using a spliterator from an iterator since there is no size >> information); and 2) the map merging are both contributing to the >> slow down. >> >> Need to try the concurrent groupBy. Things are moving so fast i am >> forgetting if b72 has that, oh for the need of continuous builds! >> >> If there are 354,984 words then that will make a tree of at least >> 354,984/1024 leaf nodes with the ~ the same depth using the current >> spliterator from iterator technique. >> >> A simple verification is to load up the words into memory as a list >> and use that list as the source of words. >> >> Paul. >> >> > 2. The Accumulators.<> type specifier is currently required, >> unfortunately. >> > >> > The complete source (all 31 lines) is at >> bitbucket.org/joebowbeer/**anagrams/src/ >> >> > >> >> > >> > In my trials, I'm using the long list of single words from >> http://icon.shef.ac.uk/Moby/**mwords.html >> > >> > Joe >> > >> > On Wed, Jan 9, 2013 at 1:15 AM, Paul Sandoz >> > wrote: >> > >> > On Jan 9, 2013, at 7:47 AM, Joe Bowbeer > **> wrote: >> > >> > > In the javadoc for Map.compute in b72, there is an error in the >> sample >> > > snippet: >> > > >> > > http://download.java.net/**lambda/b72/docs/api/java/util/** >> Map.html >> > > >> > > Attempts to compute a mapping for the specified key and its >> current mapped >> > >> value (or null if there is no current mapping). For example, >> to either >> > >> create or append a String msg to a value mapping: >> > >> map.compute(key, (k, v, msg) -> (v == null) ? msg : >> v.concat(msg)) >> > > >> > > >> > > Error: BiFunction does not accept three arguments. In >> particular, msg is >> > > extraneous. It should be defined in the lexical scope? >> > > >> > > >> > > Btw, I pondered how to efficiently use compute() or merge() to >> simulate a >> > > multi-map and I eventually gave up. >> > > >> > > I eventually wrote the following, which accumulates lists of >> anagrams, >> > > given a list of words: >> > > >> > > r.lines().forEach(s -> map.computeIfAbsent(key(s), k -> new >> > > ArrayList<>()).add(s)); >> > > >> > >> > Have you tried using the latest groupBy functionality? e.g.: >> > >> > Map> anagrams = >> r.lines().parallel().reduce(**groupBy(s -> key(s))); >> > >> > It would be interesting to compare performance of reduce vs. >> reduceUnordered and the concurrent groupBy leveraging CHM. >> > >> > >> > > >> > > Where key() returns the canonical key for a given word, and r is >> a >> > > BufferedReader for the dictionary file. >> > > >> > > The following line prints the lists of anagrams: >> > > >> > > map.values().stream().filter(v -> v.size() > 1).forEach(v -> >> > > System.out.println(v)); >> > > >> > >> > Little style tip: >> > >> > forEach(System.out::println) >> > >> > Paul. >> > >> >> >> From Vladimir.Zakharov at gs.com Thu Jan 10 19:51:41 2013 From: Vladimir.Zakharov at gs.com (Zakharov, Vladimir) Date: Thu, 10 Jan 2013 22:51:41 -0500 Subject: One more pass on flatMap/mapMulti In-Reply-To: <50EEFBF7.9050900@oracle.com> References: <50EED9F3.3040600@oracle.com> <50EEDFBF.8020204@univ-mlv.fr> <50EEE433.40502@oracle.com> <50EEEA2A.2060708@oracle.com> <50EEEDE8.8000503@oracle.com> <50EEF37B.10206@oracle.com> <50EEFBF7.9050900@oracle.com> Message-ID: +1 DownstreamCollector The Goldman Sachs Group, Inc. All rights reserved. See http://www.gs.com/disclaimer/global_email for important risk disclosures, conflicts of interest and other terms and conditions relating to this e-mail and your reliance on information contained in it.? This message may contain confidential or privileged information.? If you are not the intended recipient, please advise us immediately and delete this message.? See http://www.gs.com/disclaimer/email for further information on confidentiality and the risks of non-secure electronic communication.? If you cannot access these links, please notify us by reply message and we will send the contents to you.? -----Original Message----- From: lambda-libs-spec-experts-bounces at openjdk.java.net [mailto:lambda-libs-spec-experts-bounces at openjdk.java.net] On Behalf Of Brian Goetz Sent: Thursday, January 10, 2013 12:36 PM To: Tim Peierls Cc: lambda-libs-spec-experts at openjdk.java.net Subject: Re: One more pass on flatMap/mapMulti I like the idea that whatever is passed to the block describes the downstream; the role of the block is to emit values downstream. I think that makes things clearer about what's going on, which is good because what is going on is already not all that clear. I buy that "Downstream" could use some nounification. (Calvin say: verbing weirds language.) DownstreamCollector DownstreamAcceptor DownstreamHandler accept() is OK, and we can make it extend Block. On 1/10/2013 12:28 PM, Tim Peierls wrote: > On Thu, Jan 10, 2013 at 11:59 AM, Brian Goetz > wrote: > > Bikeshed discussions can continue. > > > OK, then! :-) > > The method name "send" is OK, but you wouldn't be seeing it in the > signature of explode. What you would see is Downstream, which still > bugs me. The word "downstream" is an adverb or adjective, not a noun. > Things are sent downstream, they aren't sent *to* a downstream. > > Was Acceptor.accept proposed and rejected already? If so, why? I would > understand > > explode(BiBlock__, T>)) > > --tim From daniel.smith at oracle.com Fri Jan 11 08:24:32 2013 From: daniel.smith at oracle.com (Dan Smith) Date: Fri, 11 Jan 2013 09:24:32 -0700 Subject: Function type naming conventions In-Reply-To: <07F4CED6-B457-4573-B111-C7547FD15692@oracle.com> References: <50E48919.2060408@oracle.com> <07F4CED6-B457-4573-B111-C7547FD15692@oracle.com> Message-ID: <0D1FD194-593A-46D2-BC11-5C7AA07A7420@oracle.com> On Jan 4, 2013, at 3:46 PM, Dan Smith wrote: > On Jan 3, 2013, at 2:07 PM, Dan Smith wrote: > >>> I'm sure I'll never remember whether IntFunction is int -> int, T -> int, or int -> T, so it'll trip me up a little each time I read it. >> >> Stephen Colebourne makes a similar comment in the comments list. His suggestion is to use a different base name for functions that return primitives -- we already have "Predicate" and "Block"; now we just need "IntThingy" and "DoubleThingy". Stephen suggests "CalcInt", which I don't love, but maybe there's a word out there that nicely conveys the concept much like "Predicate"? > > I did a little Googling: "predicate" is a.k.a. "boolean-valued function". "Real-valued function" and "integer-valued function" are also fairly widely-used terms. But I didn't come across any more concise terms for those... I've been kicking around an idea in my head for the last few days and haven't rejected it as horrible yet: "integer-valued function" (and "foo-valued function," generally) is the appropriate, widely-understood term for functions that output integers [1][2][3][4]. So let's just say that, slightly abbreviated: IntValFunction // T -> int The meaning should be unambiguous. That leaves "integer function" (and "foo function," generally) to refer to functions that input integers. Again, that general form is widely-understood terminology [5][6]. IntFunction // int -> T The weak point is that both terms are abused in some contexts (such as [2], I think) to refer to int->int. But since we've got "integer operator" in the mix as well, I think it's fairly straightforward to disambiguate. ?Dan [1] http://www.proofwiki.org/wiki/Definition:Integer-Valued_Function [2] http://alexandria.tue.nl/repository/freearticles/597509.pdf [3] http://en.wikipedia.org/wiki/Real-valued_function [4] http://en.wikipedia.org/wiki/Boolean-valued_function [5] http://php.net/manual/en/ref.strings.php [6] http://docs.racket-lang.org/reference/pairs.html#3.9.7 From dl at cs.oswego.edu Fri Jan 11 08:46:11 2013 From: dl at cs.oswego.edu (Doug Lea) Date: Fri, 11 Jan 2013 11:46:11 -0500 Subject: Function type naming conventions In-Reply-To: <0D1FD194-593A-46D2-BC11-5C7AA07A7420@oracle.com> References: <50E48919.2060408@oracle.com> <07F4CED6-B457-4573-B111-C7547FD15692@oracle.com> <0D1FD194-593A-46D2-BC11-5C7AA07A7420@oracle.com> Message-ID: <50F041D3.8070004@cs.oswego.edu> On 01/11/13 11:24, Dan Smith wrote: > I've been kicking around an idea in my head for the last few days and haven't rejected it as horrible yet: > > "integer-valued function" (and "foo-valued function," generally) is the appropriate, widely-understood term for functions that output integers [1][2][3][4]. So let's just say that, slightly abbreviated: > > IntValFunction // T -> int > > The meaning should be unambiguous. Only if you mentally associate "val" with the result :-) The more I see of alternatives, the more I like the clunky, uncreative but clear one. IntToObjectFunction ObjectToIntFunction IntToDoubleFunction LongToIntFunction etc Plus still keeping ... IntUnaryOperator IntBinaryOperator IntPredicate IntBlock etc -Doug From daniel.smith at oracle.com Fri Jan 11 09:28:09 2013 From: daniel.smith at oracle.com (Dan Smith) Date: Fri, 11 Jan 2013 10:28:09 -0700 Subject: Function type naming conventions In-Reply-To: <50F041D3.8070004@cs.oswego.edu> References: <50E48919.2060408@oracle.com> <07F4CED6-B457-4573-B111-C7547FD15692@oracle.com> <0D1FD194-593A-46D2-BC11-5C7AA07A7420@oracle.com> <50F041D3.8070004@cs.oswego.edu> Message-ID: <2564DA2D-D372-45C1-A2B3-94DD214A3F5B@oracle.com> On Jan 11, 2013, at 9:46 AM, Doug Lea
wrote: > On 01/11/13 11:24, Dan Smith wrote: > >> I've been kicking around an idea in my head for the last few days and haven't rejected it as horrible yet: >> >> "integer-valued function" (and "foo-valued function," generally) is the appropriate, widely-understood term for functions that output integers [1][2][3][4]. So let's just say that, slightly abbreviated: >> >> IntValFunction // T -> int >> >> The meaning should be unambiguous. > > Only if you mentally associate "val" with the result :-) What I'm hoping to contribute is that, beyond just "I like the way IntValFunction sounds," the established mathematical terminology IS "integer-valued function." If we can express that concisely, then the name says exactly what the type means, without having to invent a new term or fall back to the more verbose "function from integer to object." ?Dan From dl at cs.oswego.edu Fri Jan 11 09:37:31 2013 From: dl at cs.oswego.edu (Doug Lea) Date: Fri, 11 Jan 2013 12:37:31 -0500 Subject: Function type naming conventions In-Reply-To: <2564DA2D-D372-45C1-A2B3-94DD214A3F5B@oracle.com> References: <50E48919.2060408@oracle.com> <07F4CED6-B457-4573-B111-C7547FD15692@oracle.com> <0D1FD194-593A-46D2-BC11-5C7AA07A7420@oracle.com> <50F041D3.8070004@cs.oswego.edu> <2564DA2D-D372-45C1-A2B3-94DD214A3F5B@oracle.com> Message-ID: <50F04DDB.3050101@cs.oswego.edu> On 01/11/13 12:28, Dan Smith wrote: >>> IntValFunction // T -> int >>> >>> The meaning should be unambiguous. >> >> Only if you mentally associate "val" with the result :-) > > What I'm hoping to contribute is that, beyond just "I like the way > IntValFunction sounds," the established mathematical terminology IS > "integer-valued function." If we can express that concisely, then the name > says exactly what the type means, without having to invent a new term or fall > back to the more verbose "function from integer to object." > I'm really not trying to be hostile about this, but just noting that the programmer impact of these suggestions relies on common shared conventions/intuitions, but since there currently are none in Java, clarity seems to be the primary goal. (As evidence, consider discussions of method "fun1.compose(fun2)". Which way does it go? Probably best to have no method just named compose.) -Doug From forax at univ-mlv.fr Fri Jan 11 12:00:28 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Fri, 11 Jan 2013 21:00:28 +0100 Subject: Adapting an already existing stream API to a Stream Message-ID: <50F06F5C.20301@univ-mlv.fr> Let say I have an API with one static method generateAll that takes a Block as parameter and generate all elements by calling the Block n times. How can I create a Stream on top of this API ? R?mi From brian.goetz at oracle.com Fri Jan 11 12:11:06 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 11 Jan 2013 15:11:06 -0500 Subject: Adapting an already existing stream API to a Stream In-Reply-To: <50F06F5C.20301@univ-mlv.fr> References: <50F06F5C.20301@univ-mlv.fr> Message-ID: <50F071DA.3030902@oracle.com> Blocks don't return anything. Where do the elements go? On 1/11/2013 3:00 PM, Remi Forax wrote: > Let say I have an API with one static method generateAll that takes a > Block as parameter and generate all elements by calling the Block n times. > How can I create a Stream on top of this API ? > > R?mi > From forax at univ-mlv.fr Fri Jan 11 12:36:27 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Fri, 11 Jan 2013 21:36:27 +0100 Subject: Adapting an already existing stream API to a Stream In-Reply-To: <50F071DA.3030902@oracle.com> References: <50F06F5C.20301@univ-mlv.fr> <50F071DA.3030902@oracle.com> Message-ID: <50F077CB.4040808@univ-mlv.fr> On 01/11/2013 09:11 PM, Brian Goetz wrote: > Blocks don't return anything. Where do the elements go? You pass a Block as parameter, so it depends on the block. By example, static void generateAll(Block block) { for(int i=0; i<1_000; i++) { block.accept(i); } } or with a cancel interface Pusher{ // the dual of Iterator boolean isStopped(); void accept(T element); } static void generateAll(Pushiterator pusher) { for(int i=0; i<1_000 && !pusher.isStopped(); i++) { pusher.accept(i); } } R?mi > > On 1/11/2013 3:00 PM, Remi Forax wrote: >> Let say I have an API with one static method generateAll that takes a >> Block as parameter and generate all elements by calling the Block n >> times. >> How can I create a Stream on top of this API ? >> >> R?mi >> From brian.goetz at oracle.com Fri Jan 11 12:54:46 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 11 Jan 2013 15:54:46 -0500 Subject: Adapting an already existing stream API to a Stream In-Reply-To: <50F077CB.4040808@univ-mlv.fr> References: <50F06F5C.20301@univ-mlv.fr> <50F071DA.3030902@oracle.com> <50F077CB.4040808@univ-mlv.fr> Message-ID: <50F07C16.2020407@oracle.com> There are a few ways to build a Stream, in order of increasing work (and increasing stream quality): - Iterator - Iterator + size - Spliterator - Spliterator with exactSize / exactSplits With a raw Iterator, we won't know the size of the stream, nor will we get very good splits. Adding the size addresses the first problem. Going to a Spliterator addresses the "not good splits" problem (assuming your spliterator is decent.) Adding in exactSize/exactSplits enables optimizations that avoid copies when merging parallel results. Converting any push-oriented source to a Stream requires writing an Iterator/Spliterator. Which may in turn require you to do some buffering on your own. (We do something similar internally, when we want to iterate nontrivial streams one element at a time; we pull elements from the upstream source, create a chain of sinks, and the last sink is a buffer, and then the iterator reads from the buffer, and when the buffer is empty, we push more elements into the sink chain from upstream.) On 1/11/2013 3:36 PM, Remi Forax wrote: > On 01/11/2013 09:11 PM, Brian Goetz wrote: >> Blocks don't return anything. Where do the elements go? > > You pass a Block as parameter, so it depends on the block. > By example, > > static void generateAll(Block block) { > for(int i=0; i<1_000; i++) { > block.accept(i); > } > } > > or with a cancel > > interface Pusher{ // the dual of Iterator > boolean isStopped(); > void accept(T element); > } > > static void generateAll(Pushiterator pusher) { > for(int i=0; i<1_000 && !pusher.isStopped(); i++) { > pusher.accept(i); > } > } > > R?mi > >> >> On 1/11/2013 3:00 PM, Remi Forax wrote: >>> Let say I have an API with one static method generateAll that takes a >>> Block as parameter and generate all elements by calling the Block n >>> times. >>> How can I create a Stream on top of this API ? >>> >>> R?mi >>> > From forax at univ-mlv.fr Fri Jan 11 13:55:53 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Fri, 11 Jan 2013 22:55:53 +0100 Subject: Adapting an already existing stream API to a Stream In-Reply-To: <50F07C16.2020407@oracle.com> References: <50F06F5C.20301@univ-mlv.fr> <50F071DA.3030902@oracle.com> <50F077CB.4040808@univ-mlv.fr> <50F07C16.2020407@oracle.com> Message-ID: <50F08A69.50601@univ-mlv.fr> On 01/11/2013 09:54 PM, Brian Goetz wrote: > There are a few ways to build a Stream, in order of increasing work > (and increasing stream quality): > > - Iterator > - Iterator + size > - Spliterator > - Spliterator with exactSize / exactSplits > > With a raw Iterator, we won't know the size of the stream, nor will we > get very good splits. Adding the size addresses the first problem. > > Going to a Spliterator addresses the "not good splits" problem > (assuming your spliterator is decent.) > > Adding in exactSize/exactSplits enables optimizations that avoid > copies when merging parallel results. > > Converting any push-oriented source to a Stream requires writing an > Iterator/Spliterator. Which may in turn require you to do some > buffering on your own. (We do something similar internally, when we > want to iterate nontrivial streams one element at a time; we pull > elements from the upstream source, create a chain of sinks, and the > last sink is a buffer, and then the iterator reads from the buffer, > and when the buffer is empty, we push more elements into the sink > chain from upstream.) The whole pipeline works in a push way but the interface used to push the element into the pipeline uses the pull way. You can't do any buffering because you may have to buffer all the elements unlike with mapMulti by example. Basically, I think Spliterator should not be based on Iterator but on a push based API because you can extract elements from an iterator and push them in the pipeline and not do the other way around. interface Pusher { // returns false if the pusher doesn't accept any other value boolean accept(E element); } interface SplitPusher { Pusher pusher(); SplitPusher trySplit(); long exactSizeIfKnown(); boolean hasExactSplits(); } R?mi > > On 1/11/2013 3:36 PM, Remi Forax wrote: >> On 01/11/2013 09:11 PM, Brian Goetz wrote: >>> Blocks don't return anything. Where do the elements go? >> >> You pass a Block as parameter, so it depends on the block. >> By example, >> >> static void generateAll(Block block) { >> for(int i=0; i<1_000; i++) { >> block.accept(i); >> } >> } >> >> or with a cancel >> >> interface Pusher{ // the dual of Iterator >> boolean isStopped(); >> void accept(T element); >> } >> >> static void generateAll(Pushiterator pusher) { >> for(int i=0; i<1_000 && !pusher.isStopped(); i++) { >> pusher.accept(i); >> } >> } >> >> R?mi >> >>> >>> On 1/11/2013 3:00 PM, Remi Forax wrote: >>>> Let say I have an API with one static method generateAll that takes a >>>> Block as parameter and generate all elements by calling the Block n >>>> times. >>>> How can I create a Stream on top of this API ? >>>> >>>> R?mi >>>> >> From brian.goetz at oracle.com Fri Jan 11 14:07:35 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 11 Jan 2013 17:07:35 -0500 Subject: Arrays methods Message-ID: <50F08D27.6040704@oracle.com> I did a pruning pass on the Arrays methods, and we're down to: public static Spliterator spliterator(T[] array) public static Spliterator spliterator(T[] array, int fromIndex, int toIndex) public static Spliterator.OfInt spliterator(int[] array) public static Spliterator.OfInt spliterator(int[] array, int fromIndex, int toIndex) public static Stream stream(T[] array) public static Stream stream(T[] array, int fromIndex, int toIndex) public static IntStream stream(int[] source) public static IntStream stream(int[] source, int fromIndex, int toIndex) public static Stream parallelStream(T[] array) public static Stream parallelStream(T[] array, int fromIndex, int toIndex) public static IntStream parallelStream(int[] array) public static IntStream parallelStream(int[] array, int fromIndex, int toIndex) (and Double/Long versions when those are ready.) The remaining one on my to-do list is something to enable parallel updating/filling of arrays. The "obvious" thing is something like: fill(array, int -> T) replaceAll(array, (int, T) -> T) There's a combinatorial explosion here: array types x (whole array, from-to) x (parallel, sequential) x op kind (fill, replace, replace-if, etc) and what feels like a perennial game of whack-a-mole. I think a better option is just adding a small number of methods to take an array and produce a stream of valid indexes: IntStream indexes(T[]) IntStream indexes(int[]) ... Because then the user can mix and match his own: // generate Arrays.indexes(array).forEach(i -> { array[i] = f(i) }); // replace-all Arrays.indexes(array).forEach(i -> { array[i] = f(i, array[i]) }); // replace-all-conditional Arrays.indexes(array).filter(...) .forEach(i -> { array[i] = ... }); It also works on arrays of bytes, shorts, etc (because the array indexes are always an IntStream), whereas the more specific forms above would likely be restricted to int/long/double. From tim at peierls.net Fri Jan 11 14:23:28 2013 From: tim at peierls.net (Tim Peierls) Date: Fri, 11 Jan 2013 17:23:28 -0500 Subject: Arrays methods In-Reply-To: <50F08D27.6040704@oracle.com> References: <50F08D27.6040704@oracle.com> Message-ID: On Fri, Jan 11, 2013 at 5:07 PM, Brian Goetz wrote: > I think a better option is just adding a small number of methods to take > an array and produce a stream of valid indexes: > > IntStream indexes(T[]) > IntStream indexes(int[]) > ... > > Because then the user can mix and match his own: > > // generate > Arrays.indexes(array).forEach(**i -> { array[i] = f(i) }); > That's appealingly simple, but I worry that it's too easy to write something like this: Arrays.indexes(a).forEach(i -> { b[i] = f(i) }); // oops, a is not b And isn't "indices" better than "indexes"? --tim From brian.goetz at oracle.com Fri Jan 11 14:26:27 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 11 Jan 2013 17:26:27 -0500 Subject: Arrays methods In-Reply-To: References: <50F08D27.6040704@oracle.com> Message-ID: <50F09193.2000802@oracle.com> > That's appealingly simple, but I worry that it's too easy to write > something like this: > > Arrays.indexes(a).forEach(i -> { b[i] = f(i) }); // oops, a is not b' Yep, that's the downside, as far as I can see. Lots of upside, though. > And isn't "indices" better than "indexes"? OK :) From kevinb at google.com Fri Jan 11 14:44:48 2013 From: kevinb at google.com (Kevin Bourrillion) Date: Fri, 11 Jan 2013 14:44:48 -0800 Subject: Arrays methods In-Reply-To: References: <50F08D27.6040704@oracle.com> Message-ID: On Fri, Jan 11, 2013 at 2:23 PM, Tim Peierls wrote: And isn't "indices" better than "indexes"? > I confronted that question for Preconditions.checkPositionIndexes(), and concluded that "indexes" is more colloquial and no less correct. -- Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com From tim at peierls.net Fri Jan 11 14:52:26 2013 From: tim at peierls.net (Tim Peierls) Date: Fri, 11 Jan 2013 17:52:26 -0500 Subject: Arrays methods In-Reply-To: References: <50F08D27.6040704@oracle.com> Message-ID: It's so deliciously low. :-) On Fri, Jan 11, 2013 at 5:44 PM, Kevin Bourrillion wrote: > On Fri, Jan 11, 2013 at 2:23 PM, Tim Peierls wrote: > > And isn't "indices" better than "indexes"? >> > > I confronted that question for Preconditions.checkPositionIndexes(), and > concluded that "indexes" is more colloquial and no less correct. > > > -- > Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com > From brian.goetz at oracle.com Fri Jan 11 15:02:40 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 11 Jan 2013 18:02:40 -0500 Subject: Stream Message-ID: <50F09A10.4010400@oracle.com> I've done a pass through the repo and marked almost all the classes package-private. There are only a handful of public classes now. I'm going to do a set of surveys that cover all the public methods of all the public classes. This is the first one, and it's on Stream. EG members will be receiving their surveys directly from SurveyMonkey. If you don't get one within a few hours, let me know. There are not really any questions; there is a free-response box for every method or group of related methods in Stream. You are encouraged to write comments on: - the suitability of the method - the specific details of its arguments - generics nits - naming nits - semantic wonderings - anything else you might have to say Writing nothing in the box will be interpreted as "I think this method is great as is." Specifically, the Collector (reducer) API is still a work in progress, so there will be another opportunity to give final feedback on that (but feel free to give feedback here too.) From kevinb at google.com Fri Jan 11 15:23:38 2013 From: kevinb at google.com (Kevin Bourrillion) Date: Fri, 11 Jan 2013 15:23:38 -0800 Subject: Stream In-Reply-To: <50F09A10.4010400@oracle.com> References: <50F09A10.4010400@oracle.com> Message-ID: I believe you overlooked giving us a deadline on this. :-) On Fri, Jan 11, 2013 at 3:02 PM, Brian Goetz wrote: > I've done a pass through the repo and marked almost all the classes > package-private. There are only a handful of public classes now. I'm > going to do a set of surveys that cover all the public methods of all the > public classes. This is the first one, and it's on Stream. EG members > will be receiving their surveys directly from SurveyMonkey. If you don't > get one within a few hours, let me know. > > There are not really any questions; there is a free-response box for every > method or group of related methods in Stream. > > You are encouraged to write comments on: > - the suitability of the method > - the specific details of its arguments > - generics nits > - naming nits > - semantic wonderings > - anything else you might have to say > > Writing nothing in the box will be interpreted as "I think this method is > great as is." > > Specifically, the Collector (reducer) API is still a work in progress, so > there will be another opportunity to give final feedback on that (but feel > free to give feedback here too.) > > -- Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com From joe.bowbeer at gmail.com Fri Jan 11 15:48:09 2013 From: joe.bowbeer at gmail.com (Joe Bowbeer) Date: Fri, 11 Jan 2013 15:48:09 -0800 Subject: Arrays methods In-Reply-To: References: <50F08D27.6040704@oracle.com> Message-ID: indexes vs. indices: As a technical reviewer, I prefer indices. While indexes is more common in North America, it is more common in informal contexts, whereas indices is generally more common in mathematical and technical contexts (according to grammarist.com). http://grammarist.com/usage/indexes-indices/ On Fri, Jan 11, 2013 at 2:44 PM, Kevin Bourrillion wrote: > On Fri, Jan 11, 2013 at 2:23 PM, Tim Peierls wrote: > > And isn't "indices" better than "indexes"? >> > > I confronted that question for Preconditions.checkPositionIndexes(), and > concluded that "indexes" is more colloquial and no less correct. > > > -- > Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com > From dl at cs.oswego.edu Fri Jan 11 16:38:19 2013 From: dl at cs.oswego.edu (Doug Lea) Date: Fri, 11 Jan 2013 19:38:19 -0500 Subject: Arrays methods In-Reply-To: References: <50F08D27.6040704@oracle.com> Message-ID: <50F0B07B.50904@cs.oswego.edu> On 01/11/13 18:48, Joe Bowbeer wrote: > indexes vs. indices: > > As a technical reviewer, I prefer indices. Ditto. -Doug > > While indexes is more common in North America, it is more common in informal > contexts, whereas indices is generally more common in mathematical and technical > contexts (according to grammarist.com ). > > http://grammarist.com/usage/indexes-indices/ > > > On Fri, Jan 11, 2013 at 2:44 PM, Kevin Bourrillion > wrote: > > On Fri, Jan 11, 2013 at 2:23 PM, Tim Peierls > wrote: > > And isn't "indices" better than "indexes"? > > > I confronted that question for Preconditions.checkPositionIndexes(), and > concluded that "indexes" is more colloquial and no less correct. > > > -- > Kevin Bourrillion | Java Librarian | Google, Inc. |kevinb at google.com > > > From howard.lovatt at gmail.com Fri Jan 11 16:51:30 2013 From: howard.lovatt at gmail.com (Howard Lovatt) Date: Sat, 12 Jan 2013 11:51:30 +1100 Subject: Function type naming conventions In-Reply-To: <50F04DDB.3050101@cs.oswego.edu> References: <50E48919.2060408@oracle.com> <07F4CED6-B457-4573-B111-C7547FD15692@oracle.com> <0D1FD194-593A-46D2-BC11-5C7AA07A7420@oracle.com> <50F041D3.8070004@cs.oswego.edu> <2564DA2D-D372-45C1-A2B3-94DD214A3F5B@oracle.com> <50F04DDB.3050101@cs.oswego.edu> Message-ID: The method I have found concise and unambiguous is to mimic the generic arguments of the base interface: Function // I -> O FunctionTI // I -> Int FunctionIT // int -> O FunctionII // int -> Int Also note that it is an advantage to have all the 'Functions' begin with Function so that they are listed together in Javadocs. I have a preference for I and O for the generic arguments because that is the commonly used abbreviation for input and output, whereas T seem totally arbitrary and conveyed no useful information. R is OK but it is hard to think of a good match for the input. -- Howard. Sent from my iPad On 12/01/2013, at 4:37 AM, Doug Lea
wrote: > On 01/11/13 12:28, Dan Smith wrote: > >>>> IntValFunction // T -> int >>>> >>>> The meaning should be unambiguous. >>> >>> Only if you mentally associate "val" with the result :-) >> >> What I'm hoping to contribute is that, beyond just "I like the way >> IntValFunction sounds," the established mathematical terminology IS >> "integer-valued function." If we can express that concisely, then the name >> says exactly what the type means, without having to invent a new term or fall >> back to the more verbose "function from integer to object." > > I'm really not trying to be hostile about this, but just noting that > the programmer impact of these suggestions relies on common > shared conventions/intuitions, but since there currently are none > in Java, clarity seems to be the primary goal. > > (As evidence, consider discussions of method "fun1.compose(fun2)". > Which way does it go? Probably best to have no method just > named compose.) > > -Doug > From dl at cs.oswego.edu Sat Jan 12 05:13:52 2013 From: dl at cs.oswego.edu (Doug Lea) Date: Sat, 12 Jan 2013 08:13:52 -0500 Subject: concurrent sets Message-ID: <50F16190.7080202@cs.oswego.edu> I'm still working on lambda-related tie-ins for j.u.c stuff (mostly Spliterators). As mentioned before, we will supply two explicitly concurrent Sets, both via static factory methods: ConcurrentHashMap.newKeySet, and ConcurrentSkipListMap.newKeySet. (The CSLM one gives Sorted/Navigable Set) These are necessary when people need/want parallel collect-into operations for sets. But how will they know to do this? -Doug From forax at univ-mlv.fr Sat Jan 12 05:40:28 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Sat, 12 Jan 2013 14:40:28 +0100 Subject: concurrent sets In-Reply-To: <50F16190.7080202@cs.oswego.edu> References: <50F16190.7080202@cs.oswego.edu> Message-ID: <50F167CC.5030903@univ-mlv.fr> On 01/12/2013 02:13 PM, Doug Lea wrote: > > I'm still working on lambda-related tie-ins for j.u.c stuff > (mostly Spliterators). > > As mentioned before, we will supply two explicitly concurrent Sets, > both via static factory methods: ConcurrentHashMap.newKeySet, and > ConcurrentSkipListMap.newKeySet. (The CSLM one gives Sorted/Navigable > Set) > > These are necessary when people need/want parallel collect-into > operations for sets. > > But how will they know to do this? they don't need if they use methods Collector.to* the implicit way: stream.parallel(). ... .collect(Collector.toSet()); // use a concurrent hash set stream.parallell(). ... .collect(Collector.toSortedSet()); // use a concurrent skip list set the explicit way: Set<...> set = ConcurrentHashMap.newKeySet(); stream.parallel(). ... .forEach(set::add); SortedSet<...> sortedSet = ConcurrentSkipListMap.newKeySet(); stream.parallell(). ... .forEach(sortedSet::add); The explicit way can be written is the javadoc of newKeyset() as an example of use. > > -Doug > > > R?mi From brian.goetz at oracle.com Sun Jan 13 08:51:44 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Sun, 13 Jan 2013 11:51:44 -0500 Subject: Stream In-Reply-To: <50F09A10.4010400@oracle.com> References: <50F09A10.4010400@oracle.com> Message-ID: <50F2E620.4070803@oracle.com> I forgot the all-important aspect of this: a deadline. Thanks to Remi and Tim for jumping on this already. Let's aim to get all responses in by midnight Monday of some unnamed time zone. I'll then collate the responses and relay to the list. On 1/11/2013 6:02 PM, Brian Goetz wrote: > I've done a pass through the repo and marked almost all the classes > package-private. There are only a handful of public classes now. I'm > going to do a set of surveys that cover all the public methods of all > the public classes. This is the first one, and it's on Stream. EG > members will be receiving their surveys directly from SurveyMonkey. If > you don't get one within a few hours, let me know. > > There are not really any questions; there is a free-response box for > every method or group of related methods in Stream. > > You are encouraged to write comments on: > - the suitability of the method > - the specific details of its arguments > - generics nits > - naming nits > - semantic wonderings > - anything else you might have to say > > Writing nothing in the box will be interpreted as "I think this method > is great as is." > > Specifically, the Collector (reducer) API is still a work in progress, > so there will be another opportunity to give final feedback on that (but > feel free to give feedback here too.) > From joe.bowbeer at gmail.com Sun Jan 13 10:44:57 2013 From: joe.bowbeer at gmail.com (Joe Bowbeer) Date: Sun, 13 Jan 2013 10:44:57 -0800 Subject: Stream In-Reply-To: <50F2E620.4070803@oracle.com> References: <50F09A10.4010400@oracle.com> <50F2E620.4070803@oracle.com> Message-ID: I'm starting to take the survey and I'm running into name/API changes that haven't appeared yet in a binary snapshot (e.g., b72). I can't comment effectively on these until I've used them in running code. (I'll add this comment to the survey as well, but I thought it would be good to point this out, esp. w.r.t. deadlines.) On Sun, Jan 13, 2013 at 8:51 AM, Brian Goetz wrote: > I forgot the all-important aspect of this: a deadline. Thanks to Remi and > Tim for jumping on this already. > > Let's aim to get all responses in by midnight Monday of some unnamed time > zone. I'll then collate the responses and relay to the list. > > > > > On 1/11/2013 6:02 PM, Brian Goetz wrote: > >> I've done a pass through the repo and marked almost all the classes >> package-private. There are only a handful of public classes now. I'm >> going to do a set of surveys that cover all the public methods of all >> the public classes. This is the first one, and it's on Stream. EG >> members will be receiving their surveys directly from SurveyMonkey. If >> you don't get one within a few hours, let me know. >> >> There are not really any questions; there is a free-response box for >> every method or group of related methods in Stream. >> >> You are encouraged to write comments on: >> - the suitability of the method >> - the specific details of its arguments >> - generics nits >> - naming nits >> - semantic wonderings >> - anything else you might have to say >> >> Writing nothing in the box will be interpreted as "I think this method >> is great as is." >> >> Specifically, the Collector (reducer) API is still a work in progress, >> so there will be another opportunity to give final feedback on that (but >> feel free to give feedback here too.) >> >> From joe.bowbeer at gmail.com Sun Jan 13 22:00:23 2013 From: joe.bowbeer at gmail.com (Joe Bowbeer) Date: Sun, 13 Jan 2013 22:00:23 -0800 Subject: Optional.orElse(null) ambiguity Message-ID: In b72, optional.orElse(null) is ambiguous because it matches both of the orElse forms: T orElse(T other) T orElse(Supplier other) For example, this won't compile: return stream.max(comparator).orElse(null); Optional needs a concise way to return null, so I suggest: T orElseNull() --Joe From forax at univ-mlv.fr Mon Jan 14 00:06:49 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Mon, 14 Jan 2013 09:06:49 +0100 Subject: Optional.equals/hashCode Message-ID: <50F3BC99.5010904@univ-mlv.fr> A remark of one of my colleague, "if you want to avoid List> why Optional has methods hashCode and equals that are overriden". I think it makes sense to remove them. R?mi From david.holmes at oracle.com Mon Jan 14 00:19:49 2013 From: david.holmes at oracle.com (David Holmes) Date: Mon, 14 Jan 2013 18:19:49 +1000 Subject: package access modifier? Message-ID: <50F3BFA5.6000509@oracle.com> I admit I haven't been able to keep up lately but I don't recall seeing any discussion of actually adding "package" as an access modifier, yet there it is. David From forax at univ-mlv.fr Mon Jan 14 00:19:38 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Mon, 14 Jan 2013 09:19:38 +0100 Subject: Stream puzzler Message-ID: <50F3BF9A.8040401@univ-mlv.fr> Using the last binary drop (otherwise replace accumulate by collect), can you spot the bug (hint, it doesn't compile). import java.util.Arrays; import java.util.List; import java.util.stream.Accumulators; import static java.util.stream.Accumulators.*; public class Bug { public static List intoList(Object[] args) { return Arrays.stream(args).accumulate(intoList()); } } Are we able to do something for that ? R?mi From forax at univ-mlv.fr Mon Jan 14 00:22:53 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Mon, 14 Jan 2013 09:22:53 +0100 Subject: package access modifier? In-Reply-To: <50F3BFA5.6000509@oracle.com> References: <50F3BFA5.6000509@oracle.com> Message-ID: <50F3C05D.5020100@univ-mlv.fr> On 01/14/2013 09:19 AM, David Holmes wrote: > I admit I haven't been able to keep up lately but I don't recall > seeing any discussion of actually adding "package" as an access > modifier, yet there it is. > > David I have seen that too, and I don't understand why it was introduced, I remember we have discussed to add it to have a way to create package method in interface but since we rule out that. R?mi From paul.sandoz at oracle.com Mon Jan 14 02:49:00 2013 From: paul.sandoz at oracle.com (Paul Sandoz) Date: Mon, 14 Jan 2013 11:49:00 +0100 Subject: Stream puzzler In-Reply-To: <50F3BF9A.8040401@univ-mlv.fr> References: <50F3BF9A.8040401@univ-mlv.fr> Message-ID: <84448B4F-92B0-4CB4-ABB8-0220024B0BD6@oracle.com> On Jan 14, 2013, at 9:19 AM, Remi Forax wrote: > Using the last binary drop (otherwise replace accumulate by collect), > can you spot the bug (hint, it doesn't compile). > It compiles for me using a build from the tip. Paul. > import java.util.Arrays; > import java.util.List; > import java.util.stream.Accumulators; > > import static java.util.stream.Accumulators.*; > > public class Bug { > public static List intoList(Object[] args) { > return Arrays.stream(args).accumulate(intoList()); > } > } > > Are we able to do something for that ? > > R?mi > From forax at univ-mlv.fr Mon Jan 14 03:04:58 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Mon, 14 Jan 2013 12:04:58 +0100 Subject: Stream puzzler In-Reply-To: <84448B4F-92B0-4CB4-ABB8-0220024B0BD6@oracle.com> References: <50F3BF9A.8040401@univ-mlv.fr> <84448B4F-92B0-4CB4-ABB8-0220024B0BD6@oracle.com> Message-ID: <50F3E65A.7050401@univ-mlv.fr> On 01/14/2013 11:49 AM, Paul Sandoz wrote: > On Jan 14, 2013, at 9:19 AM, Remi Forax wrote: > >> Using the last binary drop (otherwise replace accumulate by collect), >> can you spot the bug (hint, it doesn't compile). >> > It compiles for me using a build from the tip. Do you have named the method containing the code intoList or something else ? or the import static rules have changed ?? > > Paul. R?mi > >> import java.util.Arrays; >> import java.util.List; >> import java.util.stream.Accumulators; >> >> import static java.util.stream.Accumulators.*; >> >> public class Bug { >> public static List intoList(Object[] args) { >> return Arrays.stream(args).accumulate(intoList()); >> } >> } >> >> Are we able to do something for that ? >> >> R?mi >> From paul.sandoz at oracle.com Mon Jan 14 03:22:00 2013 From: paul.sandoz at oracle.com (Paul Sandoz) Date: Mon, 14 Jan 2013 12:22:00 +0100 Subject: Stream puzzler In-Reply-To: <50F3E65A.7050401@univ-mlv.fr> References: <50F3BF9A.8040401@univ-mlv.fr> <84448B4F-92B0-4CB4-ABB8-0220024B0BD6@oracle.com> <50F3E65A.7050401@univ-mlv.fr> Message-ID: <49BD9BC0-9E1A-4681-9AEC-1D06CEF100A0@oracle.com> On Jan 14, 2013, at 12:04 PM, Remi Forax wrote: > On 01/14/2013 11:49 AM, Paul Sandoz wrote: >> On Jan 14, 2013, at 9:19 AM, Remi Forax wrote: >> >>> Using the last binary drop (otherwise replace accumulate by collect), >>> can you spot the bug (hint, it doesn't compile). >>> >> It compiles for me using a build from the tip. > > Do you have named the method containing the code intoList or something else ? > or the import static rules have changed ?? > Doh! when i changed the code to use Collectors i forgot to change the name of the static method: public static List toList(Object[] args) { return Arrays.stream(args).collect(toList()); } Paul. From forax at univ-mlv.fr Mon Jan 14 03:32:20 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Mon, 14 Jan 2013 12:32:20 +0100 Subject: Stream puzzler In-Reply-To: <49BD9BC0-9E1A-4681-9AEC-1D06CEF100A0@oracle.com> References: <50F3BF9A.8040401@univ-mlv.fr> <84448B4F-92B0-4CB4-ABB8-0220024B0BD6@oracle.com> <50F3E65A.7050401@univ-mlv.fr> <49BD9BC0-9E1A-4681-9AEC-1D06CEF100A0@oracle.com> Message-ID: <50F3ECC4.4020509@univ-mlv.fr> On 01/14/2013 12:22 PM, Paul Sandoz wrote: > On Jan 14, 2013, at 12:04 PM, Remi Forax wrote: > >> On 01/14/2013 11:49 AM, Paul Sandoz wrote: >>> On Jan 14, 2013, at 9:19 AM, Remi Forax wrote: >>> >>>> Using the last binary drop (otherwise replace accumulate by collect), >>>> can you spot the bug (hint, it doesn't compile). >>>> >>> It compiles for me using a build from the tip. >> Do you have named the method containing the code intoList or something else ? >> or the import static rules have changed ?? >> > Doh! when i changed the code to use Collectors i forgot to change the name of the static method: > > public static List toList(Object[] args) { > return Arrays.stream(args).collect(toList()); > } I wonder if we can do something here. Does someone know why when resolving an import static the arity of the method is not used ? > > Paul. R?mi From brian.goetz at oracle.com Mon Jan 14 09:04:55 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 14 Jan 2013 09:04:55 -0800 Subject: Survey Message-ID: <2692E14E-9F0E-4E3E-A2AC-4A92E1D48E91@oracle.com> The Oracle folks didn't get their survey links, it seems. If anyone else did not, let me know. Oracle folks: contact me privately for link. From brian.goetz at oracle.com Mon Jan 14 10:23:56 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 14 Jan 2013 10:23:56 -0800 Subject: Optional.orElse(null) ambiguity In-Reply-To: References: Message-ID: Null is a valid value for orElse(T) but not for orElse(Supplier). So we care about this ambiguity (there are other similar ones we don't care about, when null isn't a valid value for either option.) orElseNull is a possibility. Alternately, orElseGet(Supplier) ? orElseFrom(Supplier)? On Jan 13, 2013, at 10:00 PM, Joe Bowbeer wrote: > In b72, optional.orElse(null) is ambiguous because it matches both of the orElse forms: > > T orElse(T other) > T orElse(Supplier other) > > For example, this won't compile: > > return stream.max(comparator).orElse(null); > > > Optional needs a concise way to return null, so I suggest: > > T orElseNull() > > --Joe From brian.goetz at oracle.com Mon Jan 14 10:00:33 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 14 Jan 2013 10:00:33 -0800 Subject: Optional.equals/hashCode In-Reply-To: <50F3BC99.5010904@univ-mlv.fr> References: <50F3BC99.5010904@univ-mlv.fr> Message-ID: <44D3C731-B3DB-4393-9CC0-BBA621E6E95B@oracle.com> Is the only purpose of this suggestion to make it less of an "attractive nuisance"? On Jan 14, 2013, at 12:06 AM, Remi Forax wrote: > A remark of one of my colleague, > "if you want to avoid List> why Optional has methods hashCode and equals that are overriden". > > I think it makes sense to remove them. > > R?mi > From brian.goetz at oracle.com Mon Jan 14 10:02:02 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 14 Jan 2013 10:02:02 -0800 Subject: package access modifier? In-Reply-To: <50F3C05D.5020100@univ-mlv.fr> References: <50F3BFA5.6000509@oracle.com> <50F3C05D.5020100@univ-mlv.fr> Message-ID: <62E38DE1-B71B-4C4D-8412-821AD622CB35@oracle.com> Back at the last EG meeting, we discussed how in theory we would prefer to have a full set of accessibilities for interface methods, and that would require this little bit of syntax. The lack of this little bit of syntax is also a persistent annoyance to some people. However, since it is looking like even private methods may not make the train, this is certainly a dispensible feature for 8, and not doing it now does not foreclose on doing it later. On Jan 14, 2013, at 12:22 AM, Remi Forax wrote: > On 01/14/2013 09:19 AM, David Holmes wrote: >> I admit I haven't been able to keep up lately but I don't recall seeing any discussion of actually adding "package" as an access modifier, yet there it is. >> >> David > > I have seen that too, and I don't understand why it was introduced, > I remember we have discussed to add it to have a way to create package method in interface but since we rule out that. > > R?mi > From joe.bowbeer at gmail.com Mon Jan 14 10:50:09 2013 From: joe.bowbeer at gmail.com (Joe Bowbeer) Date: Mon, 14 Jan 2013 10:50:09 -0800 Subject: Optional.orElse(null) ambiguity In-Reply-To: References: Message-ID: I like: orElseFrom(Supplier) I don't like orElseGet(Supplier) because whenever I read orElse(other), I "translate" it as getOrElse(other). On Mon, Jan 14, 2013 at 10:23 AM, Brian Goetz wrote: > Null is a valid value for orElse(T) but not for orElse(Supplier). So we > care about this ambiguity (there are other similar ones we don't care > about, when null isn't a valid value for either option.) > > orElseNull is a possibility. > > Alternately, orElseGet(Supplier) ? orElseFrom(Supplier)? > > On Jan 13, 2013, at 10:00 PM, Joe Bowbeer wrote: > > > In b72, optional.orElse(null) is ambiguous because it matches both of > the orElse forms: > > > > T orElse(T other) > > T orElse(Supplier other) > > > > For example, this won't compile: > > > > return stream.max(comparator).orElse(null); > > > > > > Optional needs a concise way to return null, so I suggest: > > > > T orElseNull() > > > > --Joe > > From tim at peierls.net Mon Jan 14 10:55:41 2013 From: tim at peierls.net (Tim Peierls) Date: Mon, 14 Jan 2013 13:55:41 -0500 Subject: Optional.orElse(null) ambiguity In-Reply-To: References: Message-ID: On Mon, Jan 14, 2013 at 1:23 PM, Brian Goetz wrote: > Null is a valid value for orElse(T) but not for orElse(Supplier). So we > care about this ambiguity (there are other similar ones we don't care > about, when null isn't a valid value for either option.) > Guava Optional has or(T), or(Supplier), and orNull() where null is not an acceptable argument value for either of the first two. That has worked well for me in practice, but it would be hard on folks who insist on being able to stick null everywhere. --tim From daniel.smith at oracle.com Mon Jan 14 11:36:40 2013 From: daniel.smith at oracle.com (Dan Smith) Date: Mon, 14 Jan 2013 12:36:40 -0700 Subject: package access modifier? In-Reply-To: <62E38DE1-B71B-4C4D-8412-821AD622CB35@oracle.com> References: <50F3BFA5.6000509@oracle.com> <50F3C05D.5020100@univ-mlv.fr> <62E38DE1-B71B-4C4D-8412-821AD622CB35@oracle.com> Message-ID: See also the "Default method survey results" thread on the jsr335-eg at jcp.org and lambda-spec-experts lists from August/September of last year. Two points in that thread: 1) Officially calling the accessibility you get with no modifier "package access" rather than "default access" is important to help avoid the confusion of "Does 'default' on this method refer to accessibility or inheritance?" Introducing the 'package' keyword reinforces this terminology. 2) We concluded the thread without an in-depth discussion about the 'package' keyword for accessibility, setting that issue aside. It would be fine to start a new thread in lambda-spec-experts. (In fact, this is exactly the sort of thing that should be prompted by posts of updated specifications -- it seemed like a fairly noncontroversial change so we went ahead with it, but if someone wants to say, "hey, wait a minute...," that's great.) ?Dan On Jan 14, 2013, at 11:02 AM, Brian Goetz wrote: > Back at the last EG meeting, we discussed how in theory we would prefer to have a full set of accessibilities for interface methods, and that would require this little bit of syntax. The lack of this little bit of syntax is also a persistent annoyance to some people. However, since it is looking like even private methods may not make the train, this is certainly a dispensible feature for 8, and not doing it now does not foreclose on doing it later. > > On Jan 14, 2013, at 12:22 AM, Remi Forax wrote: > >> On 01/14/2013 09:19 AM, David Holmes wrote: >>> I admit I haven't been able to keep up lately but I don't recall seeing any discussion of actually adding "package" as an access modifier, yet there it is. >>> >>> David >> >> I have seen that too, and I don't understand why it was introduced, >> I remember we have discussed to add it to have a way to create package method in interface but since we rule out that. >> >> R?mi >> > From Donald.Raab at gs.com Mon Jan 14 12:11:15 2013 From: Donald.Raab at gs.com (Raab, Donald) Date: Mon, 14 Jan 2013 15:11:15 -0500 Subject: Stream In-Reply-To: References: <50F09A10.4010400@oracle.com> <50F2E620.4070803@oracle.com> Message-ID: <6712820CB52CFB4D842561213A77C05404C0C185E1@GSCMAMP09EX.firmwide.corp.gs.com> +1 From: lambda-libs-spec-experts-bounces at openjdk.java.net [mailto:lambda-libs-spec-experts-bounces at openjdk.java.net] On Behalf Of Joe Bowbeer Sent: Sunday, January 13, 2013 1:45 PM To: Brian Goetz Cc: lambda-libs-spec-experts at openjdk.java.net Subject: Re: Stream I'm starting to take the survey and I'm running into name/API changes that haven't appeared yet in a binary snapshot (e.g., b72). I can't comment effectively on these until I've used them in running code. (I'll add this comment to the survey as well, but I thought it would be good to point this out, esp. w.r.t. deadlines.) On Sun, Jan 13, 2013 at 8:51 AM, Brian Goetz > wrote: I forgot the all-important aspect of this: a deadline. Thanks to Remi and Tim for jumping on this already. Let's aim to get all responses in by midnight Monday of some unnamed time zone. I'll then collate the responses and relay to the list. On 1/11/2013 6:02 PM, Brian Goetz wrote: I've done a pass through the repo and marked almost all the classes package-private. There are only a handful of public classes now. I'm going to do a set of surveys that cover all the public methods of all the public classes. This is the first one, and it's on Stream. EG members will be receiving their surveys directly from SurveyMonkey. If you don't get one within a few hours, let me know. There are not really any questions; there is a free-response box for every method or group of related methods in Stream. You are encouraged to write comments on: - the suitability of the method - the specific details of its arguments - generics nits - naming nits - semantic wonderings - anything else you might have to say Writing nothing in the box will be interpreted as "I think this method is great as is." Specifically, the Collector (reducer) API is still a work in progress, so there will be another opportunity to give final feedback on that (but feel free to give feedback here too.) From dl at cs.oswego.edu Mon Jan 14 12:33:33 2013 From: dl at cs.oswego.edu (Doug Lea) Date: Mon, 14 Jan 2013 15:33:33 -0500 Subject: Spliterator implementations In-Reply-To: <50E4A0F0.1040701@cs.oswego.edu> References: <50E4A0F0.1040701@cs.oswego.edu> Message-ID: <50F46B9D.7020802@cs.oswego.edu> Status report. The implementations for a bunch of these are now basically done. (modulo some API issues Brian and I are working out.) Here's a run-down. Reasonably soon I might need the help of Mike to do lambda checkins? A few quick notes: * So far only lightly tested, mainly by adding up a million Longs seq and par, across different variants * For map views, that tend to have relatively slow iterators that can be streamlined a lot in consolidated forEach loops, seq spliterator.forEach performance is usually very good, so long as lambdas get resolved. Other cases less so. On 01/02/13 16:04, Doug Lea wrote: > For top-level classes only: > j.u.c/jsr166: > ArrayDeque, done > ArrayBlockingQueue, > ConcurrentLinkedDeque, > ConcurrentLinkedQueue, > CopyOnWriteArraySet, > DelayQueue, > LinkedBlockingDeque, > LinkedBlockingQueue, > LinkedTransferQueue, use default on all of these > PriorityBlockingQueue, done (as snaoshot) > PriorityQueue, done > SynchronousQueue use default > j.u: > AbstractCollection, > AbstractQueue, > AbstractSet, other people are doing these > EnumSet, not sure if anyone is doing; default probably OK > HashSet (delegates to HashMap.keySet), I think other people are doing? > LinkedHashSet (delegates to LinkedHashMap.keySet) I think other people are doing? > Stack, exists > other jdk: > BeanContextSupport (delegates to HashMap.keySet) > BeanContextServicesSupport (super BeanContextSupport) > JobStateReasons, (super HashSet) not me. > > For both top-level and subList classes > j.u.c/jsr166 > CopyOnWriteArrayList, done > j.u: > AbstractList, > AbstractSequentialList, other people are doing these > ArrayList, exists, but may help improve > AttributeList, dunno > LinkedList, presumably use default > Stack (super Vector), > Vector other people are doing these > other jdk: > RoleList (super ArrayList), > RoleUnresolvedList (super ArrayList), dunno > For both top-level and subSet classes: > j.u.c/jsr166 > ConcurrentSkipListSet, done (and surprisingly fast) > TreeSet done > For keySet, values and entrySet views of: > j.u.c/jsr166 > ConcurrentHashMap, done > j.u: > AbstractMap, other people? > EnumMap, not sure if anyone is doing; default probably OK > HashMap, I'm pretty sure someone else is overhauling JDK8 HM? > Hashtable, ditto? > IdentityHashMap, done (and has the best parallel speedups) > LinkedHashMap, (super HashMap but cannot use same Spliterator) Please not me. > WeakHashMap not yet done -- I just remembered that it is not subject to alt-hash overhaul > other jdk: > Attributes (delegates to HashMap) > AuthProvider (super Provider) > PrinterStateReasons, (super HashMap) > Properties, (super Hashtable) > Provider (super Properties), > RenderingHints (delegates to HashMap) > SimpleBindings (delegates to any Map), > TabularDataSupport, (delegates to > UIDefaults, (super Hashtable) dunno > For keySet, values and entrySet views, plus the same for subMap views: > j.u.c/jsr166 > ConcurrentSkipListMap, > TreeMap, done for TreeMap.descendingX only. The others use defaults. -Doug From joe.bowbeer at gmail.com Mon Jan 14 14:54:24 2013 From: joe.bowbeer at gmail.com (Joe Bowbeer) Date: Mon, 14 Jan 2013 14:54:24 -0800 Subject: Word Chain Kata Message-ID: I wrote a Java8 version of the Word Chains problem from codekata.pragprog.com. Mercurial Project: https://bitbucket.org/joebowbeer/wordchainkata The interesting parallel piece is the method below that finds all the words that are adjacent to a given word: protected Collection findNeighbors(String word) { return words.parallelStream().filter(s -> adjacent(s, word)).into(new ArrayDeque()); } Where words is a set of n-letter words (obtained from icon.shef.ac.uk/Moby/mwords.html) and adjacency is defined as differing in exactly one character. This was developed in NetBeans build #1605 for jdk8lambda and compiles and runs on jdklambda b72. I'll update this once the replacement for into() makes it into the binary snapshot... --Joe From forax at univ-mlv.fr Mon Jan 14 14:58:54 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Mon, 14 Jan 2013 23:58:54 +0100 Subject: Optional.equals/hashCode In-Reply-To: <44D3C731-B3DB-4393-9CC0-BBA621E6E95B@oracle.com> References: <50F3BC99.5010904@univ-mlv.fr> <44D3C731-B3DB-4393-9CC0-BBA621E6E95B@oracle.com> Message-ID: <50F48DAE.7020806@univ-mlv.fr> On 01/14/2013 07:00 PM, Brian Goetz wrote: > Is the only purpose of this suggestion to make it less of an "attractive nuisance"? yes. R?mi > > On Jan 14, 2013, at 12:06 AM, Remi Forax wrote: > >> A remark of one of my colleague, >> "if you want to avoid List> why Optional has methods hashCode and equals that are overriden". >> >> I think it makes sense to remove them. >> >> R?mi >> From joe.bowbeer at gmail.com Tue Jan 15 15:56:11 2013 From: joe.bowbeer at gmail.com (Joe Bowbeer) Date: Tue, 15 Jan 2013 15:56:11 -0800 Subject: Anagrams Kata (Was: ConcurrentHashMap/ConcurrentMap/Map.compute) In-Reply-To: References: Message-ID: I updated the Anagrams sample (bitbucket.org/joebowbeer/anagrams) to use the ConcurrentCollectors that are available in the latest binary snapshot (b73). The parallel piece is now: public static Stream> anagrams(Stream words) { Map> map = words.parallel().collectUnordered( ConcurrentCollectors.groupBy(Anagrams::key)); return map.values().parallelStream().filter(v -> v.size() > 1); } And I'm now seeing some parallel speedup! Notes: 1. "collectUnordered" is important. If it is replaced by "collect" then the program will appear to hang for many seconds before finally producing output. 2. Even with -XDuseGraphInference, this will not compile without the extra " ConcurrentCollectors.". In fact, the compiler throws an assertion error if one even attempts to get by with merely a groupBy static import... Joe On Thu, Jan 10, 2013 at 10:28 AM, Joe Bowbeer wrote: > Thanks for the suggestions Paul. > > After updating to jdk8lambda b72, the anagram collector is now expressed > as: > > map = r.lines().accumulate(Accumulators. String>groupBy(Anagrams::key)); > > Notes: > > 1. Adding .parallel() after lines() works, but in my tests on a dual-core > laptop, it increased the accumulate time by an order of magnitude. > > 2. The Accumulators.<> type specifier is currently required, unfortunately. > > The complete source (all 31 lines) is at > bitbucket.org/joebowbeer/anagrams/src/ > > In my trials, I'm using the long list of single words from > http://icon.shef.ac.uk/Moby/mwords.html > > Joe > > On Wed, Jan 9, 2013 at 1:15 AM, Paul Sandoz wrote: > >> >> On Jan 9, 2013, at 7:47 AM, Joe Bowbeer wrote: >> >> > In the javadoc for Map.compute in b72, there is an error in the sample >> > snippet: >> > >> > http://download.java.net/lambda/b72/docs/api/java/util/Map.html >> > >> > Attempts to compute a mapping for the specified key and its current >> mapped >> >> value (or null if there is no current mapping). For example, to either >> >> create or append a String msg to a value mapping: >> >> map.compute(key, (k, v, msg) -> (v == null) ? msg : v.concat(msg)) >> > >> > >> > Error: BiFunction does not accept three arguments. In particular, msg >> is >> > extraneous. It should be defined in the lexical scope? >> > >> > >> > Btw, I pondered how to efficiently use compute() or merge() to simulate >> a >> > multi-map and I eventually gave up. >> > >> > I eventually wrote the following, which accumulates lists of anagrams, >> > given a list of words: >> > >> > r.lines().forEach(s -> map.computeIfAbsent(key(s), k -> new >> > ArrayList<>()).add(s)); >> > >> >> Have you tried using the latest groupBy functionality? e.g.: >> >> Map> anagrams = >> r.lines().parallel().reduce(groupBy(s -> key(s))); >> >> It would be interesting to compare performance of reduce vs. >> reduceUnordered and the concurrent groupBy leveraging CHM. >> >> >> > >> > Where key() returns the canonical key for a given word, and r is a >> > BufferedReader for the dictionary file. >> > >> > The following line prints the lists of anagrams: >> > >> > map.values().stream().filter(v -> v.size() > 1).forEach(v -> >> > System.out.println(v)); >> > >> >> Little style tip: >> >> forEach(System.out::println) >> >> Paul. > > > From joe.bowbeer at gmail.com Tue Jan 15 16:55:28 2013 From: joe.bowbeer at gmail.com (Joe Bowbeer) Date: Tue, 15 Jan 2013 16:55:28 -0800 Subject: Word Chain Kata In-Reply-To: References: Message-ID: I updated this example to use Collectors.toList(), which is available in binary snapshot b73. bitbucket.org/joebowbeer/wordchainkata On Mon, Jan 14, 2013 at 2:54 PM, Joe Bowbeer wrote: > I wrote a Java8 version of the Word Chains problem from codekata.pragprog.com. > > Mercurial Project: https://bitbucket.org/joebowbeer/wordchainkata > > The interesting parallel piece is the method below that finds all the words that are adjacent to a given word: > > protected Collection findNeighbors(String word) { > return words.parallelStream().filter(s -> adjacent(s, word)).into(new ArrayDeque()); > } > > Where words is a set of n-letter words (obtained from icon.shef.ac.uk/Moby/mwords.html) and adjacency is defined as differing in exactly one character. > > > This was developed in NetBeans build #1605 for jdk8lambda and compiles and runs on jdklambda b72. I'll update this once the replacement for into() makes it into the binary snapshot... > > > --Joe > > > From brian.goetz at oracle.com Tue Jan 15 18:01:32 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 15 Jan 2013 18:01:32 -0800 Subject: Anagrams Kata (Was: ConcurrentHashMap/ConcurrentMap/Map.compute) In-Reply-To: References: Message-ID: <53576C26-B148-4499-8ACD-CD4387E76E35@oracle.com> > 1. "collectUnordered" is important. If it is replaced by "collect" then the program will appear to hang for many seconds before finally producing output. Yes. We expected that concurrent collection would be faster for simple group-by, though also we have a lot of tuning to do in the non-concurrent collection. From forax at univ-mlv.fr Wed Jan 16 03:58:02 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Wed, 16 Jan 2013 12:58:02 +0100 Subject: IntStream.sum Message-ID: <50F695CA.8010502@univ-mlv.fr> IntStream.sum have just been changed to return a long instead of an int and I don't think it's a good idea. There is 3 ways to deal with overflows: 1) do nothing, the Java default semantics 2) throw an exception if overflown (using Integer.addExact by example) 3) use BigInteger all other ways are just half baked solution. This one is very bad because usually users will use it that way, int sum = (int)intStream.sum(); so it doesn't solve anything, just make the semantics awkward. sum() is just a shortcut for a reduce, so it should stick with the default Java semantics, if users want longs, they can use reduce with Long::add or Long::addExact. R?mi From misterm at gmail.com Wed Jan 16 04:04:21 2013 From: misterm at gmail.com (Michael Nascimento) Date: Wed, 16 Jan 2013 10:04:21 -0200 Subject: IntStream.sum In-Reply-To: <50F695CA.8010502@univ-mlv.fr> References: <50F695CA.8010502@univ-mlv.fr> Message-ID: It is worth adding to javadoc the intended pattern if users expect an int is: int sum = Math.toIntExact(intStream.sum()); Regards, Michael On Wed, Jan 16, 2013 at 9:58 AM, Remi Forax wrote: > IntStream.sum have just been changed to return a long instead of an int and > I don't think it's a good idea. > > There is 3 ways to deal with overflows: > 1) do nothing, the Java default semantics > 2) throw an exception if overflown (using Integer.addExact by example) > 3) use BigInteger > all other ways are just half baked solution. > > This one is very bad because usually users will use it that way, > int sum = (int)intStream.sum(); > so it doesn't solve anything, just make the semantics awkward. > > sum() is just a shortcut for a reduce, so it should stick with the default > Java semantics, > if users want longs, they can use reduce with Long::add or Long::addExact. > > R?mi > > > > > From joe.bowbeer at gmail.com Wed Jan 16 05:14:39 2013 From: joe.bowbeer at gmail.com (Joe Bowbeer) Date: Wed, 16 Jan 2013 05:14:39 -0800 Subject: IntStream.sum In-Reply-To: <50F695CA.8010502@univ-mlv.fr> References: <50F695CA.8010502@univ-mlv.fr> Message-ID: Normally I would side with the stricter argument in order to avoid potential harm, but in this case I don't see the problem. Code quality tools will flag the (int), right? And long can hold the result of MAXINT additions? On Jan 16, 2013 4:01 AM, "Remi Forax" wrote: > IntStream.sum have just been changed to return a long instead of an int > and I don't think it's a good idea. > > There is 3 ways to deal with overflows: > 1) do nothing, the Java default semantics > 2) throw an exception if overflown (using Integer.addExact by example) > 3) use BigInteger > all other ways are just half baked solution. > > This one is very bad because usually users will use it that way, > int sum = (int)intStream.sum(); > so it doesn't solve anything, just make the semantics awkward. > > sum() is just a shortcut for a reduce, so it should stick with the default > Java semantics, > if users want longs, they can use reduce with Long::add or Long::addExact. > > R?mi > > > > > > From forax at univ-mlv.fr Wed Jan 16 05:15:43 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Wed, 16 Jan 2013 14:15:43 +0100 Subject: IntStream.sum In-Reply-To: References: <50F695CA.8010502@univ-mlv.fr> Message-ID: <50F6A7FF.3070706@univ-mlv.fr> On 01/16/2013 02:14 PM, Joe Bowbeer wrote: > > Normally I would side with the stricter argument in order to avoid > potential harm, but in this case I don't see the problem. > the problem is that id doesn't solve the overflow problem, it just hide it more. > Code quality tools will flag the (int), right? > yes, raising the number of false positive warnings. > And long can hold the result of MAXINT additions? > stream are sized is store in a long if a size if definite or can be infinite so it solves nothing. R?mi > On Jan 16, 2013 4:01 AM, "Remi Forax" > wrote: > > IntStream.sum have just been changed to return a long instead of > an int and I don't think it's a good idea. > > There is 3 ways to deal with overflows: > 1) do nothing, the Java default semantics > 2) throw an exception if overflown (using Integer.addExact by > example) > 3) use BigInteger > all other ways are just half baked solution. > > This one is very bad because usually users will use it that way, > int sum = (int)intStream.sum(); > so it doesn't solve anything, just make the semantics awkward. > > sum() is just a shortcut for a reduce, so it should stick with the > default Java semantics, > if users want longs, they can use reduce with Long::add or > Long::addExact. > > R?mi > > > > > From forax at univ-mlv.fr Wed Jan 16 05:24:58 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Wed, 16 Jan 2013 14:24:58 +0100 Subject: IntStream.sum In-Reply-To: <50F6A7FF.3070706@univ-mlv.fr> References: <50F695CA.8010502@univ-mlv.fr> <50F6A7FF.3070706@univ-mlv.fr> Message-ID: <50F6AA2A.9040606@univ-mlv.fr> On 01/16/2013 02:15 PM, Remi Forax wrote: ok, same sentences, but in english that time: > On 01/16/2013 02:14 PM, Joe Bowbeer wrote: >> >> Normally I would side with the stricter argument in order to avoid >> potential harm, but in this case I don't see the problem. >> > > the problem is that it doesn't solve the overflow problem, it just > hides it more. > >> Code quality tools will flag the (int), right? >> > > yes, raising the number of false positive warnings. > >> And long can hold the result of MAXINT additions? >> > > stream are sized by a long if there a size or can be infinite (still > think it's a bad idea BTW) > so it solves nothing. > > R?mi R?mi From paul.sandoz at oracle.com Wed Jan 16 05:32:03 2013 From: paul.sandoz at oracle.com (Paul Sandoz) Date: Wed, 16 Jan 2013 14:32:03 +0100 Subject: IntStream.sum In-Reply-To: <50F6A7FF.3070706@univ-mlv.fr> References: <50F695CA.8010502@univ-mlv.fr> <50F6A7FF.3070706@univ-mlv.fr> Message-ID: On Jan 16, 2013, at 2:15 PM, Remi Forax wrote: > >> And long can hold the result of MAXINT additions? >> > > stream are sized is store in a long if a size if definite or can be infinite > so it solves nothing. > I don't quite understand the above sentence. Are you suggesting reducing/folding on a infinite stream? Paul. From forax at univ-mlv.fr Wed Jan 16 05:33:01 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Wed, 16 Jan 2013 14:33:01 +0100 Subject: IntStream.sum In-Reply-To: References: <50F695CA.8010502@univ-mlv.fr> <50F6A7FF.3070706@univ-mlv.fr> Message-ID: <50F6AC0D.6040204@univ-mlv.fr> On 01/16/2013 02:32 PM, Paul Sandoz wrote: > On Jan 16, 2013, at 2:15 PM, Remi Forax wrote: >>> And long can hold the result of MAXINT additions? >>> >> stream are sized is store in a long if a size if definite or can be infinite >> so it solves nothing. >> > I don't quite understand the above sentence. Are you suggesting reducing/folding on a infinite stream? yes, and you throw an exception when you've done, Brian talk about that. > > Paul. R?mi From paul.sandoz at oracle.com Wed Jan 16 05:53:28 2013 From: paul.sandoz at oracle.com (Paul Sandoz) Date: Wed, 16 Jan 2013 14:53:28 +0100 Subject: IntStream.sum In-Reply-To: <50F6AC0D.6040204@univ-mlv.fr> References: <50F695CA.8010502@univ-mlv.fr> <50F6A7FF.3070706@univ-mlv.fr> <50F6AC0D.6040204@univ-mlv.fr> Message-ID: On Jan 16, 2013, at 2:33 PM, Remi Forax wrote: > On 01/16/2013 02:32 PM, Paul Sandoz wrote: >> On Jan 16, 2013, at 2:15 PM, Remi Forax wrote: >>>> And long can hold the result of MAXINT additions? >>>> >>> stream are sized is store in a long if a size if definite or can be infinite >>> so it solves nothing. >>> >> I don't quite understand the above sentence. Are you suggesting reducing/folding on a infinite stream? > > yes, and you throw an exception when you've done, Brian talk about that. > Well don't reduce/fold on a infinite stream :-) I don't really buy into your argument that because there might be streams of size > Integer.MAX_VALUE that it implies sum() solves nothing, seems a rather extreme position to take. Do you think sum() solves nothing regardless of whether it returns an int or long? Paul. From forax at univ-mlv.fr Wed Jan 16 07:53:58 2013 From: forax at univ-mlv.fr (=?utf-8?B?UmVtaSBGb3JheA==?=) Date: Wed, 16 Jan 2013 16:53:58 +0100 Subject: =?utf-8?B?UmU6IEludFN0cmVhbS5zdW0=?= Message-ID: <201301161553.r0GFrsxG021075@monge.univ-mlv.fr> My argument is when you reduce over integers, you want an integer as result, like int + int is an int. Reducing over integers and get a long should not be the default behavior because it's not what people espect. If someone want a long, using reduce is better because reduce require to pass an operator so user can choose the exact semantics he want, throwing an exception or using a BigInteger is perhaps better. R?mi Sent from my Phone ----- Reply message ----- From: "Paul Sandoz" To: Cc: Subject: IntStream.sum Date: Wed, Jan 16, 2013 14:53 On Jan 16, 2013, at 2:33 PM, Remi Forax wrote: > On 01/16/2013 02:32 PM, Paul Sandoz wrote: >> On Jan 16, 2013, at 2:15 PM, Remi Forax wrote: >>>> And long can hold the result of MAXINT additions? >>>> >>> stream are sized is store in a long if a size if definite or can be infinite >>> so it solves nothing. >>> >> I don't quite understand the above sentence. Are you suggesting reducing/folding on a infinite stream? > > yes, and you throw an exception when you've done, Brian talk about that. > Well don't reduce/fold on a infinite stream :-) I don't really buy into your argument that because there might be streams of size > Integer.MAX_VALUE that it implies sum() solves nothing, seems a rather extreme position to take. Do you think sum() solves nothing regardless of whether it returns an int or long? Paul. From kevinb at google.com Wed Jan 16 08:37:16 2013 From: kevinb at google.com (Kevin Bourrillion) Date: Wed, 16 Jan 2013 08:37:16 -0800 Subject: IntStream.sum In-Reply-To: <201301161553.r0GFrsxG021075@monge.univ-mlv.fr> References: <201301161553.r0GFrsxG021075@monge.univ-mlv.fr> Message-ID: On Wed, Jan 16, 2013 at 7:53 AM, Remi Forax wrote: My argument is when you reduce over integers, you want an integer as > result, like int + int is an int. > This argument seems overly abstract when we're trying to address the very concrete problem of overflow. Overflow is already a problem for int + int, but gets a lot more worrisome when summing a stream. Reducing over integers and get a long should not be the default behavior > because it's not what people espect. > It's not that unexpected either. I like the long return value, because the chance of 63-bit overflow is so incredibly less likely than 31-bit overflow. > If someone want a long, using reduce is better because reduce require to > pass an operator so user can choose the exact semantics he want, throwing > an exception or using a BigInteger is perhaps better. > A long return value also lets him choose the semantics he wants: he can then either do a plain cast, checked cast or saturated cast. (Guava provides Ints.checkedCast() and Ints.saturatedCast(), FYI.) And that check only has to happen once at the end. The risk of 63-bit overflow is the truly esoteric one, and I'm fine with any amount of hand-rolling needed for that. -- Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com From forax at univ-mlv.fr Wed Jan 16 09:53:56 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Wed, 16 Jan 2013 18:53:56 +0100 Subject: IntStream.sum In-Reply-To: References: <201301161553.r0GFrsxG021075@monge.univ-mlv.fr> Message-ID: <50F6E934.7060309@univ-mlv.fr> On 01/16/2013 05:37 PM, Kevin Bourrillion wrote: > On Wed, Jan 16, 2013 at 7:53 AM, Remi Forax > wrote: > > My argument is when you reduce over integers, you want an integer > as result, like int + int is an int. > > > This argument seems overly abstract when we're trying to address the > very concrete problem of overflow. Overflow is already a problem for > int + int, but gets a lot more worrisome when summing a stream. > > > Reducing over integers and get a long should not be the default > behavior because it's not what people espect. > > > It's not that unexpected either. > > I like the long return value, because the chance of 63-bit overflow is > so incredibly less likely than 31-bit overflow. given that most people will cast the result to int, this argument is moot. > > If someone want a long, using reduce is better because reduce > require to pass an operator so user can choose the exact semantics > he want, throwing an exception or using a BigInteger is perhaps > better. > > > A long return value also lets him choose the semantics he wants: he > can then either do a plain cast, checked cast or saturated cast. > (Guava provides Ints.checkedCast() and Ints.saturatedCast(), FYI.) > And that check only has to happen once at the end. The risk of > 63-bit overflow is the truly esoteric one, and I'm fine with any > amount of hand-rolling needed for that. checkedAdd seems as fast as + in a loop nowadays, I think it's because the calculation is bound by the cost of the memory accesses not by the CPU processing. Used in a pipeline that does megamorphic calls it will be invisible. Anyway, i'm clearly in minority and I don't think this issue deserve a long thread, so I'm fine with the long return value. R?mi From kevinb at google.com Wed Jan 16 09:59:04 2013 From: kevinb at google.com (Kevin Bourrillion) Date: Wed, 16 Jan 2013 09:59:04 -0800 Subject: IntStream.sum In-Reply-To: <50F6E934.7060309@univ-mlv.fr> References: <201301161553.r0GFrsxG021075@monge.univ-mlv.fr> <50F6E934.7060309@univ-mlv.fr> Message-ID: (making it just one line longer) On Wed, Jan 16, 2013 at 9:53 AM, Remi Forax wrote: I like the long return value, because the chance of 63-bit overflow is so >> incredibly less likely than 31-bit overflow. >> > > given that most people will cast the result to int, this argument is moot. Not at all, because it's *their* cast, in *their* code, which, if they are actually conscientious, *their* static analysis tools and code reviewers will challenge them over. -- Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com From dl at cs.oswego.edu Wed Jan 16 10:04:37 2013 From: dl at cs.oswego.edu (Doug Lea) Date: Wed, 16 Jan 2013 13:04:37 -0500 Subject: IntStream.sum In-Reply-To: <50F6E934.7060309@univ-mlv.fr> References: <201301161553.r0GFrsxG021075@monge.univ-mlv.fr> <50F6E934.7060309@univ-mlv.fr> Message-ID: <50F6EBB5.8010906@cs.oswego.edu> On 01/16/13 12:53, Remi Forax wrote: > Anyway, i'm clearly in minority and I don't think this issue deserve a long thread, > so I'm fine with the long return value. > FWIW, I'm with you on this. I think that if people want to avoid overflow using longs, then they should use longs! Doing this as a convenience here and there just postpones the inevitable, and makes it more likely that an overflow bug elsewhere in people's code will stay hidden. -Doug From joe.darcy at oracle.com Wed Jan 16 10:13:20 2013 From: joe.darcy at oracle.com (Joe Darcy) Date: Wed, 16 Jan 2013 10:13:20 -0800 Subject: IntStream.sum In-Reply-To: References: <201301161553.r0GFrsxG021075@monge.univ-mlv.fr> <50F6E934.7060309@univ-mlv.fr> Message-ID: <50F6EDC0.7050602@oracle.com> On 01/16/2013 09:59 AM, Kevin Bourrillion wrote: > (making it just one line longer) And one more line... > > > On Wed, Jan 16, 2013 at 9:53 AM, Remi Forax > wrote: > > I like the long return value, because the chance of 63-bit > overflow is so incredibly less likely than 31-bit overflow. > > > given that most people will cast the result to int, this argument > is moot. > > > Not at all, because it's /their/ cast, in /their/ code, which, if they > are actually conscientious, /their/ static analysis tools and code > reviewers will challenge them over. > > Summing in a long allows for the possibility of an intermediate overflow of int range with the final result coming back into int range. -Joe From forax at univ-mlv.fr Wed Jan 16 10:16:56 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Wed, 16 Jan 2013 19:16:56 +0100 Subject: Convert old "into" code to the collectors In-Reply-To: References: <50F6C1B2.8070007@oracle.com> Message-ID: <50F6EE98.3010400@univ-mlv.fr> Does anyone can find a better name that toCollection that suggest that the supplier taken as parameter can be called more than once ? R?mi from lambda-dev: On 01/16/2013 05:17 PM, Paul Sandoz wrote: > On Jan 16, 2013, at 5:10 PM, Michael Nascimento wrote: > >> Since you have an existing instance, you can do: >> >> toCollection(() -> propriedades) >> > That will break the contract and the results will be undefined when going parallel. > > When going parallel multiple instances of the collection will need to be created and those instances will be merged together. > > Paul. > >> Regards, >> Michael From Donald.Raab at gs.com Wed Jan 16 12:37:53 2013 From: Donald.Raab at gs.com (Raab, Donald) Date: Wed, 16 Jan 2013 15:37:53 -0500 Subject: GS Collections Kata w/ JCF Solutions Message-ID: <6712820CB52CFB4D842561213A77C05404C0C18764@GSCMAMP09EX.firmwide.corp.gs.com> I checked in new solutions using the latest b73 binary. I wound up implementing b72 and b73 within a 48 hour period so it was interesting to see the evolution of user experience. I've shared a few rounds of the evolution of the kata using the binary snapshots here. b73 (explode/collect): https://github.com/goldmansachs/gs-collections-kata/blob/solutions-java8-jcf/src/test/java/com/gs/collections/kata/Exercise9Test.java b72 (mapMulti/accumulate/into:): https://github.com/goldmansachs/gs-collections-kata/blob/105a5dd64aafcf2a0fe3bebda6c120a7e759e2b7/src/test/java/com/gs/collections/kata/Exercise9Test.java Prior: (flatMap/tabulate/into): https://github.com/goldmansachs/gs-collections-kata/blob/d9e4f006012d9b5c9e2ede0e78e426b94b740890/src/test/java/com/gs/collections/kata/Exercise9Test.java Prior: (flatMap/groupBy/into) https://github.com/goldmansachs/gs-collections-kata/blob/9c344e12e3f502c9f9f2961135a6b095207f9d88/src/test/java/com/gs/collections/kata/Exercise9Test.java First binary version I tried: (flatMap w/ Mapper/no groupBy available/into) https://github.com/goldmansachs/gs-collections-kata/blob/dd3055771cb09c8a2ed1fc9d9ebe9cb09a8e2930/src/test/java/com/gs/collections/kata/Exercise9Test.java From joe.bowbeer at gmail.com Wed Jan 16 13:20:48 2013 From: joe.bowbeer at gmail.com (Joe Bowbeer) Date: Wed, 16 Jan 2013 13:20:48 -0800 Subject: GS Collections Kata w/ JCF Solutions In-Reply-To: <6712820CB52CFB4D842561213A77C05404C0C18764@GSCMAMP09EX.firmwide.corp.gs.com> References: <6712820CB52CFB4D842561213A77C05404C0C18764@GSCMAMP09EX.firmwide.corp.gs.com> Message-ID: Thanks. Would it be better to rewrite the following using Stream.max()? .reduce(0.0, (x, y) -> Math.max(x, y)) --Joe On Wed, Jan 16, 2013 at 12:37 PM, Raab, Donald wrote: > I checked in new solutions using the latest b73 binary. I wound up > implementing b72 and b73 within a 48 hour period so it was interesting to > see the evolution of user experience. I've shared a few rounds of the > evolution of the kata using the binary snapshots here. > > b73 (explode/collect): > > > https://github.com/goldmansachs/gs-collections-kata/blob/solutions-java8-jcf/src/test/java/com/gs/collections/kata/Exercise9Test.java > > b72 (mapMulti/accumulate/into:): > > > https://github.com/goldmansachs/gs-collections-kata/blob/105a5dd64aafcf2a0fe3bebda6c120a7e759e2b7/src/test/java/com/gs/collections/kata/Exercise9Test.java > > Prior: (flatMap/tabulate/into): > > > https://github.com/goldmansachs/gs-collections-kata/blob/d9e4f006012d9b5c9e2ede0e78e426b94b740890/src/test/java/com/gs/collections/kata/Exercise9Test.java > > Prior: (flatMap/groupBy/into) > > > https://github.com/goldmansachs/gs-collections-kata/blob/9c344e12e3f502c9f9f2961135a6b095207f9d88/src/test/java/com/gs/collections/kata/Exercise9Test.java > > First binary version I tried: (flatMap w/ Mapper/no groupBy available/into) > > > https://github.com/goldmansachs/gs-collections-kata/blob/dd3055771cb09c8a2ed1fc9d9ebe9cb09a8e2930/src/test/java/com/gs/collections/kata/Exercise9Test.java > > From Donald.Raab at gs.com Wed Jan 16 16:15:52 2013 From: Donald.Raab at gs.com (Raab, Donald) Date: Wed, 16 Jan 2013 19:15:52 -0500 Subject: GS Collections Kata w/ JCF Solutions In-Reply-To: References: <6712820CB52CFB4D842561213A77C05404C0C18764@GSCMAMP09EX.firmwide.corp.gs.com> Message-ID: <6712820CB52CFB4D842561213A77C05404C0C1878A@GSCMAMP09EX.firmwide.corp.gs.com> Great suggestion. I just gave it a try. Max takes a comparator and returns Optional so you have to add a call to get(). I was able to change the example to the following: Map> multimap = this.company.getCustomers() .stream() .collect(Collectors.groupBy((Customer customer) -> customer.getOrders() .stream() .explode((Stream.Downstream downstream, Order order) -> { downstream.send(order.getLineItems()); }) .map(LineItem::getValue) .max(Comparators.naturalOrder()) .get())); I get an unchecked error on Comparators.naturalOrder() in IntelliJ, but adding there makes it ugly. This is the GS Collections version of the kata, which also used max(), but calls this method on a DoubleIterable: MutableListMultimap multimap = this.company.getCustomers() .groupBy(customer -> customer.getOrders() .asLazy() .flatCollect(Order::getLineItems) .collectDouble(LineItem::getValue) .max()); Unfortunately, the type inference seems to be very successful at thwarting me trying to simplify the above example using the stream library features. I would like to simplify down to this but can't seem to get it to work: Map> multimap = this.company.getCustomers() .stream() .collect(groupBy((customer) -> customer.getOrders() .stream() .explode((downstream, order) -> { downstream.send(order.getLineItems()); }) .map(LineItem::getValue) .max(Comparators.naturalOrder()) .get())); From: Joe Bowbeer [mailto:joe.bowbeer at gmail.com] Sent: Wednesday, January 16, 2013 4:21 PM To: Raab, Donald [Tech] Cc: lambda-libs-spec-experts at openjdk.java.net; Motlin, Craig P. [Tech]; Weir, John [Tech] Subject: Re: GS Collections Kata w/ JCF Solutions Thanks. Would it be better to rewrite the following using Stream.max()? .reduce(0.0, (x, y) -> Math.max(x, y)) --Joe From brian.goetz at oracle.com Wed Jan 16 17:55:23 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 16 Jan 2013 17:55:23 -0800 Subject: GS Collections Kata w/ JCF Solutions In-Reply-To: <6712820CB52CFB4D842561213A77C05404C0C1878A@GSCMAMP09EX.firmwide.corp.gs.com> References: <6712820CB52CFB4D842561213A77C05404C0C18764@GSCMAMP09EX.firmwide.corp.gs.com> <6712820CB52CFB4D842561213A77C05404C0C1878A@GSCMAMP09EX.firmwide.corp.gs.com> Message-ID: <2BAE8A49-B082-4AB2-A053-4BE7FB65D14C@oracle.com> Try compiling with -XDuseGraphInference. On Jan 16, 2013, at 4:15 PM, Raab, Donald wrote: > Great suggestion. I just gave it a try. Max takes a comparator and returns Optional so you have to add a call to get(). I was able to change the example to the following: > > Map> multimap = this.company.getCustomers() > .stream() > .collect(Collectors.groupBy((Customer customer) -> > customer.getOrders() > .stream() > .explode((Stream.Downstream downstream, Order order) -> { > downstream.send(order.getLineItems()); > }) > .map(LineItem::getValue) > .max(Comparators.naturalOrder()) > .get())); > > I get an unchecked error on Comparators.naturalOrder() in IntelliJ, but adding there makes it ugly. > > This is the GS Collections version of the kata, which also used max(), but calls this method on a DoubleIterable: > > MutableListMultimap multimap = this.company.getCustomers() > .groupBy(customer -> customer.getOrders() > .asLazy() > .flatCollect(Order::getLineItems) > .collectDouble(LineItem::getValue) > .max()); > > Unfortunately, the type inference seems to be very successful at thwarting me trying to simplify the above example using the stream library features. I would like to simplify down to this but can't seem to get it to work: > > Map> multimap = this.company.getCustomers() > .stream() > .collect(groupBy((customer) -> > customer.getOrders() > .stream() > .explode((downstream, order) -> { > downstream.send(order.getLineItems()); > }) > .map(LineItem::getValue) > .max(Comparators.naturalOrder()) > .get())); > > > From: Joe Bowbeer [mailto:joe.bowbeer at gmail.com] > Sent: Wednesday, January 16, 2013 4:21 PM > To: Raab, Donald [Tech] > Cc: lambda-libs-spec-experts at openjdk.java.net; Motlin, Craig P. [Tech]; Weir, John [Tech] > Subject: Re: GS Collections Kata w/ JCF Solutions > > Thanks. > > > Would it be better to rewrite the following using Stream.max()? > > > .reduce(0.0, (x, y) -> Math.max(x, y)) > > --Joe > > From Donald.Raab at gs.com Wed Jan 16 18:14:04 2013 From: Donald.Raab at gs.com (Raab, Donald) Date: Wed, 16 Jan 2013 21:14:04 -0500 Subject: GS Collections Kata w/ JCF Solutions In-Reply-To: <2BAE8A49-B082-4AB2-A053-4BE7FB65D14C@oracle.com> References: <6712820CB52CFB4D842561213A77C05404C0C18764@GSCMAMP09EX.firmwide.corp.gs.com> <6712820CB52CFB4D842561213A77C05404C0C1878A@GSCMAMP09EX.firmwide.corp.gs.com> <2BAE8A49-B082-4AB2-A053-4BE7FB65D14C@oracle.com> Message-ID: <6712820CB52CFB4D842561213A77C05404C0C18793@GSCMAMP09EX.firmwide.corp.gs.com> Tried. No luck. What is it possible for this to infer beyond normal levels of inference? > -----Original Message----- > From: Brian Goetz [mailto:brian.goetz at oracle.com] > Sent: Wednesday, January 16, 2013 8:55 PM > To: Raab, Donald [Tech] > Cc: 'Joe Bowbeer'; 'lambda-libs-spec-experts at openjdk.java.net'; Weir, John > [Tech]; Motlin, Craig P. [Tech] > Subject: Re: GS Collections Kata w/ JCF Solutions > > Try compiling with -XDuseGraphInference. > > On Jan 16, 2013, at 4:15 PM, Raab, Donald wrote: > > > Great suggestion. I just gave it a try. Max takes a comparator and returns > Optional so you have to add a call to get(). I was able to change the example > to the following: > > > > Map> multimap = this.company.getCustomers() > > .stream() > > .collect(Collectors.groupBy((Customer customer) -> > > customer.getOrders() > > .stream() > > .explode((Stream.Downstream downstream, Order order) -> > { > > downstream.send(order.getLineItems()); > > }) > > .map(LineItem::getValue) > > .max(Comparators.naturalOrder()) > > .get())); > > > > I get an unchecked error on Comparators.naturalOrder() in IntelliJ, but > adding there makes it ugly. > > > > This is the GS Collections version of the kata, which also used max(), but > calls this method on a DoubleIterable: > > > > MutableListMultimap multimap = this.company.getCustomers() > > .groupBy(customer -> customer.getOrders() > > .asLazy() > > .flatCollect(Order::getLineItems) > > .collectDouble(LineItem::getValue) > > .max()); > > > > Unfortunately, the type inference seems to be very successful at thwarting > me trying to simplify the above example using the stream library features. I > would like to simplify down to this but can't seem to get it to work: > > > > Map> multimap = this.company.getCustomers() > > .stream() > > .collect(groupBy((customer) -> > > customer.getOrders() > > .stream() > > .explode((downstream, order) -> { > > downstream.send(order.getLineItems()); > > }) > > .map(LineItem::getValue) > > .max(Comparators.naturalOrder()) > > .get())); > > > > > > From: Joe Bowbeer [mailto:joe.bowbeer at gmail.com] > > Sent: Wednesday, January 16, 2013 4:21 PM > > To: Raab, Donald [Tech] > > Cc: lambda-libs-spec-experts at openjdk.java.net; Motlin, Craig P. [Tech]; > Weir, John [Tech] > > Subject: Re: GS Collections Kata w/ JCF Solutions > > > > Thanks. > > > > > > Would it be better to rewrite the following using Stream.max()? > > > > > > .reduce(0.0, (x, y) -> Math.max(x, y)) > > > > --Joe > > > > From joe.bowbeer at gmail.com Wed Jan 16 18:27:58 2013 From: joe.bowbeer at gmail.com (Joe Bowbeer) Date: Wed, 16 Jan 2013 18:27:58 -0800 Subject: GS Collections Kata w/ JCF Solutions In-Reply-To: <6712820CB52CFB4D842561213A77C05404C0C18793@GSCMAMP09EX.firmwide.corp.gs.com> References: <6712820CB52CFB4D842561213A77C05404C0C18764@GSCMAMP09EX.firmwide.corp.gs.com> <6712820CB52CFB4D842561213A77C05404C0C1878A@GSCMAMP09EX.firmwide.corp.gs.com> <2BAE8A49-B082-4AB2-A053-4BE7FB65D14C@oracle.com> <6712820CB52CFB4D842561213A77C05404C0C18793@GSCMAMP09EX.firmwide.corp.gs.com> Message-ID: So far (b73), I haven't found any uses for -XDuseGraphInference. Most of the times I've tried it, it has thrown an AssertionException. On Wed, Jan 16, 2013 at 6:14 PM, Raab, Donald wrote: > Tried. No luck. > > What is it possible for this to infer beyond normal levels of inference? > > > -----Original Message----- > > From: Brian Goetz [mailto:brian.goetz at oracle.com] > > Sent: Wednesday, January 16, 2013 8:55 PM > > To: Raab, Donald [Tech] > > Cc: 'Joe Bowbeer'; 'lambda-libs-spec-experts at openjdk.java.net'; Weir, > John > > [Tech]; Motlin, Craig P. [Tech] > > Subject: Re: GS Collections Kata w/ JCF Solutions > > > > Try compiling with -XDuseGraphInference. > > > > On Jan 16, 2013, at 4:15 PM, Raab, Donald wrote: > > > > > Great suggestion. I just gave it a try. Max takes a comparator and > returns > > Optional so you have to add a call to get(). I was able to change the > example > > to the following: > > > > > > Map> multimap = > this.company.getCustomers() > > > .stream() > > > .collect(Collectors.groupBy((Customer customer) -> > > > customer.getOrders() > > > .stream() > > > .explode((Stream.Downstream downstream, Order > order) -> > > { > > > downstream.send(order.getLineItems()); > > > }) > > > .map(LineItem::getValue) > > > .max(Comparators.naturalOrder()) > > > .get())); > > > > > > I get an unchecked error on Comparators.naturalOrder() in IntelliJ, but > > adding there makes it ugly. > > > > > > This is the GS Collections version of the kata, which also used max(), > but > > calls this method on a DoubleIterable: > > > > > > MutableListMultimap multimap = > this.company.getCustomers() > > > .groupBy(customer -> customer.getOrders() > > > .asLazy() > > > .flatCollect(Order::getLineItems) > > > .collectDouble(LineItem::getValue) > > > .max()); > > > > > > Unfortunately, the type inference seems to be very successful at > thwarting > > me trying to simplify the above example using the stream library > features. I > > would like to simplify down to this but can't seem to get it to work: > > > > > > Map> multimap = > this.company.getCustomers() > > > .stream() > > > .collect(groupBy((customer) -> > > > customer.getOrders() > > > .stream() > > > .explode((downstream, order) -> { > > > downstream.send(order.getLineItems()); > > > }) > > > .map(LineItem::getValue) > > > .max(Comparators.naturalOrder()) > > > .get())); > > > > > > > > > From: Joe Bowbeer [mailto:joe.bowbeer at gmail.com] > > > Sent: Wednesday, January 16, 2013 4:21 PM > > > To: Raab, Donald [Tech] > > > Cc: lambda-libs-spec-experts at openjdk.java.net; Motlin, Craig P. > [Tech]; > > Weir, John [Tech] > > > Subject: Re: GS Collections Kata w/ JCF Solutions > > > > > > Thanks. > > > > > > > > > Would it be better to rewrite the following using Stream.max()? > > > > > > > > > .reduce(0.0, (x, y) -> Math.max(x, y)) > > > > > > --Joe > > > > > > > > From aruld at acm.org Wed Jan 16 19:19:51 2013 From: aruld at acm.org (Arul Dhesiaseelan) Date: Wed, 16 Jan 2013 17:19:51 -1000 Subject: GS Collections Kata w/ JCF Solutions In-Reply-To: <6712820CB52CFB4D842561213A77C05404C0C1878A@GSCMAMP09EX.firmwide.corp.gs.com> References: <6712820CB52CFB4D842561213A77C05404C0C18764@GSCMAMP09EX.firmwide.corp.gs.com> <6712820CB52CFB4D842561213A77C05404C0C1878A@GSCMAMP09EX.firmwide.corp.gs.com> Message-ID: Try this (works with b73): Map> multimap = this.company.getCustomers() .stream() .collect(Collectors.groupBy((Customer customer) -> customer.getOrders() .stream() .explode((Stream.Downstream downstream, Order order) -> downstream.send(order.getLineItems())) .map(LineItem::getValue) .max(Comparators.naturalOrder()) .get())); I used this in a similar context here [1] -Arul [1] https://github.com/aruld/java-oneliners/blob/master/src/main/java/com/aruld/oneliners/Item10.java On Wed, Jan 16, 2013 at 2:15 PM, Raab, Donald wrote: > Great suggestion. I just gave it a try. Max takes a comparator and > returns Optional so you have to add a call to get(). I was able to change > the example to the following: > > Map> multimap = this.company.getCustomers() > .stream() > .collect(Collectors.groupBy((Customer customer) -> > customer.getOrders() > .stream() > .explode((Stream.Downstream downstream, Order order) > -> { > downstream.send(order.getLineItems()); > }) > .map(LineItem::getValue) > .max(Comparators.naturalOrder()) > .get())); > > I get an unchecked error on Comparators.naturalOrder() in IntelliJ, but > adding there makes it ugly. > > This is the GS Collections version of the kata, which also used max(), but > calls this method on a DoubleIterable: > > MutableListMultimap multimap = > this.company.getCustomers() > .groupBy(customer -> customer.getOrders() > .asLazy() > .flatCollect(Order::getLineItems) > .collectDouble(LineItem::getValue) > .max()); > > Unfortunately, the type inference seems to be very successful at thwarting > me trying to simplify the above example using the stream library features. > I would like to simplify down to this but can't seem to get it to work: > > Map> multimap = this.company.getCustomers() > .stream() > .collect(groupBy((customer) -> > customer.getOrders() > .stream() > .explode((downstream, order) -> { > downstream.send(order.getLineItems()); > }) > .map(LineItem::getValue) > .max(Comparators.naturalOrder()) > .get())); > > > From: Joe Bowbeer [mailto:joe.bowbeer at gmail.com] > Sent: Wednesday, January 16, 2013 4:21 PM > To: Raab, Donald [Tech] > Cc: lambda-libs-spec-experts at openjdk.java.net; Motlin, Craig P. [Tech]; > Weir, John [Tech] > Subject: Re: GS Collections Kata w/ JCF Solutions > > Thanks. > > > Would it be better to rewrite the following using Stream.max()? > > > .reduce(0.0, (x, y) -> Math.max(x, y)) > > --Joe > > > From paul.sandoz at oracle.com Thu Jan 17 09:09:12 2013 From: paul.sandoz at oracle.com (Paul Sandoz) Date: Thu, 17 Jan 2013 18:09:12 +0100 Subject: FYI: LongStream and DoubleStream have been pushed Message-ID: <7A6FD5F1-8FFD-40AA-8ECA-5D75F6DBFBEE@oracle.com> Hi, Just an FYI that i have pushed implementations of LongStream and DoubleStream. http://hg.openjdk.java.net/lambda/lambda/jdk/rev/27f8ad910a32 It's currently only lightly tested, i now need to flesh out the test harness for long and double specific support. Paul. From kevinb at google.com Fri Jan 18 10:58:52 2013 From: kevinb at google.com (Kevin Bourrillion) Date: Fri, 18 Jan 2013 10:58:52 -0800 Subject: Let's please rename Block to Receiver before it's too late Message-ID: When I see methods like doSomething(IntBlock intBlock); doSomethingElse(Block stringBlock); ... I can't even guess what these things mean. What is a "block of string" or an "int block"? If forced to guess, I'd say "well, this clearly has nothing to do with a 'block' in the Java language, but that's my best analogy anyway, and blocks have no well-defined inputs or outputs, but that int/string has to get involved somehow, so..... if that whole block somehow represents an Int in some way it must be that the whole thing *evaluates* to an Int... except wait, there's also IntSupplier.... wtf?" Procedure has similar problems to maybe half the same degree. But then consider this: doSomething(IntReceiver receiver); doSomethingElse(Receiver receiver); How much clearer could anything be? It's an int receiver: it receives ints! Bonus: it has a much clearer relationship to Supplier. I have scoured the threads to find what the problems are that people had with Receiver, and I haven't found any. Privately Brian guessed the problem could be "confusion with receiver in the sense of method receiver?" But that's not even a term 95% of normal Java developers know or use. And even if so, the meaning of "an int receiver" is so clear the mind doesn't even * go* there. Agree/disagree/neutral? -- Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com From spullara at gmail.com Fri Jan 18 11:56:57 2013 From: spullara at gmail.com (Sam Pullara) Date: Fri, 18 Jan 2013 11:56:57 -0800 Subject: Let's please rename Block to Receiver before it's too late In-Reply-To: References: Message-ID: Seems reasonable to me. Other options: Handler Taker Consumer I only offer them up because Receiver sounds likes events or message passing to me. Sam On Jan 18, 2013, at 10:58 AM, Kevin Bourrillion wrote: > When I see methods like > > doSomething(IntBlock intBlock); > doSomethingElse(Block stringBlock); > > ... I can't even guess what these things mean. What is a "block of string" or an "int block"? If forced to guess, I'd say "well, this clearly has nothing to do with a 'block' in the Java language, but that's my best analogy anyway, and blocks have no well-defined inputs or outputs, but that int/string has to get involved somehow, so..... if that whole block somehow represents an Int in some way it must be that the whole thing evaluates to an Int... except wait, there's also IntSupplier.... wtf?" > > Procedure has similar problems to maybe half the same degree. > > But then consider this: > > doSomething(IntReceiver receiver); > doSomethingElse(Receiver receiver); > > How much clearer could anything be? It's an int receiver: it receives ints! Bonus: it has a much clearer relationship to Supplier. > > I have scoured the threads to find what the problems are that people had with Receiver, and I haven't found any. Privately Brian guessed the problem could be "confusion with receiver in the sense of method receiver?" But that's not even a term 95% of normal Java developers know or use. And even if so, the meaning of "an int receiver" is so clear the mind doesn't even go there. > > Agree/disagree/neutral? > > -- > Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com From dl at cs.oswego.edu Fri Jan 18 12:57:10 2013 From: dl at cs.oswego.edu (Doug Lea) Date: Fri, 18 Jan 2013 15:57:10 -0500 Subject: Let's please rename Block to Receiver before it's too late In-Reply-To: References: Message-ID: <50F9B726.1020904@cs.oswego.edu> I've never much liked Block. (Maybe only ex-smalltalkers actually prefer it?) I hadn't thought about it much lately after we rejected the obviously (to me) best choice of "Action" (because Swing took that name) and second choice Procedure. Having typed "block" hundreds of times in the past few weeks, I'm finally getting to the point of not being overly distracted by it though. Receiver (with method "receive") seems a little less jarring, but still a little jarring to me. So I guess I abstain. BTW, "too late" is already on its way. Because of overload confusability problems, people (including me in CompletableFuture) are already propagating the Block method name "accept" one level out to help prevent usage ambiguities ("thenAccept" vs "thenApply"). -Doug On 01/18/13 13:58, Kevin Bourrillion wrote: > When I see methods like > > doSomething(IntBlock intBlock); > doSomethingElse(Block stringBlock); > > ... I can't even guess what these things mean. What is a "block of string" or > an "int block"? If forced to guess, I'd say "well, this clearly has nothing to > do with a 'block' in the Java language, but that's my best analogy anyway, and > blocks have no well-defined inputs or outputs, but that int/string has to get > involved somehow, so..... if that whole block somehow represents an Int in some > way it must be that the whole thing /evaluates/ to an Int... except wait, > there's also IntSupplier.... wtf?" > > Procedure has similar problems to maybe half the same degree. > > But then consider this: > > doSomething(IntReceiver receiver); > doSomethingElse(Receiver receiver); > > How much clearer could anything be? It's an int receiver: it receives ints! > Bonus: it has a much clearer relationship to Supplier. > > I have scoured the threads to find what the problems are that people had with > Receiver, and I haven't found any. Privately Brian guessed the problem could be > "confusion with receiver in the sense of method receiver?" But that's not even > a term 95% of normal Java developers know or use. And even if so, the meaning > of "an int receiver" is so clear the mind doesn't even /go/ there. > > Agree/disagree/neutral? > > -- > Kevin Bourrillion | Java Librarian | Google, Inc. |kevinb at google.com > From Donald.Raab at gs.com Fri Jan 18 14:08:57 2013 From: Donald.Raab at gs.com (Raab, Donald) Date: Fri, 18 Jan 2013 17:08:57 -0500 Subject: Let's please rename Block to Receiver before it's too late In-Reply-To: <50F9B726.1020904@cs.oswego.edu> References: <50F9B726.1020904@cs.oswego.edu> Message-ID: <6712820CB52CFB4D842561213A77C05404C0C188F8@GSCMAMP09EX.firmwide.corp.gs.com> This ex-Smalltalker still prefers Procedure. I also prefer either spelling things out completely (Binary vs. Bi), or going to the other extreme (suffix with 1, 2, 3, 4...). Please can we rename collect as well. Here's an example of collect being used with GS Collections in our Kata. MutableList customers = this.company.getCustomers(); Set customerNamesSet = customers.collect(Customer::getName).stream().collect(Collectors.toSet()); This will be very confusing for developers who use our library. I have been mostly fine up till now on the method name choices, as there has been only a few overlaps with names in our space (e.g. forEach, sort, groupBy). At least with these names the meaning has been more or less the same. I usually teach people that collect=map/transform, select=filter, groupBy=groupBy, etc. Collect!=collect is not something I would look forward to explaining. > -----Original Message----- > From: lambda-libs-spec-experts-bounces at openjdk.java.net [mailto:lambda- > libs-spec-experts-bounces at openjdk.java.net] On Behalf Of Doug Lea > Sent: Friday, January 18, 2013 3:57 PM > To: lambda-libs-spec-experts at openjdk.java.net > Subject: Re: Let's please rename Block to Receiver before it's too late > > > I've never much liked Block. (Maybe only ex-smalltalkers actually prefer > it?) I hadn't thought about it much lately after we rejected the obviously > (to me) best choice of "Action" (because Swing took that name) and second > choice Procedure. > Having typed "block" hundreds of times in the past few weeks, I'm finally > getting to the point of not being overly distracted by it though. > From tim at peierls.net Fri Jan 18 14:26:05 2013 From: tim at peierls.net (Tim Peierls) Date: Fri, 18 Jan 2013 17:26:05 -0500 Subject: Let's please rename Block to Receiver before it's too late In-Reply-To: <6712820CB52CFB4D842561213A77C05404C0C188F8@GSCMAMP09EX.firmwide.corp.gs.com> References: <50F9B726.1020904@cs.oswego.edu> <6712820CB52CFB4D842561213A77C05404C0C188F8@GSCMAMP09EX.firmwide.corp.gs.com> Message-ID: On Fri, Jan 18, 2013 at 5:08 PM, Raab, Donald wrote: > This ex-Smalltalker still prefers Procedure. > And this non-Smalltalker prefers Procedure, Receiver, Handler ... pretty much anything except Block, which is an inert chunk of wood to me. Please can we rename collect as well. Whoa, steady -- I *like* collect. I see the overlap issue, but that really can't be allowed to stand in the way of a good name. What were you proposing instead? --tim From josh at bloch.us Fri Jan 18 14:42:59 2013 From: josh at bloch.us (Joshua Bloch) Date: Fri, 18 Jan 2013 14:42:59 -0800 Subject: Let's please rename Block to Receiver before it's too late In-Reply-To: References: <50F9B726.1020904@cs.oswego.edu> <6712820CB52CFB4D842561213A77C05404C0C188F8@GSCMAMP09EX.firmwide.corp.gs.com> Message-ID: I like Procedure too. Josh On Fri, Jan 18, 2013 at 2:26 PM, Tim Peierls wrote: > On Fri, Jan 18, 2013 at 5:08 PM, Raab, Donald wrote: > >> This ex-Smalltalker still prefers Procedure. >> > > And this non-Smalltalker prefers Procedure, Receiver, Handler ... pretty > much anything except Block, which is an inert chunk of wood to me. > > > Please can we rename collect as well. > > > Whoa, steady -- I *like* collect. I see the overlap issue, but that really > can't be allowed to stand in the way of a good name. What were you > proposing instead? > > --tim > From spullara at gmail.com Fri Jan 18 14:47:25 2013 From: spullara at gmail.com (Sam Pullara) Date: Fri, 18 Jan 2013 14:47:25 -0800 Subject: Let's please rename Block to Receiver before it's too late In-Reply-To: References: <50F9B726.1020904@cs.oswego.edu> <6712820CB52CFB4D842561213A77C05404C0C188F8@GSCMAMP09EX.firmwide.corp.gs.com> Message-ID: <-6070796713685253096@unknownmsgid> Fine with me. Sam All my photos are panoramas. On Jan 18, 2013, at 2:43 PM, Joshua Bloch wrote: I like Procedure too. Josh On Fri, Jan 18, 2013 at 2:26 PM, Tim Peierls wrote: > On Fri, Jan 18, 2013 at 5:08 PM, Raab, Donald wrote: > >> This ex-Smalltalker still prefers Procedure. >> > > And this non-Smalltalker prefers Procedure, Receiver, Handler ... pretty > much anything except Block, which is an inert chunk of wood to me. > > > Please can we rename collect as well. > > > Whoa, steady -- I *like* collect. I see the overlap issue, but that really > can't be allowed to stand in the way of a good name. What were you > proposing instead? > > --tim > From joe.bowbeer at gmail.com Fri Jan 18 14:56:32 2013 From: joe.bowbeer at gmail.com (Joe Bowbeer) Date: Fri, 18 Jan 2013 14:56:32 -0800 Subject: Let's please rename Block to Receiver before it's too late In-Reply-To: References: Message-ID: I prefer Procedure to Receiver, but I'm getting used to Block despite my initial exasperation. At the moment, I prefer Block to Receiver. Procedure > Block > Receiver Receiver is commonly used in Android (BroadcastReceiver) and is a common name in message and event frameworks, as is Handler, so I consider them both out-of-bounds for use here. Joe On Fri, Jan 18, 2013 at 10:58 AM, Kevin Bourrillion wrote: > When I see methods like > > doSomething(IntBlock intBlock); > doSomethingElse(Block stringBlock); > > ... I can't even guess what these things mean. What is a "block of > string" or an "int block"? If forced to guess, I'd say "well, this clearly > has nothing to do with a 'block' in the Java language, but that's my best > analogy anyway, and blocks have no well-defined inputs or outputs, but that > int/string has to get involved somehow, so..... if that whole block somehow > represents an Int in some way it must be that the whole thing *evaluates* to > an Int... except wait, there's also IntSupplier.... wtf?" > > Procedure has similar problems to maybe half the same degree. > > But then consider this: > > doSomething(IntReceiver receiver); > doSomethingElse(Receiver receiver); > > How much clearer could anything be? It's an int receiver: it receives > ints! Bonus: it has a much clearer relationship to Supplier. > > I have scoured the threads to find what the problems are that people had > with Receiver, and I haven't found any. Privately Brian guessed the problem > could be "confusion with receiver in the sense of method receiver?" But > that's not even a term 95% of normal Java developers know or use. And even > if so, the meaning of "an int receiver" is so clear the mind doesn't even > *go* there. > > Agree/disagree/neutral? > > -- > Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com > From kevinb at google.com Fri Jan 18 15:28:52 2013 From: kevinb at google.com (Kevin Bourrillion) Date: Fri, 18 Jan 2013 15:28:52 -0800 Subject: Let's please rename Block to Receiver before it's too late In-Reply-To: References: Message-ID: On Fri, Jan 18, 2013 at 2:56 PM, Joe Bowbeer wrote: I prefer Procedure to Receiver, but I'm getting used to Block despite my > initial exasperation. At the moment, I prefer Block to Receiver. > > Procedure > Block > Receiver > > Receiver is commonly used in Android (BroadcastReceiver) and is a common > name in message and event frameworks, as is Handler, so I consider them > both out-of-bounds for use here. > Finally we have what seems to be the first concrete complaint about Receiver here. Thank you. So we can list that as a strike against it. I see no reason that makes it an automatic DQ. Procedure seems to have a lot of support, but again, very little concrete explanation of *why*. If it's "that's what X calls it," that's worth * something*, but how *much* it's worth is at least partly dependent on just how many Java programmers today actually know anything about X. As I've expressed, I see tremendous value in this name ("IntReceiver" or "IntConsumer", etc.) painting* that* clear and obvious a picture of what the thing is. Just as IntSupplier does. It seems that others here really just don't see that value? If that's so, then okay, I'm done here. Just surprised. -- Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com From brian.goetz at oracle.com Fri Jan 18 18:36:35 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 18 Jan 2013 18:36:35 -0800 Subject: Let's please rename Block to Receiver before it's too late In-Reply-To: References: Message-ID: <3E805E80-6E9F-4D97-8E62-B30339C16CED@oracle.com> OK, OK, if people really, really hate Block, I would still consider Sink. Sink is the opposite of Source. We could rename Supplier to Source, and rename Block to Sink. Leaving the method names the same (accept and get.) I think this is better than the status quo. On Jan 18, 2013, at 10:58 AM, Kevin Bourrillion wrote: > When I see methods like > > doSomething(IntBlock intBlock); > doSomethingElse(Block stringBlock); > > ... I can't even guess what these things mean. What is a "block of string" or an "int block"? If forced to guess, I'd say "well, this clearly has nothing to do with a 'block' in the Java language, but that's my best analogy anyway, and blocks have no well-defined inputs or outputs, but that int/string has to get involved somehow, so..... if that whole block somehow represents an Int in some way it must be that the whole thing evaluates to an Int... except wait, there's also IntSupplier.... wtf?" > > Procedure has similar problems to maybe half the same degree. > > But then consider this: > > doSomething(IntReceiver receiver); > doSomethingElse(Receiver receiver); > > How much clearer could anything be? It's an int receiver: it receives ints! Bonus: it has a much clearer relationship to Supplier. > > I have scoured the threads to find what the problems are that people had with Receiver, and I haven't found any. Privately Brian guessed the problem could be "confusion with receiver in the sense of method receiver?" But that's not even a term 95% of normal Java developers know or use. And even if so, the meaning of "an int receiver" is so clear the mind doesn't even go there. > > Agree/disagree/neutral? > > -- > Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com From jfinn at boxoftricks.org Sat Jan 19 04:43:38 2013 From: jfinn at boxoftricks.org (Jonathan Finn) Date: Sat, 19 Jan 2013 12:43:38 +0000 Subject: Let's please rename Block to Receiver before it's too late In-Reply-To: <3E805E80-6E9F-4D97-8E62-B30339C16CED@oracle.com> References: <3E805E80-6E9F-4D97-8E62-B30339C16CED@oracle.com> Message-ID: <67C8FBBC-C163-4477-B559-F21772349914@boxoftricks.org> Names like Receiver, Sink etc. sound too passive to me, given that a Block has to actually do something with the parameter: store it, draw it etc. Handler, Action, Procedure are more active. I'm still gunning for Action because it echoes Function (which is nice), it's active and it's shorter than Procedure. I don't see a big problem with Swing's Action because AWT's List hasn't been a problem. Jonathan On 19 Jan 2013, at 02:36, Brian Goetz wrote: > OK, OK, if people really, really hate Block, I would still consider Sink. Sink is the opposite of Source. We could rename Supplier to Source, and rename Block to Sink. Leaving the method names the same (accept and get.) > > I think this is better than the status quo. > > > > On Jan 18, 2013, at 10:58 AM, Kevin Bourrillion wrote: > >> When I see methods like >> >> doSomething(IntBlock intBlock); >> doSomethingElse(Block stringBlock); >> >> ... I can't even guess what these things mean. What is a "block of string" or an "int block"? If forced to guess, I'd say "well, this clearly has nothing to do with a 'block' in the Java language, but that's my best analogy anyway, and blocks have no well-defined inputs or outputs, but that int/string has to get involved somehow, so..... if that whole block somehow represents an Int in some way it must be that the whole thing evaluates to an Int... except wait, there's also IntSupplier.... wtf?" >> >> Procedure has similar problems to maybe half the same degree. >> >> But then consider this: >> >> doSomething(IntReceiver receiver); >> doSomethingElse(Receiver receiver); >> >> How much clearer could anything be? It's an int receiver: it receives ints! Bonus: it has a much clearer relationship to Supplier. >> >> I have scoured the threads to find what the problems are that people had with Receiver, and I haven't found any. Privately Brian guessed the problem could be "confusion with receiver in the sense of method receiver?" But that's not even a term 95% of normal Java developers know or use. And even if so, the meaning of "an int receiver" is so clear the mind doesn't even go there. >> >> Agree/disagree/neutral? >> >> -- >> Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com > Jonathan Finn jfinn at boxoftricks.org From dl at cs.oswego.edu Sat Jan 19 05:19:26 2013 From: dl at cs.oswego.edu (Doug Lea) Date: Sat, 19 Jan 2013 08:19:26 -0500 Subject: Let's please rename Block to Receiver before it's too late In-Reply-To: <3E805E80-6E9F-4D97-8E62-B30339C16CED@oracle.com> References: <3E805E80-6E9F-4D97-8E62-B30339C16CED@oracle.com> Message-ID: <50FA9D5E.1030000@cs.oswego.edu> On 01/18/13 21:36, Brian Goetz wrote: > OK, OK, if people really, really hate Block, I would still consider Sink. > Sink is the opposite of Source. We could rename Supplier to Source, and > rename Block to Sink. Leaving the method names the same (accept and get.) > "Sink" is nice in the Stream framework, which is a plus in encouraging people to use it, but not in other common contexts where the main property to convey is that it is a possibly side-effecting action, as opposed to a Function. Does everyone else prefer "Action" as best possible name? If so, would we be willing to reconsider rejecting it because of Swing conflicts? The interactions do not seem to be all that bad: Swing programmers would never encounter problems as pure lambda-users (they need not import java.util.functions.Action or otherwise use the type.) So the only ones impacted are those who write lambda-accepting Swing methods, which do not yet exist. And to help a bit further, we could keep the method name as "accept", to reduce conceptual conflict with the conventions surrounding "perform" in Swing. interface Action { void accept(T t); } -Doug From mcnepp02 at googlemail.com Sat Jan 19 05:22:53 2013 From: mcnepp02 at googlemail.com (Gernot Neppert) Date: Sat, 19 Jan 2013 14:22:53 +0100 Subject: Let's please rename Block to Receiver before it's too late In-Reply-To: <3E805E80-6E9F-4D97-8E62-B30339C16CED@oracle.com> References: <3E805E80-6E9F-4D97-8E62-B30339C16CED@oracle.com> Message-ID: <50FA9E2D.9020504@googlemail.com> Am 19.01.2013 03:36, schrieb Brian Goetz: > OK, OK, if people really, really hate Block, I would still consider Sink. Sink is the opposite of Source. We could rename Supplier to Source, and rename Block to Sink. Leaving the method names the same (accept and get.) > > I think this is better than the status quo. Thumbs up for "Source" and "Sink". I was just about to propose it when I read your posting ;-) From jfinn at boxoftricks.org Sat Jan 19 05:29:25 2013 From: jfinn at boxoftricks.org (Jonathan Finn) Date: Sat, 19 Jan 2013 13:29:25 +0000 Subject: Let's please rename Block to Receiver before it's too late In-Reply-To: <50FA9D5E.1030000@cs.oswego.edu> References: <3E805E80-6E9F-4D97-8E62-B30339C16CED@oracle.com> <50FA9D5E.1030000@cs.oswego.edu> Message-ID: <211B12EB-E68C-41BD-8D68-01E6640C828D@boxoftricks.org> > And to help a bit further, we could keep the method > name as "accept", to reduce conceptual conflict with the > conventions surrounding "perform" in Swing. > > interface Action { void accept(T t); } > I was going to suggest one of: interface Action { void actOn(T t); } interface Action { void act(T t); } interface Action { void action(T t); } // here 'action' is a verb. Jonathan On 19 Jan 2013, at 13:19, Doug Lea
wrote: > On 01/18/13 21:36, Brian Goetz wrote: >> OK, OK, if people really, really hate Block, I would still consider Sink. >> Sink is the opposite of Source. We could rename Supplier to Source, and >> rename Block to Sink. Leaving the method names the same (accept and get.) >> > > "Sink" is nice in the Stream framework, which is a plus in > encouraging people to use it, but not in other common > contexts where the main property to convey is that it is a > possibly side-effecting action, as opposed to a Function. > > Does everyone else prefer "Action" as best possible name? > If so, would we be willing to reconsider rejecting it because > of Swing conflicts? The interactions do not seem to > be all that bad: Swing programmers would never encounter > problems as pure lambda-users (they need not import > java.util.functions.Action or otherwise use the type.) > So the only ones impacted are those who write > lambda-accepting Swing methods, which do not yet exist. > > And to help a bit further, we could keep the method > name as "accept", to reduce conceptual conflict with the > conventions surrounding "perform" in Swing. > > interface Action { void accept(T t); } > > -Doug > Jonathan Finn jfinn at boxoftricks.org From forax at univ-mlv.fr Sat Jan 19 06:43:34 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Sat, 19 Jan 2013 15:43:34 +0100 Subject: Let's please rename Block to Receiver before it's too late In-Reply-To: <50FA9D5E.1030000@cs.oswego.edu> References: <3E805E80-6E9F-4D97-8E62-B30339C16CED@oracle.com> <50FA9D5E.1030000@cs.oswego.edu> Message-ID: <50FAB116.9020908@univ-mlv.fr> On 01/19/2013 02:19 PM, Doug Lea wrote: > On 01/18/13 21:36, Brian Goetz wrote: >> OK, OK, if people really, really hate Block, I would still consider >> Sink. >> Sink is the opposite of Source. We could rename Supplier to >> Source, and >> rename Block to Sink. Leaving the method names the same (accept and >> get.) >> > > "Sink" is nice in the Stream framework, which is a plus in > encouraging people to use it, but not in other common > contexts where the main property to convey is that it is a > possibly side-effecting action, as opposed to a Function. > > Does everyone else prefer "Action" as best possible name? > If so, would we be willing to reconsider rejecting it because > of Swing conflicts? The interactions do not seem to > be all that bad: Swing programmers would never encounter > problems as pure lambda-users (they need not import > java.util.functions.Action or otherwise use the type.) > So the only ones impacted are those who write > lambda-accepting Swing methods, which do not yet exist. > > And to help a bit further, we could keep the method > name as "accept", to reduce conceptual conflict with the > conventions surrounding "perform" in Swing. > > interface Action { void accept(T t); } Writing event broadcaster (listener support in swing slang) is a place where you want to mix streams and swing actions. listeners.stream().filter(l -> l instanceof XXXListener).forEach(l -> l.fireEvent(...)); AWT also claims the name Action for something unrelated (desktop default behaviours). BTW, Block should be renamed to DoIter (Do-it-er) in my opinion ... :) sorry, could not resist ... > > -Doug > R?mi From tim at peierls.net Sat Jan 19 06:58:34 2013 From: tim at peierls.net (Tim Peierls) Date: Sat, 19 Jan 2013 09:58:34 -0500 Subject: Let's please rename Block to Receiver before it's too late In-Reply-To: <50FA9D5E.1030000@cs.oswego.edu> References: <3E805E80-6E9F-4D97-8E62-B30339C16CED@oracle.com> <50FA9D5E.1030000@cs.oswego.edu> Message-ID: On Sat, Jan 19, 2013 at 8:19 AM, Doug Lea
wrote: > On 01/18/13 21:36, Brian Goetz wrote: > >> OK, OK, if people really, really hate Block, I would still consider Sink. >> Sink is the opposite of Source. We could rename Supplier to Source, and >> rename Block to Sink. Leaving the method names the same (accept and get.) >> >> > "Sink" is nice in the Stream framework, which is a plus in > encouraging people to use it, but not in other common > contexts where the main property to convey is that it is a > possibly side-effecting action, as opposed to a Function. > > Does everyone else prefer "Action" as best possible name? > I mildly prefer Sink to Action. For one thing, IntSink sounds like a place that ints end up in, and with IntAction it's less obvious what the Int-ness means to the Action. I agree that "sink" doesn't emphasize the side-effect aspect, but does it really need to? What other use could a sink have? Whereas "action" doesn't carry any sense of "consuming" or "accepting". I would (again, *mildly*) prefer Receiver, Consumer, Acceptor, Handler, etc., but only a little more than Source/Sink. But I'm so happy to have moved off Block that any one of these would be fine. --tim From dl at cs.oswego.edu Sat Jan 19 06:57:00 2013 From: dl at cs.oswego.edu (Doug Lea) Date: Sat, 19 Jan 2013 09:57:00 -0500 Subject: Let's please rename Block to Receiver before it's too late In-Reply-To: <50FAB116.9020908@univ-mlv.fr> References: <3E805E80-6E9F-4D97-8E62-B30339C16CED@oracle.com> <50FA9D5E.1030000@cs.oswego.edu> <50FAB116.9020908@univ-mlv.fr> Message-ID: <50FAB43C.6000902@cs.oswego.edu> On 01/19/13 09:43, Remi Forax wrote: >> interface Action { void accept(T t); } > > Writing event broadcaster (listener support in swing slang) is a place where you > want to mix streams and swing actions. > listeners.stream().filter(l -> l instanceof XXXListener).forEach(l -> > l.fireEvent(...)); People can do this without ever importing or using j.u.f.Action. The only people affected are those writing new methods with j.u.f.Action vs javax.swing.Action arguments. Still concerned? -Doug From dl at cs.oswego.edu Sat Jan 19 07:10:10 2013 From: dl at cs.oswego.edu (Doug Lea) Date: Sat, 19 Jan 2013 10:10:10 -0500 Subject: Let's please rename Block to Receiver before it's too late In-Reply-To: References: <3E805E80-6E9F-4D97-8E62-B30339C16CED@oracle.com> <50FA9D5E.1030000@cs.oswego.edu> Message-ID: <50FAB752.1080905@cs.oswego.edu> On 01/19/13 09:58, Tim Peierls wrote: > I agree that "sink" doesn't emphasize the side-effect aspect, but does it really > need to? What other use could a sink have? Whereas "action" doesn't carry any > sense of "consuming" or "accepting". Right. That's why it is nice in streams, but not in cases where the connotation of consuming is just plain wrong -- the argument is just "used" not "consumed". a.accept(x); b.accept(x); doSomethingElseWith(x); My reason for preferring Action (or even Block!) to Sink is that the only commonality is potential (and extremely likely) side-effecting-ness. (What would you name a void action of two arguments? There are a lot of these now.) -Doug From kevinb at google.com Sat Jan 19 07:17:23 2013 From: kevinb at google.com (Kevin Bourrillion) Date: Sat, 19 Jan 2013 07:17:23 -0800 Subject: Let's please rename Block to Receiver before it's too late In-Reply-To: <50FAB752.1080905@cs.oswego.edu> References: <3E805E80-6E9F-4D97-8E62-B30339C16CED@oracle.com> <50FA9D5E.1030000@cs.oswego.edu> <50FAB752.1080905@cs.oswego.edu> Message-ID: Source IntSource DoubleSource Sink IntSink DoubleSink BiSource ObjIntBiSource BiSink ObjIntBiSink I think this is as good as we're going to get and, with Receiver failing to catch on, I'd like to see if we can converge on Source/Sink now; who's in? On Sat, Jan 19, 2013 at 7:10 AM, Doug Lea
wrote: > On 01/19/13 09:58, Tim Peierls wrote: > > I agree that "sink" doesn't emphasize the side-effect aspect, but does it >> really >> need to? What other use could a sink have? Whereas "action" doesn't carry >> any >> sense of "consuming" or "accepting". >> > > Right. That's why it is nice in streams, but not in cases > where the connotation of consuming is just plain wrong -- > the argument is just "used" not "consumed". > > a.accept(x); b.accept(x); doSomethingElseWith(x); > > My reason for preferring Action (or even Block!) to Sink > is that the only commonality is potential (and extremely > likely) side-effecting-ness. > > (What would you name a void action of two arguments? > There are a lot of these now.) > > -Doug > > > > > -- Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com From tim at peierls.net Sat Jan 19 07:19:12 2013 From: tim at peierls.net (Tim Peierls) Date: Sat, 19 Jan 2013 10:19:12 -0500 Subject: Let's please rename Block to Receiver before it's too late In-Reply-To: References: <3E805E80-6E9F-4D97-8E62-B30339C16CED@oracle.com> <50FA9D5E.1030000@cs.oswego.edu> <50FAB752.1080905@cs.oswego.edu> Message-ID: I don't know what BiSource means, but the rest seems reasonable. On Sat, Jan 19, 2013 at 10:17 AM, Kevin Bourrillion wrote: > Source > IntSource > DoubleSource > > Sink > IntSink > DoubleSink > > BiSource > ObjIntBiSource > BiSink > ObjIntBiSink > > I think this is as good as we're going to get and, with Receiver failing > to catch on, I'd like to see if we can converge on Source/Sink now; who's > in? > > > On Sat, Jan 19, 2013 at 7:10 AM, Doug Lea
wrote: > >> On 01/19/13 09:58, Tim Peierls wrote: >> >> I agree that "sink" doesn't emphasize the side-effect aspect, but does >>> it really >>> need to? What other use could a sink have? Whereas "action" doesn't >>> carry any >>> sense of "consuming" or "accepting". >>> >> >> Right. That's why it is nice in streams, but not in cases >> where the connotation of consuming is just plain wrong -- >> the argument is just "used" not "consumed". >> >> a.accept(x); b.accept(x); doSomethingElseWith(x); >> >> My reason for preferring Action (or even Block!) to Sink >> is that the only commonality is potential (and extremely >> likely) side-effecting-ness. >> >> (What would you name a void action of two arguments? >> There are a lot of these now.) >> >> -Doug >> >> >> >> >> > > > -- > Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com > From kevinb at google.com Sat Jan 19 07:24:04 2013 From: kevinb at google.com (Kevin Bourrillion) Date: Sat, 19 Jan 2013 07:24:04 -0800 Subject: Let's please rename Block to Receiver before it's too late In-Reply-To: References: <3E805E80-6E9F-4D97-8E62-B30339C16CED@oracle.com> <50FA9D5E.1030000@cs.oswego.edu> <50FAB752.1080905@cs.oswego.edu> Message-ID: Well, now that's an interesting point. I got caught up in the excitement of BiEverything. On Sat, Jan 19, 2013 at 7:19 AM, Tim Peierls wrote: > I don't know what BiSource means, but the rest seems reasonable. > > > On Sat, Jan 19, 2013 at 10:17 AM, Kevin Bourrillion wrote: > >> Source >> IntSource >> DoubleSource >> >> Sink >> IntSink >> DoubleSink >> >> BiSource >> ObjIntBiSource >> BiSink >> ObjIntBiSink >> >> I think this is as good as we're going to get and, with Receiver failing >> to catch on, I'd like to see if we can converge on Source/Sink now; who's >> in? >> >> >> On Sat, Jan 19, 2013 at 7:10 AM, Doug Lea
wrote: >> >>> On 01/19/13 09:58, Tim Peierls wrote: >>> >>> I agree that "sink" doesn't emphasize the side-effect aspect, but does >>>> it really >>>> need to? What other use could a sink have? Whereas "action" doesn't >>>> carry any >>>> sense of "consuming" or "accepting". >>>> >>> >>> Right. That's why it is nice in streams, but not in cases >>> where the connotation of consuming is just plain wrong -- >>> the argument is just "used" not "consumed". >>> >>> a.accept(x); b.accept(x); doSomethingElseWith(x); >>> >>> My reason for preferring Action (or even Block!) to Sink >>> is that the only commonality is potential (and extremely >>> likely) side-effecting-ness. >>> >>> (What would you name a void action of two arguments? >>> There are a lot of these now.) >>> >>> -Doug >>> >>> >>> >>> >>> >> >> >> -- >> Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com >> > > -- Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com From tim at peierls.net Sat Jan 19 08:06:06 2013 From: tim at peierls.net (Tim Peierls) Date: Sat, 19 Jan 2013 11:06:06 -0500 Subject: Let's please rename Block to Receiver before it's too late In-Reply-To: References: <3E805E80-6E9F-4D97-8E62-B30339C16CED@oracle.com> <50FA9D5E.1030000@cs.oswego.edu> <50FAB752.1080905@cs.oswego.edu> Message-ID: > > On Sat, Jan 19, 2013 at 7:19 AM, Tim Peierls wrote: > >> I don't know what BiSource means, but the rest seems reasonable. > > BiSink actually appeals to me. I picture a double bowl kitchen sink, with one bowl (having a disposal unit) for food prep and scraps, the other for washing: BiSink. I know: The metaphor is weak because kitchen sinks don't have to accept a unit of food and a unit of waste together. On Sat, Jan 19, 2013 at 10:24 AM, Kevin Bourrillion wrote: > Well, now that's an interesting point. I got caught up in the excitement > of BiEverything. And hey, who wouldn't? :-) --tim From forax at univ-mlv.fr Sat Jan 19 08:11:54 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Sat, 19 Jan 2013 17:11:54 +0100 Subject: Let's please rename Block to Receiver before it's too late In-Reply-To: <50FAB43C.6000902@cs.oswego.edu> References: <3E805E80-6E9F-4D97-8E62-B30339C16CED@oracle.com> <50FA9D5E.1030000@cs.oswego.edu> <50FAB116.9020908@univ-mlv.fr> <50FAB43C.6000902@cs.oswego.edu> Message-ID: <50FAC5CA.2040409@univ-mlv.fr> On 01/19/2013 03:57 PM, Doug Lea wrote: > On 01/19/13 09:43, Remi Forax wrote: > >>> interface Action { void accept(T t); } >> >> Writing event broadcaster (listener support in swing slang) is a >> place where you >> want to mix streams and swing actions. >> listeners.stream().filter(l -> l instanceof XXXListener).forEach(l -> >> l.fireEvent(...)); > > People can do this without ever importing or using j.u.f.Action. > The only people affected are those writing new methods with > j.u.f.Action vs javax.swing.Action arguments. Still concerned? the issues that stay are when users want to understand error message from the compiler (I don"t know if the compiler will print the j.u.f.Action when full diagnostic message will be turn of ?) And users that want to use a lambda as an action (AbstractAction is abstract so you need to use delegation). jx.s.Action createAction(j.u.f.Action action) { return new AbstractAction() { public void accept(ActionEvent event) { return action.accept(event); } }; } but this one can be solved by adding this method in swing. > > -Doug > > R?mi From joe.bowbeer at gmail.com Sat Jan 19 08:20:48 2013 From: joe.bowbeer at gmail.com (Joe Bowbeer) Date: Sat, 19 Jan 2013 08:20:48 -0800 Subject: Let's please rename Block to Receiver before it's too late In-Reply-To: <50FA9D5E.1030000@cs.oswego.edu> References: <3E805E80-6E9F-4D97-8E62-B30339C16CED@oracle.com> <50FA9D5E.1030000@cs.oswego.edu> Message-ID: Action is too common in UI frameworks, including Android. And it is not parameterized. It is pretty far down on my list, below Block. Block is now neutral for me. Procedure is slightly positive. Everything else so far is below Block. I agree with your criticism of Sink. I would not expect forEach to accept a Sink. This seems like an awkward mix of terms. On Jan 19, 2013 5:20 AM, "Doug Lea"
wrote: > On 01/18/13 21:36, Brian Goetz wrote: > >> OK, OK, if people really, really hate Block, I would still consider Sink. >> Sink is the opposite of Source. We could rename Supplier to Source, and >> rename Block to Sink. Leaving the method names the same (accept and get.) >> >> > "Sink" is nice in the Stream framework, which is a plus in > encouraging people to use it, but not in other common > contexts where the main property to convey is that it is a > possibly side-effecting action, as opposed to a Function. > > Does everyone else prefer "Action" as best possible name? > If so, would we be willing to reconsider rejecting it because > of Swing conflicts? The interactions do not seem to > be all that bad: Swing programmers would never encounter > problems as pure lambda-users (they need not import > java.util.functions.Action or otherwise use the type.) > So the only ones impacted are those who write > lambda-accepting Swing methods, which do not yet exist. > > And to help a bit further, we could keep the method > name as "accept", to reduce conceptual conflict with the > conventions surrounding "perform" in Swing. > > interface Action { void accept(T t); } > > -Doug > > From brian.goetz at oracle.com Sat Jan 19 08:47:06 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Sat, 19 Jan 2013 08:47:06 -0800 Subject: Let's please rename Block to Receiver before it's too late In-Reply-To: <50FA9D5E.1030000@cs.oswego.edu> References: <3E805E80-6E9F-4D97-8E62-B30339C16CED@oracle.com> <50FA9D5E.1030000@cs.oswego.edu> Message-ID: <48261E7F-DF82-4065-94FC-68205F84B97C@oracle.com> Let me explain for the record why I fine Procedure and Action so unacceptable. (Aside from the Algol-68 flashback of Procedure.) They focus on the wrong aspect of what the abstraction that implements them IS. Yes, of course they are implementing a procedure / action; a void-returning thing had better have some side-effect or else it is useless. But from the perspective of how one of these things interacts with other things in an API, it is focusing on the wrong aspect. The key aspect is: it is something you can put a value to. Focus on the value. Here's an example which I think is highly typical. For sake of argument, let's call it ValueAcceptingThingie. class Hasher implements ValueAcceptingThingie { int hash; void accept(T t) { hash = k*hash + t.hashCode(); } int getHash() { return hash; } } ... ValueAcceptingThingie h = new Hasher(); for (T t : collection) h.accept(t); If we went with a verby name like Procedure or Action, it feels all wrong: a hasher *is not* an action or a procedure. But the essence of how these things are used is: "take a value from me", just as supplier is "give me a value." That's where the focus should be. And this is not just about Streams, though I'll admit I have Streams on the brain since that's what I've been working on. But this shows up in other places too. There are lots of words other than Sink that would do the job. The main problem is that nearly every good word is overloaded. Producer/Consumer would be fine for Supplier/Block, but they have other connotations. So do many of the otherwise reasonable candidates. And ones that are sufficiently specific are just too bulky to use. (Maybe even clunkier than ObjToVoidFunction.) Yes, there are places where the value focus is a little strained, such as forEach(). But even there, I think its something you can get used to quickly. (I've used the phrase 'foreach the elements into the sink' and no one was ever confused by what I meant.) This mismatch seems far worse than the action-focused mismatch. On Jan 19, 2013, at 5:19 AM, Doug Lea wrote: > On 01/18/13 21:36, Brian Goetz wrote: >> OK, OK, if people really, really hate Block, I would still consider Sink. >> Sink is the opposite of Source. We could rename Supplier to Source, and >> rename Block to Sink. Leaving the method names the same (accept and get.) >> > > "Sink" is nice in the Stream framework, which is a plus in > encouraging people to use it, but not in other common > contexts where the main property to convey is that it is a > possibly side-effecting action, as opposed to a Function. > > Does everyone else prefer "Action" as best possible name? > If so, would we be willing to reconsider rejecting it because > of Swing conflicts? The interactions do not seem to > be all that bad: Swing programmers would never encounter > problems as pure lambda-users (they need not import > java.util.functions.Action or otherwise use the type.) > So the only ones impacted are those who write > lambda-accepting Swing methods, which do not yet exist. > > And to help a bit further, we could keep the method > name as "accept", to reduce conceptual conflict with the > conventions surrounding "perform" in Swing. > > interface Action { void accept(T t); } > > -Doug > From joe.bowbeer at gmail.com Sat Jan 19 09:09:03 2013 From: joe.bowbeer at gmail.com (Joe Bowbeer) Date: Sat, 19 Jan 2013 09:09:03 -0800 Subject: Let's please rename Block to Receiver before it's too late In-Reply-To: <48261E7F-DF82-4065-94FC-68205F84B97C@oracle.com> References: <3E805E80-6E9F-4D97-8E62-B30339C16CED@oracle.com> <50FA9D5E.1030000@cs.oswego.edu> <48261E7F-DF82-4065-94FC-68205F84B97C@oracle.com> Message-ID: I understand your perspective. In my mind, neither Block nor Procedure are overloaded. Procedure is however by far the more familiar term in this context, which is why I prefer it. On Jan 19, 2013 8:47 AM, "Brian Goetz" wrote: > Let me explain for the record why I fine Procedure and Action so > unacceptable. (Aside from the Algol-68 flashback of Procedure.) They > focus on the wrong aspect of what the abstraction that implements them IS. > Yes, of course they are implementing a procedure / action; a > void-returning thing had better have some side-effect or else it is > useless. But from the perspective of how one of these things interacts > with other things in an API, it is focusing on the wrong aspect. The key > aspect is: it is something you can put a value to. Focus on the value. > > Here's an example which I think is highly typical. For sake of argument, > let's call it ValueAcceptingThingie. > > class Hasher implements ValueAcceptingThingie { > int hash; > > void accept(T t) { hash = k*hash + t.hashCode(); } > > int getHash() { return hash; } > } > > ... > > ValueAcceptingThingie h = new Hasher(); > for (T t : collection) > h.accept(t); > > If we went with a verby name like Procedure or Action, it feels all wrong: > a hasher *is not* an action or a procedure. But the essence of how these > things are used is: "take a value from me", just as supplier is "give me a > value." That's where the focus should be. > > And this is not just about Streams, though I'll admit I have Streams on > the brain since that's what I've been working on. But this shows up in > other places too. > > There are lots of words other than Sink that would do the job. The main > problem is that nearly every good word is overloaded. Producer/Consumer > would be fine for Supplier/Block, but they have other connotations. So do > many of the otherwise reasonable candidates. And ones that are > sufficiently specific are just too bulky to use. (Maybe even clunkier than > ObjToVoidFunction.) > > Yes, there are places where the value focus is a little strained, such as > forEach(). But even there, I think its something you can get used to > quickly. (I've used the phrase 'foreach the elements into the sink' and no > one was ever confused by what I meant.) This mismatch seems far worse than > the action-focused mismatch. > > > On Jan 19, 2013, at 5:19 AM, Doug Lea wrote: > > > On 01/18/13 21:36, Brian Goetz wrote: > >> OK, OK, if people really, really hate Block, I would still consider > Sink. > >> Sink is the opposite of Source. We could rename Supplier to Source, > and > >> rename Block to Sink. Leaving the method names the same (accept and > get.) > >> > > > > "Sink" is nice in the Stream framework, which is a plus in > > encouraging people to use it, but not in other common > > contexts where the main property to convey is that it is a > > possibly side-effecting action, as opposed to a Function. > > > > Does everyone else prefer "Action" as best possible name? > > If so, would we be willing to reconsider rejecting it because > > of Swing conflicts? The interactions do not seem to > > be all that bad: Swing programmers would never encounter > > problems as pure lambda-users (they need not import > > java.util.functions.Action or otherwise use the type.) > > So the only ones impacted are those who write > > lambda-accepting Swing methods, which do not yet exist. > > > > And to help a bit further, we could keep the method > > name as "accept", to reduce conceptual conflict with the > > conventions surrounding "perform" in Swing. > > > > interface Action { void accept(T t); } > > > > -Doug > > > > From forax at univ-mlv.fr Mon Jan 21 11:07:49 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Mon, 21 Jan 2013 20:07:49 +0100 Subject: hg: lambda/lambda/jdk: - remove Spliterator.iterator(), see Streams.iteratorFrom for equivalent In-Reply-To: <20130121151833.AFCD647425@hg.openjdk.java.net> References: <20130121151833.AFCD647425@hg.openjdk.java.net> Message-ID: <50FD9205.8050906@univ-mlv.fr> Paul (or Brian), can you explain how tryAdvance work ? R?mi On 01/21/2013 04:17 PM, paul.sandoz at oracle.com wrote: > Changeset: 31fea8a84b52 > Author: psandoz > Date: 2013-01-21 16:17 +0100 > URL: http://hg.openjdk.java.net/lambda/lambda/jdk/rev/31fea8a84b52 > > - remove Spliterator.iterator(), see Streams.iteratorFrom for equivalent > functionality. > - Stream.iterator() becomes a default method adapting the spliterator > to an iterator. > - consolidate spliterator tests for ref and primitives. > (Certain spliterator tests are disabled until constraints on > intermixing tryAdvance/forEach and multiple calls to forEach > are resolved.) > > ! src/share/classes/java/util/Spliterator.java > ! src/share/classes/java/util/stream/AbstractPipeline.java > ! src/share/classes/java/util/stream/BaseStream.java > ! src/share/classes/java/util/stream/DoublePipeline.java > ! src/share/classes/java/util/stream/DoubleStream.java > ! src/share/classes/java/util/stream/IntPipeline.java > ! src/share/classes/java/util/stream/IntStream.java > ! src/share/classes/java/util/stream/LongPipeline.java > ! src/share/classes/java/util/stream/LongStream.java > ! src/share/classes/java/util/stream/Node.java > ! src/share/classes/java/util/stream/Nodes.java > ! src/share/classes/java/util/stream/ReferencePipeline.java > ! src/share/classes/java/util/stream/SpinedBuffer.java > ! src/share/classes/java/util/stream/Spliterators.java > ! src/share/classes/java/util/stream/Stream.java > ! src/share/classes/java/util/stream/Streams.java > ! test-ng/bootlib/java/util/stream/DoubleStreamTestScenario.java > ! test-ng/bootlib/java/util/stream/IntStreamTestScenario.java > ! test-ng/bootlib/java/util/stream/LambdaTestHelpers.java > ! test-ng/bootlib/java/util/stream/LongStreamTestScenario.java > ! test-ng/bootlib/java/util/stream/SpliteratorTestHelper.java > ! test-ng/bootlib/java/util/stream/StreamTestScenario.java > ! test-ng/boottests/java/util/stream/DoubleNodeTest.java > ! test-ng/boottests/java/util/stream/IntNodeTest.java > ! test-ng/boottests/java/util/stream/LongNodeTest.java > ! test-ng/boottests/java/util/stream/NodeBuilderTest.java > ! test-ng/boottests/java/util/stream/NodeTest.java > ! test-ng/boottests/java/util/stream/SpinedBufferTest.java > ! test-ng/boottests/java/util/stream/ToArrayOpTest.java > - test-ng/tests/org/openjdk/tests/java/util/stream/IntSpliteratorTest.java > ! test-ng/tests/org/openjdk/tests/java/util/stream/IntStreamSpliteratorTest.java > ! test-ng/tests/org/openjdk/tests/java/util/stream/SortedOpTest.java > ! test-ng/tests/org/openjdk/tests/java/util/stream/SpliteratorTest.java > > From paul.sandoz at oracle.com Tue Jan 22 04:16:10 2013 From: paul.sandoz at oracle.com (Paul Sandoz) Date: Tue, 22 Jan 2013 13:16:10 +0100 Subject: hg: lambda/lambda/jdk: - remove Spliterator.iterator(), see Streams.iteratorFrom for equivalent In-Reply-To: <50FD9205.8050906@univ-mlv.fr> References: <20130121151833.AFCD647425@hg.openjdk.java.net> <50FD9205.8050906@univ-mlv.fr> Message-ID: <274404F1-CE95-4355-913F-9FBDDCCA6DCC@oracle.com> Hi Remi, On Jan 21, 2013, at 8:07 PM, Remi Forax wrote: > Paul (or Brian), > can you explain how tryAdvance work ? > I updated the JavaDoc of tryAdvance (obtained via Doug): /** * If a remaining element exists, performs the given action on it, * returning {@code true}, else returns {@code false}. * * @param block The action. * @return {@code false} if no remaining elements existed * upon entry to this method, else {@code true}. */ Can be used like this: Block b = ... while(s.tryAdvance(b)); (e.g. that could be the default forEach method.) I expect there will be a more thorough update to Spliterator docs soon once Doug's updates are ready. Hth, Paul. From brian.goetz at oracle.com Tue Jan 22 10:10:51 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 22 Jan 2013 10:10:51 -0800 Subject: hg: lambda/lambda/jdk: - remove Spliterator.iterator(), see Streams.iteratorFrom for equivalent In-Reply-To: <50FD9205.8050906@univ-mlv.fr> References: <20130121151833.AFCD647425@hg.openjdk.java.net> <50FD9205.8050906@univ-mlv.fr> Message-ID: Doug and I have been working on the form for Spliterator. We've combined the advance/get methods into tryAdvance, which sends the next element (if present) to the provided Block. So there is: trySplit -> return spliterator or null tryAdvance(Block) -> send element to Block if present; return whether there was an element forEach(Block) -> send remaning elements to Block On Jan 21, 2013, at 11:07 AM, Remi Forax wrote: > Paul (or Brian), > can you explain how tryAdvance work ? > > R?mi > > On 01/21/2013 04:17 PM, paul.sandoz at oracle.com wrote: >> Changeset: 31fea8a84b52 >> Author: psandoz >> Date: 2013-01-21 16:17 +0100 >> URL: http://hg.openjdk.java.net/lambda/lambda/jdk/rev/31fea8a84b52 >> >> - remove Spliterator.iterator(), see Streams.iteratorFrom for equivalent >> functionality. >> - Stream.iterator() becomes a default method adapting the spliterator >> to an iterator. >> - consolidate spliterator tests for ref and primitives. >> (Certain spliterator tests are disabled until constraints on >> intermixing tryAdvance/forEach and multiple calls to forEach >> are resolved.) >> >> ! src/share/classes/java/util/Spliterator.java >> ! src/share/classes/java/util/stream/AbstractPipeline.java >> ! src/share/classes/java/util/stream/BaseStream.java >> ! src/share/classes/java/util/stream/DoublePipeline.java >> ! src/share/classes/java/util/stream/DoubleStream.java >> ! src/share/classes/java/util/stream/IntPipeline.java >> ! src/share/classes/java/util/stream/IntStream.java >> ! src/share/classes/java/util/stream/LongPipeline.java >> ! src/share/classes/java/util/stream/LongStream.java >> ! src/share/classes/java/util/stream/Node.java >> ! src/share/classes/java/util/stream/Nodes.java >> ! src/share/classes/java/util/stream/ReferencePipeline.java >> ! src/share/classes/java/util/stream/SpinedBuffer.java >> ! src/share/classes/java/util/stream/Spliterators.java >> ! src/share/classes/java/util/stream/Stream.java >> ! src/share/classes/java/util/stream/Streams.java >> ! test-ng/bootlib/java/util/stream/DoubleStreamTestScenario.java >> ! test-ng/bootlib/java/util/stream/IntStreamTestScenario.java >> ! test-ng/bootlib/java/util/stream/LambdaTestHelpers.java >> ! test-ng/bootlib/java/util/stream/LongStreamTestScenario.java >> ! test-ng/bootlib/java/util/stream/SpliteratorTestHelper.java >> ! test-ng/bootlib/java/util/stream/StreamTestScenario.java >> ! test-ng/boottests/java/util/stream/DoubleNodeTest.java >> ! test-ng/boottests/java/util/stream/IntNodeTest.java >> ! test-ng/boottests/java/util/stream/LongNodeTest.java >> ! test-ng/boottests/java/util/stream/NodeBuilderTest.java >> ! test-ng/boottests/java/util/stream/NodeTest.java >> ! test-ng/boottests/java/util/stream/SpinedBufferTest.java >> ! test-ng/boottests/java/util/stream/ToArrayOpTest.java >> - test-ng/tests/org/openjdk/tests/java/util/stream/IntSpliteratorTest.java >> ! test-ng/tests/org/openjdk/tests/java/util/stream/IntStreamSpliteratorTest.java >> ! test-ng/tests/org/openjdk/tests/java/util/stream/SortedOpTest.java >> ! test-ng/tests/org/openjdk/tests/java/util/stream/SpliteratorTest.java >> >> > From Donald.Raab at gs.com Tue Jan 22 13:59:39 2013 From: Donald.Raab at gs.com (Raab, Donald) Date: Tue, 22 Jan 2013 16:59:39 -0500 Subject: Let's please rename Block to Receiver before it's too late In-Reply-To: References: <50F9B726.1020904@cs.oswego.edu> <6712820CB52CFB4D842561213A77C05404C0C188F8@GSCMAMP09EX.firmwide.corp.gs.com> Message-ID: <6712820CB52CFB4D842561213A77C05404C3A889B0@GSCMAMP09EX.firmwide.corp.gs.com> The "collect" overlap and translation problem will happen with Groovy and Ruby developers as well. One possible alternative name is "aggregate". http://dictionary.reference.com/browse/aggregate?s=t verb forms: -"to bring together; collect into one sum, mass, or body." -"to?combine?and?form?a?collection?or?mass." It would probably make sense then to rename Collector -> Aggregator and then accumulate -> aggregate and finally Collectors -> Aggregators. public interface Collector { default boolean isConcurrent() { return false; } R makeResult(); void accumulate(R result, T value); R combine(R result, R other); } From: tpeierls at gmail.com [mailto:tpeierls at gmail.com] On Behalf Of Tim Peierls Sent: Friday, January 18, 2013 5:26 PM To: Raab, Donald [Tech] Cc: Doug Lea; lambda-libs-spec-experts at openjdk.java.net Subject: Re: Let's please rename Block to Receiver before it's too late Please can we rename collect as well. ? Whoa, steady -- I *like* collect. I see the overlap issue, but that really can't be allowed to stand in the way of a good name. What were you proposing instead? --tim From brian.goetz at oracle.com Wed Jan 23 12:11:06 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 23 Jan 2013 15:11:06 -0500 Subject: Function type naming conventions In-Reply-To: <50F04DDB.3050101@cs.oswego.edu> References: <50E48919.2060408@oracle.com> <07F4CED6-B457-4573-B111-C7547FD15692@oracle.com> <0D1FD194-593A-46D2-BC11-5C7AA07A7420@oracle.com> <50F041D3.8070004@cs.oswego.edu> <2564DA2D-D372-45C1-A2B3-94DD214A3F5B@oracle.com> <50F04DDB.3050101@cs.oswego.edu> Message-ID: <510043DA.4040405@oracle.com> Returning to this.... Recall that the primary goal in choosing SAM names is user ergonomics. Not only do we want to make things clear, but we want to assign the simple names to the most commonly used configurations. Hence "Function" instead of "ObjectToObjectFunction". A secondary goal is for the names to be mechanically derivable from a reasonably small set of rules. To recap where we are: Base types, each with their own natural arity: Block T -> void Function T -> R Predicate T -> boolean Supplier () -> T Plus some derived convenience types: UnaryOperator extends Function BinaryOperator extends BiFunction Plus arity prefixes to modify the natural arity of these: BiFunction // (T,U) -> R BiPredicate // (T,U) -> boolean We have a primitive specialization convention that lets you prefix {Int,Long,Double,Boolean,Obj} on front of one of these (possibly multiple times). In accordance with our ergonomics goal, we want simple specializations for those that specalize return type, since that has been the most common specialization. (Obj is used when you want to specialize later type params.) We first tried ordering the type parameters return-type-first, so that partial specialization could proceed left-to-right. This went over like a lead balloon. So we then adopted the rule that we specialize return type first when the return type is generic, and otherwise left-to-right. This means that IntFunction is a function T -> int. This seemed attractive as T -> int was far, far more common than int -> T. But this special treatment introduces anomalies. For example, this rule would (probably) assign IntDoubleFunction for double -> int, whereas users will probably expect IntDouble function to correspond to int -> double based on left-to-right. There are ways to further complicate the mapping to avoid this but that just moves the anomalies into darker corners. Based on offline discussions with Kevin and Joe, we concluded that backing off slightly from our desire to have the shortest possible name for the most common specialization can yield a more consistent naming scheme while still being acceptably ergonomic. So, new proposal: - When specializing return type, prefix "ToXxx" - Otherwise specializations gobble type arguments left-to-right. So: Function // T -> U DoubleFunction // double -> T ToIntFunction // T -> int DoubleToIntFunction // double -> int This also means there is more than one way to represent the same thing in some cases. For example, also consistent with these rules are: DoubleIntFunction // double -> int since this covers all the type arguments. Only Function and Supplier are generic in return, so specializations of Block and Predicate would not be affected by the new "To" rule. This scheme is simpler and has fewer anomalies. The casualty is that IntFunction becomes ToIntFunction -- arguably clearer, and only slightly more verbose -- overall seems a win. Here's how the current SAMs would be affected: IntFunction ToIntFunction IntBiFunction ToIntBiFunction IntBlock not affected IntBinaryOperator not affected IntPredicate not affected IntSupplier ToIntSupplier alternately, not affected IntUnaryOperator not affected ObjIntBiBlock not affected ObjIntFunction IntToObjFunction alternately, IntObjFunction A possible (orthogonal) tweak to this system would be: - When there are enough specializations to cover all arguments, omit the arity prefix This would turn ObjIntBiBlock into ObjIntBlock. Some more investigation would be required to ensure there are no collisions here. From spullara at gmail.com Wed Jan 23 12:19:11 2013 From: spullara at gmail.com (Sam Pullara) Date: Wed, 23 Jan 2013 12:19:11 -0800 Subject: Function type naming conventions In-Reply-To: <510043DA.4040405@oracle.com> References: <50E48919.2060408@oracle.com> <07F4CED6-B457-4573-B111-C7547FD15692@oracle.com> <0D1FD194-593A-46D2-BC11-5C7AA07A7420@oracle.com> <50F041D3.8070004@cs.oswego.edu> <2564DA2D-D372-45C1-A2B3-94DD214A3F5B@oracle.com> <50F04DDB.3050101@cs.oswego.edu> <510043DA.4040405@oracle.com> Message-ID: <5B350BAD-8F13-4C57-80ED-D2CA708729FE@gmail.com> Awesome, this is where I hoped we would end up on this. Thanks, Sam On Jan 23, 2013, at 12:11 PM, Brian Goetz wrote: > Returning to this.... > > Recall that the primary goal in choosing SAM names is user ergonomics. Not only do we want to make things clear, but we want to assign the simple names to the most commonly used configurations. Hence "Function" instead of "ObjectToObjectFunction". A secondary goal is for the names to be mechanically derivable from a reasonably small set of rules. > > To recap where we are: > > Base types, each with their own natural arity: > > Block > T -> void > Function T -> R > Predicate T -> boolean > Supplier () -> T > > Plus some derived convenience types: > > UnaryOperator extends Function > BinaryOperator extends BiFunction > > Plus arity prefixes to modify the natural arity of these: > > BiFunction // (T,U) -> R > BiPredicate // (T,U) -> boolean > > We have a primitive specialization convention that lets you prefix {Int,Long,Double,Boolean,Obj} on front of one of these (possibly multiple times). In accordance with our ergonomics goal, we want simple specializations for those that specalize return type, since that has been the most common specialization. (Obj is used when you want to specialize later type params.) > > We first tried ordering the type parameters return-type-first, so that partial specialization could proceed left-to-right. This went over like a lead balloon. So we then adopted the rule that we specialize return type first when the return type is generic, and otherwise left-to-right. This means that > > IntFunction > > is a function T -> int. This seemed attractive as T -> int was far, far more common than int -> T. > > But this special treatment introduces anomalies. For example, this rule would (probably) assign > > IntDoubleFunction > > for double -> int, whereas users will probably expect IntDouble function to correspond to int -> double based on left-to-right. There are ways to further complicate the mapping to avoid this but that just moves the anomalies into darker corners. > > Based on offline discussions with Kevin and Joe, we concluded that backing off slightly from our desire to have the shortest possible name for the most common specialization can yield a more consistent naming scheme while still being acceptably ergonomic. > > So, new proposal: > > - When specializing return type, prefix "ToXxx" > - Otherwise specializations gobble type arguments left-to-right. > > So: > > Function // T -> U > DoubleFunction // double -> T > ToIntFunction // T -> int > DoubleToIntFunction // double -> int > > This also means there is more than one way to represent the same thing in some cases. For example, also consistent with these rules are: > DoubleIntFunction // double -> int > since this covers all the type arguments. > > Only Function and Supplier are generic in return, so specializations of Block and Predicate would not be affected by the new "To" rule. > > This scheme is simpler and has fewer anomalies. The casualty is that IntFunction becomes ToIntFunction -- arguably clearer, and only slightly more verbose -- overall seems a win. > > Here's how the current SAMs would be affected: > > IntFunction > ToIntFunction > IntBiFunction > ToIntBiFunction > IntBlock > not affected > IntBinaryOperator > not affected > IntPredicate > not affected > IntSupplier > ToIntSupplier > alternately, not affected > IntUnaryOperator > not affected > ObjIntBiBlock > not affected > ObjIntFunction > IntToObjFunction > alternately, IntObjFunction > > A possible (orthogonal) tweak to this system would be: > - When there are enough specializations to cover all arguments, omit the arity prefix > > This would turn ObjIntBiBlock into ObjIntBlock. Some more investigation would be required to ensure there are no collisions here. From forax at univ-mlv.fr Wed Jan 23 12:34:54 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Wed, 23 Jan 2013 21:34:54 +0100 Subject: Function type naming conventions In-Reply-To: <5B350BAD-8F13-4C57-80ED-D2CA708729FE@gmail.com> References: <50E48919.2060408@oracle.com> <07F4CED6-B457-4573-B111-C7547FD15692@oracle.com> <0D1FD194-593A-46D2-BC11-5C7AA07A7420@oracle.com> <50F041D3.8070004@cs.oswego.edu> <2564DA2D-D372-45C1-A2B3-94DD214A3F5B@oracle.com> <50F04DDB.3050101@cs.oswego.edu> <510043DA.4040405@oracle.com> <5B350BAD-8F13-4C57-80ED-D2CA708729FE@gmail.com> Message-ID: <5100496E.1010008@univ-mlv.fr> On 01/23/2013 09:19 PM, Sam Pullara wrote: > Awesome, this is where I hoped we would end up on this. > > Thanks, > Sam I agree with Sam :) but I still think that > Plus some derived convenience types: > > UnaryOperator extends Function > BinaryOperator extends BiFunction UnaryOperator should be Operator and BinaryOperator should be BiOperator, it's just more regular. Operator extends Function BiOperator extends BiFunction R?mi > > On Jan 23, 2013, at 12:11 PM, Brian Goetz > wrote: > >> Returning to this.... >> >> Recall that the primary goal in choosing SAM names is user >> ergonomics. Not only do we want to make things clear, but we want to >> assign the simple names to the most commonly used configurations. >> Hence "Function" instead of "ObjectToObjectFunction". A >> secondary goal is for the names to be mechanically derivable from a >> reasonably small set of rules. >> >> To recap where we are: >> >> Base types, each with their own natural arity: >> >> Block >> T -> void >> Function T -> R >> Predicate T -> boolean >> Supplier () -> T >> >> >> Plus some derived convenience types: >> >> UnaryOperator extends Function >> BinaryOperator extends BiFunction >> >> Plus arity prefixes to modify the natural arity of these: >> >> BiFunction // (T,U) -> R >> BiPredicate // (T,U) -> boolean >> >> We have a primitive specialization convention that lets you prefix >> {Int,Long,Double,Boolean,Obj} on front of one of these (possibly >> multiple times). In accordance with our ergonomics goal, we want >> simple specializations for those that specalize return type, since >> that has been the most common specialization. (Obj is used when you >> want to specialize later type params.) >> >> We first tried ordering the type parameters return-type-first, so >> that partial specialization could proceed left-to-right. This went >> over like a lead balloon. So we then adopted the rule that we >> specialize return type first when the return type is generic, and >> otherwise left-to-right. This means that >> >> IntFunction >> >> is a function T -> int. This seemed attractive as T -> int was far, >> far more common than int -> T. >> >> But this special treatment introduces anomalies. For example, this >> rule would (probably) assign >> >> IntDoubleFunction >> >> for double -> int, whereas users will probably expect IntDouble >> function to correspond to int -> double based on left-to-right. >> There are ways to further complicate the mapping to avoid this but >> that just moves the anomalies into darker corners. >> >> Based on offline discussions with Kevin and Joe, we concluded that >> backing off slightly from our desire to have the shortest possible >> name for the most common specialization can yield a more consistent >> naming scheme while still being acceptably ergonomic. >> >> So, new proposal: >> >> - When specializing return type, prefix "ToXxx" >> - Otherwise specializations gobble type arguments left-to-right. >> >> So: >> >> Function // T -> U >> DoubleFunction // double -> T >> ToIntFunction // T -> int >> DoubleToIntFunction // double -> int >> >> This also means there is more than one way to represent the same >> thing in some cases. For example, also consistent with these rules are: >> DoubleIntFunction // double -> int >> since this covers all the type arguments. >> >> Only Function and Supplier are generic in return, so specializations >> of Block and Predicate would not be affected by the new "To" rule. >> >> This scheme is simpler and has fewer anomalies. The casualty is that >> IntFunction becomes ToIntFunction -- arguably clearer, and only >> slightly more verbose -- overall seems a win. >> >> Here's how the current SAMs would be affected: >> >> IntFunction >> ToIntFunction >> IntBiFunction >> ToIntBiFunction >> IntBlock >> not affected >> IntBinaryOperator >> not affected >> IntPredicate >> not affected >> IntSupplier >> ToIntSupplier >> alternately, not affected >> IntUnaryOperator >> not affected >> ObjIntBiBlock >> not affected >> ObjIntFunction >> IntToObjFunction >> alternately, IntObjFunction >> >> >> A possible (orthogonal) tweak to this system would be: >> - When there are enough specializations to cover all arguments, omit >> the arity prefix >> >> This would turn ObjIntBiBlock into ObjIntBlock. Some more >> investigation would be required to ensure there are no collisions here. > From spullara at gmail.com Wed Jan 23 13:11:35 2013 From: spullara at gmail.com (Sam Pullara) Date: Wed, 23 Jan 2013 13:11:35 -0800 Subject: Function type naming conventions In-Reply-To: <5100496E.1010008@univ-mlv.fr> References: <50E48919.2060408@oracle.com> <07F4CED6-B457-4573-B111-C7547FD15692@oracle.com> <0D1FD194-593A-46D2-BC11-5C7AA07A7420@oracle.com> <50F041D3.8070004@cs.oswego.edu> <2564DA2D-D372-45C1-A2B3-94DD214A3F5B@oracle.com> <50F04DDB.3050101@cs.oswego.edu> <510043DA.4040405@oracle.com> <5B350BAD-8F13-4C57-80ED-D2CA708729FE@gmail.com> <5100496E.1010008@univ-mlv.fr> Message-ID: <51808D9E-4B63-4A93-B182-F9374B326FFC@gmail.com> Seems super reasonable to me. Sam On Jan 23, 2013, at 12:34 PM, Remi Forax wrote: > On 01/23/2013 09:19 PM, Sam Pullara wrote: >> Awesome, this is where I hoped we would end up on this. >> >> Thanks, >> Sam > > I agree with Sam :) > but I still think that > > > Plus some derived convenience types: > > > > UnaryOperator extends Function > > BinaryOperator extends BiFunction > > UnaryOperator should be Operator and BinaryOperator should be BiOperator, > it's just more regular. > > Operator extends Function > BiOperator extends BiFunction > > R?mi > >> >> On Jan 23, 2013, at 12:11 PM, Brian Goetz > wrote: >> >>> Returning to this.... >>> >>> Recall that the primary goal in choosing SAM names is user ergonomics. Not only do we want to make things clear, but we want to assign the simple names to the most commonly used configurations. Hence "Function" instead of "ObjectToObjectFunction". A secondary goal is for the names to be mechanically derivable from a reasonably small set of rules. >>> >>> To recap where we are: >>> >>> Base types, each with their own natural arity: >>> >>> Block >>> T -> void >>> Function T -> R >>> Predicate T -> boolean >>> Supplier () -> T >>> >>> >>> Plus some derived convenience types: >>> >>> UnaryOperator extends Function >>> BinaryOperator extends BiFunction >>> >>> Plus arity prefixes to modify the natural arity of these: >>> >>> BiFunction // (T,U) -> R >>> BiPredicate // (T,U) -> boolean >>> >>> We have a primitive specialization convention that lets you prefix {Int,Long,Double,Boolean,Obj} on front of one of these (possibly multiple times). In accordance with our ergonomics goal, we want simple specializations for those that specalize return type, since that has been the most common specialization. (Obj is used when you want to specialize later type params.) >>> >>> We first tried ordering the type parameters return-type-first, so that partial specialization could proceed left-to-right. This went over like a lead balloon. So we then adopted the rule that we specialize return type first when the return type is generic, and otherwise left-to-right. This means that >>> >>> IntFunction >>> >>> is a function T -> int. This seemed attractive as T -> int was far, far more common than int -> T. >>> >>> But this special treatment introduces anomalies. For example, this rule would (probably) assign >>> >>> IntDoubleFunction >>> >>> for double -> int, whereas users will probably expect IntDouble function to correspond to int -> double based on left-to-right. There are ways to further complicate the mapping to avoid this but that just moves the anomalies into darker corners. >>> >>> Based on offline discussions with Kevin and Joe, we concluded that backing off slightly from our desire to have the shortest possible name for the most common specialization can yield a more consistent naming scheme while still being acceptably ergonomic. >>> >>> So, new proposal: >>> >>> - When specializing return type, prefix "ToXxx" >>> - Otherwise specializations gobble type arguments left-to-right. >>> >>> So: >>> >>> Function // T -> U >>> DoubleFunction // double -> T >>> ToIntFunction // T -> int >>> DoubleToIntFunction // double -> int >>> >>> This also means there is more than one way to represent the same thing in some cases. For example, also consistent with these rules are: >>> DoubleIntFunction // double -> int >>> since this covers all the type arguments. >>> >>> Only Function and Supplier are generic in return, so specializations of Block and Predicate would not be affected by the new "To" rule. >>> >>> This scheme is simpler and has fewer anomalies. The casualty is that IntFunction becomes ToIntFunction -- arguably clearer, and only slightly more verbose -- overall seems a win. >>> >>> Here's how the current SAMs would be affected: >>> >>> IntFunction >>> ToIntFunction >>> IntBiFunction >>> ToIntBiFunction >>> IntBlock >>> not affected >>> IntBinaryOperator >>> not affected >>> IntPredicate >>> not affected >>> IntSupplier >>> ToIntSupplier >>> alternately, not affected >>> IntUnaryOperator >>> not affected >>> ObjIntBiBlock >>> not affected >>> ObjIntFunction >>> IntToObjFunction >>> alternately, IntObjFunction >>> >>> >>> A possible (orthogonal) tweak to this system would be: >>> - When there are enough specializations to cover all arguments, omit the arity prefix >>> >>> This would turn ObjIntBiBlock into ObjIntBlock. Some more investigation would be required to ensure there are no collisions here. >> > From brian.goetz at oracle.com Wed Jan 23 13:11:10 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 23 Jan 2013 16:11:10 -0500 Subject: Let's please rename Block to Receiver before it's too late In-Reply-To: References: Message-ID: <510051EE.1070000@oracle.com> Time to close on this. I've posted an A/B SurveyMonkey survey between Source/Sink and Supplier/Block, which seem to be the least objectionable combinations. On 1/18/2013 1:58 PM, Kevin Bourrillion wrote: > When I see methods like > > doSomething(IntBlock intBlock); > doSomethingElse(Block stringBlock); > > ... I can't even guess what these things mean. What is a "block of > string" or an "int block"? If forced to guess, I'd say "well, this > clearly has nothing to do with a 'block' in the Java language, but > that's my best analogy anyway, and blocks have no well-defined inputs or > outputs, but that int/string has to get involved somehow, so..... if > that whole block somehow represents an Int in some way it must be that > the whole thing /evaluates/ to an Int... except wait, there's also > IntSupplier.... wtf?" > > Procedure has similar problems to maybe half the same degree. > > But then consider this: > > doSomething(IntReceiver receiver); > doSomethingElse(Receiver receiver); > > How much clearer could anything be? It's an int receiver: it receives > ints! Bonus: it has a much clearer relationship to Supplier. > > I have scoured the threads to find what the problems are that people had > with Receiver, and I haven't found any. Privately Brian guessed the > problem could be "confusion with receiver in the sense of method > receiver?" But that's not even a term 95% of normal Java developers > know or use. And even if so, the meaning of "an int receiver" is so > clear the mind doesn't even /go/ there. > > Agree/disagree/neutral? > > -- > Kevin Bourrillion | Java Librarian | Google, Inc. |kevinb at google.com > From brian.goetz at oracle.com Wed Jan 23 13:20:05 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 23 Jan 2013 16:20:05 -0500 Subject: Function type naming conventions In-Reply-To: <5B350BAD-8F13-4C57-80ED-D2CA708729FE@gmail.com> References: <50E48919.2060408@oracle.com> <07F4CED6-B457-4573-B111-C7547FD15692@oracle.com> <0D1FD194-593A-46D2-BC11-5C7AA07A7420@oracle.com> <50F041D3.8070004@cs.oswego.edu> <2564DA2D-D372-45C1-A2B3-94DD214A3F5B@oracle.com> <50F04DDB.3050101@cs.oswego.edu> <510043DA.4040405@oracle.com> <5B350BAD-8F13-4C57-80ED-D2CA708729FE@gmail.com> Message-ID: <51005405.7020306@oracle.com> Will do the first part tomorrow unless someone strongly objects. Open issues: - Where multiple rules apply, do we prefer XY or XToY? - Do we like the "drop the arity prefix in case all are specialized" rule tweak? On 1/23/2013 3:19 PM, Sam Pullara wrote: > Awesome, this is where I hoped we would end up on this. > > Thanks, > Sam > > On Jan 23, 2013, at 12:11 PM, Brian Goetz > wrote: > >> Returning to this.... >> >> Recall that the primary goal in choosing SAM names is user ergonomics. >> Not only do we want to make things clear, but we want to assign the >> simple names to the most commonly used configurations. Hence >> "Function" instead of "ObjectToObjectFunction". A secondary goal >> is for the names to be mechanically derivable from a reasonably small >> set of rules. >> >> To recap where we are: >> >> Base types, each with their own natural arity: >> >> Block >> T -> void >> Function T -> R >> Predicate T -> boolean >> Supplier () -> T >> >> >> Plus some derived convenience types: >> >> UnaryOperator extends Function >> BinaryOperator extends BiFunction >> >> Plus arity prefixes to modify the natural arity of these: >> >> BiFunction // (T,U) -> R >> BiPredicate // (T,U) -> boolean >> >> We have a primitive specialization convention that lets you prefix >> {Int,Long,Double,Boolean,Obj} on front of one of these (possibly >> multiple times). In accordance with our ergonomics goal, we want >> simple specializations for those that specalize return type, since >> that has been the most common specialization. (Obj is used when you >> want to specialize later type params.) >> >> We first tried ordering the type parameters return-type-first, so that >> partial specialization could proceed left-to-right. This went over >> like a lead balloon. So we then adopted the rule that we specialize >> return type first when the return type is generic, and otherwise >> left-to-right. This means that >> >> IntFunction >> >> is a function T -> int. This seemed attractive as T -> int was far, >> far more common than int -> T. >> >> But this special treatment introduces anomalies. For example, this >> rule would (probably) assign >> >> IntDoubleFunction >> >> for double -> int, whereas users will probably expect IntDouble >> function to correspond to int -> double based on left-to-right. There >> are ways to further complicate the mapping to avoid this but that just >> moves the anomalies into darker corners. >> >> Based on offline discussions with Kevin and Joe, we concluded that >> backing off slightly from our desire to have the shortest possible >> name for the most common specialization can yield a more consistent >> naming scheme while still being acceptably ergonomic. >> >> So, new proposal: >> >> - When specializing return type, prefix "ToXxx" >> - Otherwise specializations gobble type arguments left-to-right. >> >> So: >> >> Function // T -> U >> DoubleFunction // double -> T >> ToIntFunction // T -> int >> DoubleToIntFunction // double -> int >> >> This also means there is more than one way to represent the same thing >> in some cases. For example, also consistent with these rules are: >> DoubleIntFunction // double -> int >> since this covers all the type arguments. >> >> Only Function and Supplier are generic in return, so specializations >> of Block and Predicate would not be affected by the new "To" rule. >> >> This scheme is simpler and has fewer anomalies. The casualty is that >> IntFunction becomes ToIntFunction -- arguably clearer, and only >> slightly more verbose -- overall seems a win. >> >> Here's how the current SAMs would be affected: >> >> IntFunction >> ToIntFunction >> IntBiFunction >> ToIntBiFunction >> IntBlock >> not affected >> IntBinaryOperator >> not affected >> IntPredicate >> not affected >> IntSupplier >> ToIntSupplier >> alternately, not affected >> IntUnaryOperator >> not affected >> ObjIntBiBlock >> not affected >> ObjIntFunction >> IntToObjFunction >> alternately, IntObjFunction >> >> >> A possible (orthogonal) tweak to this system would be: >> - When there are enough specializations to cover all arguments, omit >> the arity prefix >> >> This would turn ObjIntBiBlock into ObjIntBlock. Some more >> investigation would be required to ensure there are no collisions here. > From tim at peierls.net Wed Jan 23 13:22:27 2013 From: tim at peierls.net (Tim Peierls) Date: Wed, 23 Jan 2013 16:22:27 -0500 Subject: Function type naming conventions In-Reply-To: <5100496E.1010008@univ-mlv.fr> References: <50E48919.2060408@oracle.com> <07F4CED6-B457-4573-B111-C7547FD15692@oracle.com> <0D1FD194-593A-46D2-BC11-5C7AA07A7420@oracle.com> <50F041D3.8070004@cs.oswego.edu> <2564DA2D-D372-45C1-A2B3-94DD214A3F5B@oracle.com> <50F04DDB.3050101@cs.oswego.edu> <510043DA.4040405@oracle.com> <5B350BAD-8F13-4C57-80ED-D2CA708729FE@gmail.com> <5100496E.1010008@univ-mlv.fr> Message-ID: On Wed, Jan 23, 2013 at 3:34 PM, Remi Forax wrote: > > UnaryOperator extends Function > > BinaryOperator extends BiFunction > > UnaryOperator should be Operator and BinaryOperator should be BiOperator, > it's just more regular. > > Operator extends Function > BiOperator extends BiFunction > The reason to have these "convenience" types in the first place is so they'll be easy to remember and recognize. The term "function" is much more commonly used to mean "function of one argument", so Function/BiFunction is better than UnaryFunction/BinaryFunction. The term "operator" skew more evenly between unary and binary, so UnaryOperator/BinaryOperator is better than Operator/BiOperator. --tim From brian.goetz at oracle.com Wed Jan 23 13:24:38 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 23 Jan 2013 16:24:38 -0500 Subject: Function type naming conventions In-Reply-To: References: <50E48919.2060408@oracle.com> <07F4CED6-B457-4573-B111-C7547FD15692@oracle.com> <0D1FD194-593A-46D2-BC11-5C7AA07A7420@oracle.com> <50F041D3.8070004@cs.oswego.edu> <2564DA2D-D372-45C1-A2B3-94DD214A3F5B@oracle.com> <50F04DDB.3050101@cs.oswego.edu> <510043DA.4040405@oracle.com> <5B350BAD-8F13-4C57-80ED-D2CA708729FE@gmail.com> <5100496E.1010008@univ-mlv.fr> Message-ID: <51005516.6040404@oracle.com> Agreed. (Which is the same conclusion we had the last several times Remi brought this issue up :) On 1/23/2013 4:22 PM, Tim Peierls wrote: > On Wed, Jan 23, 2013 at 3:34 PM, Remi Forax > wrote: > > > UnaryOperator extends Function > > BinaryOperator extends BiFunction > > UnaryOperator should be Operator and BinaryOperator should be > BiOperator, > it's just more regular. > > Operator extends Function > BiOperator extends BiFunction > > > The reason to have these "convenience" types in the first place is so > they'll be easy to remember and recognize. The term "function" is much > more commonly used to mean "function of one argument", so > Function/BiFunction is better than UnaryFunction/BinaryFunction. The > term "operator" skew more evenly between unary and binary, so > UnaryOperator/BinaryOperator is better than Operator/BiOperator. > > --tim From tim at peierls.net Wed Jan 23 13:26:12 2013 From: tim at peierls.net (Tim Peierls) Date: Wed, 23 Jan 2013 16:26:12 -0500 Subject: Function type naming conventions In-Reply-To: <51005405.7020306@oracle.com> References: <50E48919.2060408@oracle.com> <07F4CED6-B457-4573-B111-C7547FD15692@oracle.com> <0D1FD194-593A-46D2-BC11-5C7AA07A7420@oracle.com> <50F041D3.8070004@cs.oswego.edu> <2564DA2D-D372-45C1-A2B3-94DD214A3F5B@oracle.com> <50F04DDB.3050101@cs.oswego.edu> <510043DA.4040405@oracle.com> <5B350BAD-8F13-4C57-80ED-D2CA708729FE@gmail.com> <51005405.7020306@oracle.com> Message-ID: On Wed, Jan 23, 2013 at 4:20 PM, Brian Goetz wrote: > - Where multiple rules apply, do we prefer XY or XToY? > XToY - Do we like the "drop the arity prefix in case all are specialized" rule > tweak? Yes, assuming it doesn't lead to collisions. (And, orthogonal, I know, but let's not forget the Block renaming issue in all this. Anything, anything is better than Block.) --tim From joe.bowbeer at gmail.com Wed Jan 23 13:30:21 2013 From: joe.bowbeer at gmail.com (Joe Bowbeer) Date: Wed, 23 Jan 2013 13:30:21 -0800 Subject: Function type naming conventions In-Reply-To: References: <50E48919.2060408@oracle.com> <07F4CED6-B457-4573-B111-C7547FD15692@oracle.com> <0D1FD194-593A-46D2-BC11-5C7AA07A7420@oracle.com> <50F041D3.8070004@cs.oswego.edu> <2564DA2D-D372-45C1-A2B3-94DD214A3F5B@oracle.com> <50F04DDB.3050101@cs.oswego.edu> <510043DA.4040405@oracle.com> <5B350BAD-8F13-4C57-80ED-D2CA708729FE@gmail.com> <51005405.7020306@oracle.com> Message-ID: I nominate "Anything" as write-in candidate for survey. On Wed, Jan 23, 2013 at 1:26 PM, Tim Peierls wrote: > On Wed, Jan 23, 2013 at 4:20 PM, Brian Goetz wrote: > >> - Where multiple rules apply, do we prefer XY or XToY? >> > > XToY > > > - Do we like the "drop the arity prefix in case all are specialized" rule >> tweak? > > > Yes, assuming it doesn't lead to collisions. > > (And, orthogonal, I know, but let's not forget the Block renaming issue in > all this. Anything, anything is better than Block.) > > --tim > From brian.goetz at oracle.com Wed Jan 23 13:38:30 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 23 Jan 2013 16:38:30 -0500 Subject: Function type naming conventions In-Reply-To: References: <50E48919.2060408@oracle.com> <07F4CED6-B457-4573-B111-C7547FD15692@oracle.com> <0D1FD194-593A-46D2-BC11-5C7AA07A7420@oracle.com> <50F041D3.8070004@cs.oswego.edu> <2564DA2D-D372-45C1-A2B3-94DD214A3F5B@oracle.com> <50F04DDB.3050101@cs.oswego.edu> <510043DA.4040405@oracle.com> <5B350BAD-8F13-4C57-80ED-D2CA708729FE@gmail.com> <51005405.7020306@oracle.com> Message-ID: <51005856.7090008@oracle.com> At Doug's request, I added "Abstain". On 1/23/2013 4:30 PM, Joe Bowbeer wrote: > I nominate "Anything" as write-in candidate for survey. > > > On Wed, Jan 23, 2013 at 1:26 PM, Tim Peierls > wrote: > > On Wed, Jan 23, 2013 at 4:20 PM, Brian Goetz > wrote: > > - Where multiple rules apply, do we prefer XY or XToY? > > > XToY > > - Do we like the "drop the arity prefix in case all are > specialized" rule tweak? > > > Yes, assuming it doesn't lead to collisions. > > (And, orthogonal, I know, but let's not forget the Block renaming > issue in all this. Anything, anything is better than Block.) > > --tim > > From david.holmes at oracle.com Wed Jan 23 17:41:59 2013 From: david.holmes at oracle.com (David Holmes) Date: Thu, 24 Jan 2013 11:41:59 +1000 Subject: Function type naming conventions In-Reply-To: <51005405.7020306@oracle.com> References: <50E48919.2060408@oracle.com> <07F4CED6-B457-4573-B111-C7547FD15692@oracle.com> <0D1FD194-593A-46D2-BC11-5C7AA07A7420@oracle.com> <50F041D3.8070004@cs.oswego.edu> <2564DA2D-D372-45C1-A2B3-94DD214A3F5B@oracle.com> <50F04DDB.3050101@cs.oswego.edu> <510043DA.4040405@oracle.com> <5B350BAD-8F13-4C57-80ED-D2CA708729FE@gmail.com> <51005405.7020306@oracle.com> Message-ID: <51009167.4010909@oracle.com> On 24/01/2013 7:20 AM, Brian Goetz wrote: > Will do the first part tomorrow unless someone strongly objects. Sounds very reasonable to me. > Open issues: > - Where multiple rules apply, do we prefer XY or XToY? XToY > - Do we like the "drop the arity prefix in case all are specialized" > rule tweak? I feel I will need to consult a lexicon regardless so I abstain on this one. David > > > On 1/23/2013 3:19 PM, Sam Pullara wrote: >> Awesome, this is where I hoped we would end up on this. >> >> Thanks, >> Sam >> >> On Jan 23, 2013, at 12:11 PM, Brian Goetz > > wrote: >> >>> Returning to this.... >>> >>> Recall that the primary goal in choosing SAM names is user ergonomics. >>> Not only do we want to make things clear, but we want to assign the >>> simple names to the most commonly used configurations. Hence >>> "Function" instead of "ObjectToObjectFunction". A secondary goal >>> is for the names to be mechanically derivable from a reasonably small >>> set of rules. >>> >>> To recap where we are: >>> >>> Base types, each with their own natural arity: >>> >>> Block >>> T -> void >>> Function T -> R >>> Predicate T -> boolean >>> Supplier () -> T >>> >>> >>> Plus some derived convenience types: >>> >>> UnaryOperator extends Function >>> BinaryOperator extends BiFunction >>> >>> Plus arity prefixes to modify the natural arity of these: >>> >>> BiFunction // (T,U) -> R >>> BiPredicate // (T,U) -> boolean >>> >>> We have a primitive specialization convention that lets you prefix >>> {Int,Long,Double,Boolean,Obj} on front of one of these (possibly >>> multiple times). In accordance with our ergonomics goal, we want >>> simple specializations for those that specalize return type, since >>> that has been the most common specialization. (Obj is used when you >>> want to specialize later type params.) >>> >>> We first tried ordering the type parameters return-type-first, so that >>> partial specialization could proceed left-to-right. This went over >>> like a lead balloon. So we then adopted the rule that we specialize >>> return type first when the return type is generic, and otherwise >>> left-to-right. This means that >>> >>> IntFunction >>> >>> is a function T -> int. This seemed attractive as T -> int was far, >>> far more common than int -> T. >>> >>> But this special treatment introduces anomalies. For example, this >>> rule would (probably) assign >>> >>> IntDoubleFunction >>> >>> for double -> int, whereas users will probably expect IntDouble >>> function to correspond to int -> double based on left-to-right. There >>> are ways to further complicate the mapping to avoid this but that just >>> moves the anomalies into darker corners. >>> >>> Based on offline discussions with Kevin and Joe, we concluded that >>> backing off slightly from our desire to have the shortest possible >>> name for the most common specialization can yield a more consistent >>> naming scheme while still being acceptably ergonomic. >>> >>> So, new proposal: >>> >>> - When specializing return type, prefix "ToXxx" >>> - Otherwise specializations gobble type arguments left-to-right. >>> >>> So: >>> >>> Function // T -> U >>> DoubleFunction // double -> T >>> ToIntFunction // T -> int >>> DoubleToIntFunction // double -> int >>> >>> This also means there is more than one way to represent the same thing >>> in some cases. For example, also consistent with these rules are: >>> DoubleIntFunction // double -> int >>> since this covers all the type arguments. >>> >>> Only Function and Supplier are generic in return, so specializations >>> of Block and Predicate would not be affected by the new "To" rule. >>> >>> This scheme is simpler and has fewer anomalies. The casualty is that >>> IntFunction becomes ToIntFunction -- arguably clearer, and only >>> slightly more verbose -- overall seems a win. >>> >>> Here's how the current SAMs would be affected: >>> >>> IntFunction >>> ToIntFunction >>> IntBiFunction >>> ToIntBiFunction >>> IntBlock >>> not affected >>> IntBinaryOperator >>> not affected >>> IntPredicate >>> not affected >>> IntSupplier >>> ToIntSupplier >>> alternately, not affected >>> IntUnaryOperator >>> not affected >>> ObjIntBiBlock >>> not affected >>> ObjIntFunction >>> IntToObjFunction >>> alternately, IntObjFunction >>> >>> >>> A possible (orthogonal) tweak to this system would be: >>> - When there are enough specializations to cover all arguments, omit >>> the arity prefix >>> >>> This would turn ObjIntBiBlock into ObjIntBlock. Some more >>> investigation would be required to ensure there are no collisions here. >> From Vladimir.Zakharov at gs.com Wed Jan 23 19:08:27 2013 From: Vladimir.Zakharov at gs.com (Zakharov, Vladimir) Date: Wed, 23 Jan 2013 22:08:27 -0500 Subject: Let's please rename Block to Receiver before it's too late In-Reply-To: <510051EE.1070000@oracle.com> References: <510051EE.1070000@oracle.com> Message-ID: A couple of comments on the survey: Can we have options that we can rank in preference order in the survey? I think it is ok for the survey to include things deemed "objectionable" (by whom?), if they truly are people just won't vote for them. BTW, I also thought Procedure got quite a few mentions. Thank you, Vlad The Goldman Sachs Group, Inc. All rights reserved. See http://www.gs.com/disclaimer/global_email for important risk disclosures, conflicts of interest and other terms and conditions relating to this e-mail and your reliance on information contained in it.? This message may contain confidential or privileged information.? If you are not the intended recipient, please advise us immediately and delete this message.? See http://www.gs.com/disclaimer/email for further information on confidentiality and the risks of non-secure electronic communication.? If you cannot access these links, please notify us by reply message and we will send the contents to you.? -----Original Message----- From: lambda-libs-spec-experts-bounces at openjdk.java.net [mailto:lambda-libs-spec-experts-bounces at openjdk.java.net] On Behalf Of Brian Goetz Sent: Wednesday, January 23, 2013 4:11 PM To: Kevin Bourrillion Cc: lambda-libs-spec-experts at openjdk.java.net Subject: Re: Let's please rename Block to Receiver before it's too late Time to close on this. I've posted an A/B SurveyMonkey survey between Source/Sink and Supplier/Block, which seem to be the least objectionable combinations. On 1/18/2013 1:58 PM, Kevin Bourrillion wrote: > When I see methods like > > doSomething(IntBlock intBlock); > doSomethingElse(Block stringBlock); > > ... I can't even guess what these things mean. What is a "block of > string" or an "int block"? If forced to guess, I'd say "well, this > clearly has nothing to do with a 'block' in the Java language, but > that's my best analogy anyway, and blocks have no well-defined inputs or > outputs, but that int/string has to get involved somehow, so..... if > that whole block somehow represents an Int in some way it must be that > the whole thing /evaluates/ to an Int... except wait, there's also > IntSupplier.... wtf?" > > Procedure has similar problems to maybe half the same degree. > > But then consider this: > > doSomething(IntReceiver receiver); > doSomethingElse(Receiver receiver); > > How much clearer could anything be? It's an int receiver: it receives > ints! Bonus: it has a much clearer relationship to Supplier. > > I have scoured the threads to find what the problems are that people had > with Receiver, and I haven't found any. Privately Brian guessed the > problem could be "confusion with receiver in the sense of method > receiver?" But that's not even a term 95% of normal Java developers > know or use. And even if so, the meaning of "an int receiver" is so > clear the mind doesn't even /go/ there. > > Agree/disagree/neutral? > > -- > Kevin Bourrillion | Java Librarian | Google, Inc. |kevinb at google.com > From spullara at gmail.com Wed Jan 23 19:30:57 2013 From: spullara at gmail.com (Sam Pullara) Date: Wed, 23 Jan 2013 19:30:57 -0800 Subject: Let's please rename Block to Receiver before it's too late In-Reply-To: References: <510051EE.1070000@oracle.com> Message-ID: <79112D3D-AD90-482C-8624-39BD82686D9C@gmail.com> After I finished the survey, I thought of: Supplier/Processor Kind of like Procedure but I think more targeted at something that takes something and does something with it. Sam On Jan 23, 2013, at 7:08 PM, Zakharov, Vladimir wrote: > A couple of comments on the survey: > > Can we have options that we can rank in preference order in the survey? > > I think it is ok for the survey to include things deemed "objectionable" (by whom?), if they truly are people just won't vote for them. BTW, I also thought Procedure got quite a few mentions. > > Thank you, > Vlad > > The Goldman Sachs Group, Inc. All rights reserved. > See http://www.gs.com/disclaimer/global_email for important risk disclosures, conflicts of interest and other terms and conditions relating to this e-mail and your reliance on information contained in it. This message may contain confidential or privileged information. If you are not the intended recipient, please advise us immediately and delete this message. See http://www.gs.com/disclaimer/email for further information on confidentiality and the risks of non-secure electronic communication. If you cannot access these links, please notify us by reply message and we will send the contents to you. > > > -----Original Message----- > From: lambda-libs-spec-experts-bounces at openjdk.java.net [mailto:lambda-libs-spec-experts-bounces at openjdk.java.net] On Behalf Of Brian Goetz > Sent: Wednesday, January 23, 2013 4:11 PM > To: Kevin Bourrillion > Cc: lambda-libs-spec-experts at openjdk.java.net > Subject: Re: Let's please rename Block to Receiver before it's too late > > Time to close on this. I've posted an A/B SurveyMonkey survey between > Source/Sink and Supplier/Block, which seem to be the least objectionable > combinations. > > On 1/18/2013 1:58 PM, Kevin Bourrillion wrote: >> When I see methods like >> >> doSomething(IntBlock intBlock); >> doSomethingElse(Block stringBlock); >> >> ... I can't even guess what these things mean. What is a "block of >> string" or an "int block"? If forced to guess, I'd say "well, this >> clearly has nothing to do with a 'block' in the Java language, but >> that's my best analogy anyway, and blocks have no well-defined inputs or >> outputs, but that int/string has to get involved somehow, so..... if >> that whole block somehow represents an Int in some way it must be that >> the whole thing /evaluates/ to an Int... except wait, there's also >> IntSupplier.... wtf?" >> >> Procedure has similar problems to maybe half the same degree. >> >> But then consider this: >> >> doSomething(IntReceiver receiver); >> doSomethingElse(Receiver receiver); >> >> How much clearer could anything be? It's an int receiver: it receives >> ints! Bonus: it has a much clearer relationship to Supplier. >> >> I have scoured the threads to find what the problems are that people had >> with Receiver, and I haven't found any. Privately Brian guessed the >> problem could be "confusion with receiver in the sense of method >> receiver?" But that's not even a term 95% of normal Java developers >> know or use. And even if so, the meaning of "an int receiver" is so >> clear the mind doesn't even /go/ there. >> >> Agree/disagree/neutral? >> >> -- >> Kevin Bourrillion | Java Librarian | Google, Inc. |kevinb at google.com >> From joe.bowbeer at gmail.com Wed Jan 23 19:52:42 2013 From: joe.bowbeer at gmail.com (Joe Bowbeer) Date: Wed, 23 Jan 2013 19:52:42 -0800 Subject: Let's please rename Block to Receiver before it's too late In-Reply-To: <79112D3D-AD90-482C-8624-39BD82686D9C@gmail.com> References: <510051EE.1070000@oracle.com> <79112D3D-AD90-482C-8624-39BD82686D9C@gmail.com> Message-ID: There is already a Processor though it is probably not on the IDE's list of likely imports: javax.annotation.processing.Processor I commented in the small space provided by the survey that I might be OK with Supplier/Consumer. (This was an essay question, right?) On Wed, Jan 23, 2013 at 7:30 PM, Sam Pullara wrote: > After I finished the survey, I thought of: > > Supplier/Processor > > Kind of like Procedure but I think more targeted at something that takes > something and does something with it. > > Sam > > On Jan 23, 2013, at 7:08 PM, Zakharov, Vladimir wrote: > > > A couple of comments on the survey: > > > > Can we have options that we can rank in preference order in the survey? > > > > I think it is ok for the survey to include things deemed "objectionable" > (by whom?), if they truly are people just won't vote for them. BTW, I also > thought Procedure got quite a few mentions. > > > > Thank you, > > Vlad > > > > The Goldman Sachs Group, Inc. All rights reserved. > > See http://www.gs.com/disclaimer/global_email for important risk > disclosures, conflicts of interest and other terms and conditions relating > to this e-mail and your reliance on information contained in it. This > message may contain confidential or privileged information. If you are not > the intended recipient, please advise us immediately and delete this > message. See http://www.gs.com/disclaimer/email for further information > on confidentiality and the risks of non-secure electronic communication. > If you cannot access these links, please notify us by reply message and we > will send the contents to you. > > > > > > -----Original Message----- > > From: lambda-libs-spec-experts-bounces at openjdk.java.net [mailto: > lambda-libs-spec-experts-bounces at openjdk.java.net] On Behalf Of Brian > Goetz > > Sent: Wednesday, January 23, 2013 4:11 PM > > To: Kevin Bourrillion > > Cc: lambda-libs-spec-experts at openjdk.java.net > > Subject: Re: Let's please rename Block to Receiver before it's too late > > > > Time to close on this. I've posted an A/B SurveyMonkey survey between > > Source/Sink and Supplier/Block, which seem to be the least objectionable > > combinations. > > > > On 1/18/2013 1:58 PM, Kevin Bourrillion wrote: > >> When I see methods like > >> > >> doSomething(IntBlock intBlock); > >> doSomethingElse(Block stringBlock); > >> > >> ... I can't even guess what these things mean. What is a "block of > >> string" or an "int block"? If forced to guess, I'd say "well, this > >> clearly has nothing to do with a 'block' in the Java language, but > >> that's my best analogy anyway, and blocks have no well-defined inputs or > >> outputs, but that int/string has to get involved somehow, so..... if > >> that whole block somehow represents an Int in some way it must be that > >> the whole thing /evaluates/ to an Int... except wait, there's also > >> IntSupplier.... wtf?" > >> > >> Procedure has similar problems to maybe half the same degree. > >> > >> But then consider this: > >> > >> doSomething(IntReceiver receiver); > >> doSomethingElse(Receiver receiver); > >> > >> How much clearer could anything be? It's an int receiver: it receives > >> ints! Bonus: it has a much clearer relationship to Supplier. > >> > >> I have scoured the threads to find what the problems are that people had > >> with Receiver, and I haven't found any. Privately Brian guessed the > >> problem could be "confusion with receiver in the sense of method > >> receiver?" But that's not even a term 95% of normal Java developers > >> know or use. And even if so, the meaning of "an int receiver" is so > >> clear the mind doesn't even /go/ there. > >> > >> Agree/disagree/neutral? > >> > >> -- > >> Kevin Bourrillion | Java Librarian | Google, Inc. |kevinb at google.com > >> > > From paul.sandoz at oracle.com Thu Jan 24 01:33:18 2013 From: paul.sandoz at oracle.com (Paul Sandoz) Date: Thu, 24 Jan 2013 10:33:18 +0100 Subject: Function type naming conventions In-Reply-To: <51005405.7020306@oracle.com> References: <50E48919.2060408@oracle.com> <07F4CED6-B457-4573-B111-C7547FD15692@oracle.com> <0D1FD194-593A-46D2-BC11-5C7AA07A7420@oracle.com> <50F041D3.8070004@cs.oswego.edu> <2564DA2D-D372-45C1-A2B3-94DD214A3F5B@oracle.com> <50F04DDB.3050101@cs.oswego.edu> <510043DA.4040405@oracle.com> <5B350BAD-8F13-4C57-80ED-D2CA708729FE@gmail.com> <51005405.7020306@oracle.com> Message-ID: <339998A1-E993-43DE-A1AD-910AFAF2B084@oracle.com> On Jan 23, 2013, at 10:20 PM, Brian Goetz wrote: > Will do the first part tomorrow unless someone strongly objects. > > Open issues: > - Where multiple rules apply, do we prefer XY or XToY? XToY, i find the "To" is a useful demarcation. > - Do we like the "drop the arity prefix in case all are specialized" rule tweak? > If it is retained there is a more direct connection to the equivalent boxed version: ObjIntBiBlock -> BiBlock ObjIntToDoubleBiFunction -> BiFunction I suppose the alternative is to view Bi as shorthand for ObjObj and ObjObjToObj. Paul. From brian.goetz at oracle.com Thu Jan 24 09:33:34 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 24 Jan 2013 12:33:34 -0500 Subject: Stream method survey responses Message-ID: <5101706E.3030601@oracle.com> I've closed the survey on Stream methods. Here's the full data for people to browse on their own: https://www.surveymonkey.com/sr.aspx?sm=Zv5N7TvvYN_2fcpK7zP3vLnAuM6F_2fvluAFb0eNub28P_2bw_3d Here's a summary of comments. *PLEASE START SEPARATE, METHOD-SPECIFIC THREADS FOR NONTRIVIAL REPLIES*. filter -- No comments map -- No comments explode -- Lot's of comments here, clearly we're not done yet. Most comments had to do with confusing naming. uniqueElements -- "distinct" seemed preferred to "unique". Also, most other method names are verby; I propose changing to "removeDuplicates" or "filterDuplicates". sorted -- strong desire to add sorted() which uses natural order (or throws). Similar to above, should we replace "sorted" with "sort" to be more verby? forEach -- No comments. forEachUntil -- Sam prefers a content-based forEachUntil. Remi prefers we outlaw infinite streams. tee -- Clear confusion over the name. This method is mostly for debugging, since stream operations are jammed and therefore filtering, mapping, and reducing all happen at once, while users might wanting to see what happens after each stage. The tee() method lets users peek at values that go by. The name "tee" suggests it creates a new stream, which is wrong. Suggestions include "peek" or "tap". limit / substream -- Suggested to change method parameter names to not suggest indexing. toArray -- Need code examples to illustrate array supplier. Suggestion to use a Class literal instead of a lambda. (After extensive reflection, I think this is a bad idea. Everything else in this API uses constructor references, and we support array constructor references.) reduce -- suggest parameter rename identity -> base. Remi hates Optional. collect(Supplier, BiBlock, BiBlock) -- Don hates the name. Suggestion to define in terms of collect(Collector). collect(Collector) -- various naming suggestions collectUnordered -- suggestion to use forEach instead min/max -- Some distaste for Optional only being used when stream is empty. xxxMatch -- no comments findXxx -- Same comment about Optional for empty streams only sequential / parallel -- minor naming nits. From this, here's what I think is left to do: - More work on explode needed - Consider renaming uniqueElements / sorted / others for verbification - Rename tee - Parameter renaming From daniel.smith at oracle.com Thu Jan 24 10:52:51 2013 From: daniel.smith at oracle.com (Dan Smith) Date: Thu, 24 Jan 2013 11:52:51 -0700 Subject: Function type naming conventions In-Reply-To: <51005405.7020306@oracle.com> References: <50E48919.2060408@oracle.com> <07F4CED6-B457-4573-B111-C7547FD15692@oracle.com> <0D1FD194-593A-46D2-BC11-5C7AA07A7420@oracle.com> <50F041D3.8070004@cs.oswego.edu> <2564DA2D-D372-45C1-A2B3-94DD214A3F5B@oracle.com> <50F04DDB.3050101@cs.oswego.edu> <510043DA.4040405@oracle.com> <5B350BAD-8F13-4C57-80ED-D2CA708729FE@gmail.com> <51005405.7020306@oracle.com> Message-ID: <7AE4DE90-2F81-4D31-B7A8-99B8219B4CF7@oracle.com> On Jan 23, 2013, at 2:20 PM, Brian Goetz wrote: > Will do the first part tomorrow unless someone strongly objects. > > Open issues: > - Where multiple rules apply, do we prefer XY or XToY? I think it depends on the base type. ToIntSupplier sounds silly. It's a Supplier, so of course the "Int" qualifier means the thing being returned. (In fact, my inclination is to read this as a "supplier of ToInts", where a "ToInt" is a "ToIntFunction" -- i.e., "ToIntSupplier" = "() -> T -> int". IntToDoubleFunction, on the other hand, sounds right. An IntDoubleFunction sounds more like a BiFunction. Let me propose a slightly different convention: if the base type is parameterized in both its parameters and return, then the "To" prefix is mandatory. If not, "To" is not used. ?Dan From tim at peierls.net Thu Jan 24 11:00:04 2013 From: tim at peierls.net (Tim Peierls) Date: Thu, 24 Jan 2013 14:00:04 -0500 Subject: Function type naming conventions In-Reply-To: <7AE4DE90-2F81-4D31-B7A8-99B8219B4CF7@oracle.com> References: <50E48919.2060408@oracle.com> <07F4CED6-B457-4573-B111-C7547FD15692@oracle.com> <0D1FD194-593A-46D2-BC11-5C7AA07A7420@oracle.com> <50F041D3.8070004@cs.oswego.edu> <2564DA2D-D372-45C1-A2B3-94DD214A3F5B@oracle.com> <50F04DDB.3050101@cs.oswego.edu> <510043DA.4040405@oracle.com> <5B350BAD-8F13-4C57-80ED-D2CA708729FE@gmail.com> <51005405.7020306@oracle.com> <7AE4DE90-2F81-4D31-B7A8-99B8219B4CF7@oracle.com> Message-ID: On Thu, Jan 24, 2013 at 1:52 PM, Dan Smith wrote: > Let me propose a slightly different convention: if the base type is > parameterized in both its parameters and return, then the "To" prefix is > mandatory. If not, "To" is not used. > So: Function // Foo -> Bar ToIntFunction // Foo -> int Supplier // () -> Bar IntSupplier // () -> int Right? Works for me. --tim From joe.bowbeer at gmail.com Thu Jan 24 11:03:55 2013 From: joe.bowbeer at gmail.com (Joe Bowbeer) Date: Thu, 24 Jan 2013 11:03:55 -0800 Subject: Function type naming conventions In-Reply-To: <7AE4DE90-2F81-4D31-B7A8-99B8219B4CF7@oracle.com> References: <50E48919.2060408@oracle.com> <07F4CED6-B457-4573-B111-C7547FD15692@oracle.com> <0D1FD194-593A-46D2-BC11-5C7AA07A7420@oracle.com> <50F041D3.8070004@cs.oswego.edu> <2564DA2D-D372-45C1-A2B3-94DD214A3F5B@oracle.com> <50F04DDB.3050101@cs.oswego.edu> <510043DA.4040405@oracle.com> <5B350BAD-8F13-4C57-80ED-D2CA708729FE@gmail.com> <51005405.7020306@oracle.com> <7AE4DE90-2F81-4D31-B7A8-99B8219B4CF7@oracle.com> Message-ID: On Thu, Jan 24, 2013 at 10:52 AM, Dan Smith wrote: > Let me propose a slightly different convention: if the base type is > parameterized in both its parameters and return, then the "To" prefix is > mandatory. If not, "To" is not used. > > This works for me if the base name is descriptive enough. IntSupplier, IntConsumer, even IntBlock (now that I know what a Block is). > ?Dan > > From kevinb at google.com Thu Jan 24 11:53:42 2013 From: kevinb at google.com (Kevin Bourrillion) Date: Thu, 24 Jan 2013 11:53:42 -0800 Subject: Stream method survey responses In-Reply-To: <5101706E.3030601@oracle.com> References: <5101706E.3030601@oracle.com> Message-ID: On Thu, Jan 24, 2013 at 9:33 AM, Brian Goetz wrote: sorted -- strong desire to add sorted() which uses natural order (or > throws). +1, I think. Could this perhaps yield other benefits? For example, since users are strenuously urged to make compareTo() consistent with equals() -- or should be urged even more strenuously to do so -- can .uniqueElements().sorted() (in either order) do the smart thing? -- Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com From brian.goetz at oracle.com Thu Jan 24 12:02:09 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 24 Jan 2013 15:02:09 -0500 Subject: Op fusing (was: Stream method survey responses) In-Reply-To: References: <5101706E.3030601@oracle.com> Message-ID: <51019341.1000302@oracle.com> Yes, we can (though do not yet) fuse sorted+uniq into a single operation. On 1/24/2013 2:53 PM, Kevin Bourrillion wrote: > On Thu, Jan 24, 2013 at 9:33 AM, Brian Goetz > wrote: > > sorted -- strong desire to add sorted() which uses natural order (or > throws). > > > +1, I think. > > Could this perhaps yield other benefits? For example, since users are > strenuously urged to make compareTo() consistent with equals() -- or > should be urged even more strenuously to do so -- can > .uniqueElements().sorted() (in either order) do the smart thing? > > -- > Kevin Bourrillion | Java Librarian | Google, Inc. |kevinb at google.com > From kevinb at google.com Thu Jan 24 12:16:22 2013 From: kevinb at google.com (Kevin Bourrillion) Date: Thu, 24 Jan 2013 12:16:22 -0800 Subject: About the stream deduping method Message-ID: Brian's survey summary: > "distinct" seemed preferred to "unique". Sounds good, let this be the end of "unique" then. > Also, most other method names are verby; I propose changing to "removeDuplicates" or "filterDuplicates". *About the term "duplicate".* If it were always the case that the *first* occurrence of 'obj' in a stream is the one preserved, and all the rest thereby deemed to be the "duplicates" and excluded, then there would be a stronger reason to like the use of "duplicates" here. Since that's not the case, it's just "meh". *About "remove".* It sounds very mutative. Since Streams feel similar to Iterators, there is some potential for confusion. *About "filter".* The sense is inverted. filter() preserves the ones that * do* match the predicate. This would have to be something like "filterOutDuplicates" (yuck). *About consistency*. Right now among the chaining-style methods you've got imperative verbs (filter, map, limit, explode, perhaps sort), you've got nouns (substream), adjectives (parallel, sequential, sorted), and one that's not even based in anything grammatical (tee). (Side point: the set of terminal operations are no more consistent than this, either.) If consistency seems at all attainable (?), we would have to start by choosing which out of all this we want to be consistent *with*. *If I had to choose. *I would probably just go with: distinct(). That's all SQL needed, and I think it did fine with it. -- Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com From brian.goetz at oracle.com Thu Jan 24 12:28:30 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 24 Jan 2013 15:28:30 -0500 Subject: About the stream deduping method In-Reply-To: References: Message-ID: <5101996E.3030103@oracle.com> > *About the term "duplicate".* If it were always the case that the > /first/ occurrence of 'obj' in a stream is the one preserved, and all > the rest thereby deemed to be the "duplicates" and excluded, then there > would be a stronger reason to like the use of "duplicates" here. Since > that's not the case, it's just "meh". FWIW, we do make this promise for ordered streams (for unordered streams, by definition, you can't tell whether we adhere to this or not.) > *If I had to choose. *I would probably just go with: distinct(). > That's all SQL needed, and I think it did fine with it. Works for me. From kevinb at google.com Thu Jan 24 12:43:33 2013 From: kevinb at google.com (Kevin Bourrillion) Date: Thu, 24 Jan 2013 12:43:33 -0800 Subject: tee() Message-ID: tee() stands out like a sore thumb. I'm not surprised that Brian says "this method is mostly for debugging." It just feels very, very strange to let the user inject a side-effect into the middle of their stream somewhere, for mysterious hidden later execution *maybe*. If it really must stay, I think I do like "peek" or "observe" over "tee". But I would love to drop it. -- Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com From brian.goetz at oracle.com Thu Jan 24 12:53:10 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 24 Jan 2013 15:53:10 -0500 Subject: tee() In-Reply-To: References: Message-ID: <51019F36.9080309@oracle.com> peek() is fine with me, and far more descriptive. Don convinced me of the need for this one when he described experiences his team had adapting to lazy collections in their libraries. On 1/24/2013 3:43 PM, Kevin Bourrillion wrote: > tee() stands out like a sore thumb. I'm not surprised that Brian says > "this method is mostly for debugging." > > It just feels very, very strange to let the user inject a side-effect > into the middle of their stream somewhere, for mysterious hidden later > execution /maybe/. > > If it really must stay, I think I do like "peek" or "observe" over > "tee". But I would love to drop it. > > -- > Kevin Bourrillion | Java Librarian | Google, Inc. |kevinb at google.com > From tim at peierls.net Thu Jan 24 12:54:03 2013 From: tim at peierls.net (Tim Peierls) Date: Thu, 24 Jan 2013 15:54:03 -0500 Subject: tee() In-Reply-To: References: Message-ID: On Thu, Jan 24, 2013 at 3:43 PM, Kevin Bourrillion wrote: > It just feels very, very strange to let the user inject a side-effect into > the middle of their stream somewhere, for mysterious hidden later execution > *maybe*. > If tee stays in -- not saying it should -- what about a special argument type: Stream tee(Observer observer); // tee -> observingWith ? where public interface Observer /* does not extend Consumer/Sink/Block! */ { void observe(T value); } That distinction would be lost when using lambdas, but in other cases it could serve as a reminder that you can look but not touch. Maybe have a boolean return from observe, with false meaning "no longer interested in observing". That would prevent lambda writers from accidentally using something that looks like a Consumer/Sink/Block. --tim From joe.bowbeer at gmail.com Thu Jan 24 13:01:54 2013 From: joe.bowbeer at gmail.com (Joe Bowbeer) Date: Thu, 24 Jan 2013 13:01:54 -0800 Subject: tee() In-Reply-To: <51019F36.9080309@oracle.com> References: <51019F36.9080309@oracle.com> Message-ID: I agree that something like tee is needed for iterative development. The name suggests a bifurcation though. peek seems more accurate. On Jan 24, 2013 12:53 PM, "Brian Goetz" wrote: > peek() is fine with me, and far more descriptive. > > Don convinced me of the need for this one when he described experiences > his team had adapting to lazy collections in their libraries. > > On 1/24/2013 3:43 PM, Kevin Bourrillion wrote: > >> tee() stands out like a sore thumb. I'm not surprised that Brian says >> "this method is mostly for debugging." >> >> It just feels very, very strange to let the user inject a side-effect >> into the middle of their stream somewhere, for mysterious hidden later >> execution /maybe/. >> >> If it really must stay, I think I do like "peek" or "observe" over >> "tee". But I would love to drop it. >> >> -- >> Kevin Bourrillion | Java Librarian | Google, Inc. |kevinb at google.com >> >> > From kevinb at google.com Thu Jan 24 13:02:51 2013 From: kevinb at google.com (Kevin Bourrillion) Date: Thu, 24 Jan 2013 13:02:51 -0800 Subject: tee() In-Reply-To: <51019F36.9080309@oracle.com> References: <51019F36.9080309@oracle.com> Message-ID: On Thu, Jan 24, 2013 at 12:53 PM, Brian Goetz wrote: Don convinced me of the need for this one when he described experiences his > team had adapting to lazy collections in their libraries. Depending on the details of that conversation, I either buy it or don't buy it. :-) Lazy collections are a whole other story; Collection gets returned from API X and passed into API Y and people don't realize it; that lazy evaluation happens god-knows-when. Stream makes that a lot better. On 1/24/2013 3:43 PM, Kevin Bourrillion wrote: > >> tee() stands out like a sore thumb. I'm not surprised that Brian says >> "this method is mostly for debugging." >> >> It just feels very, very strange to let the user inject a side-effect >> into the middle of their stream somewhere, for mysterious hidden later >> execution /maybe/. >> >> >> If it really must stay, I think I do like "peek" or "observe" over >> "tee". But I would love to drop it. >> >> -- >> Kevin Bourrillion | Java Librarian | Google, Inc. |kevinb at google.com >> >> > -- Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com From brian.goetz at oracle.com Thu Jan 24 13:05:45 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 24 Jan 2013 16:05:45 -0500 Subject: tee() In-Reply-To: References: <51019F36.9080309@oracle.com> Message-ID: <5101A229.2070505@oracle.com> But streams *are* lazy. What confuses people is that when you say: stream.filter(...) .map(...) .reduce(...) the filtering, mapping, and reducing are all happening *at once*. Users will look for a place to stick a breakpoint "after the filtering" and come up empty. By putting a "peek()" call with debugging code, now they have a place to put a breakpoint or print stuff out, so they can debug complex pipelines stage by stage. On 1/24/2013 4:02 PM, Kevin Bourrillion wrote: > On Thu, Jan 24, 2013 at 12:53 PM, Brian Goetz > wrote: > > Don convinced me of the need for this one when he described > experiences his team had adapting to lazy collections in their > libraries. > > > Depending on the details of that conversation, I either buy it or don't > buy it. :-) > > Lazy collections are a whole other story; Collection gets returned > from API X and passed into API Y and people don't realize it; that lazy > evaluation happens god-knows-when. Stream makes that a lot better. > > > > On 1/24/2013 3:43 PM, Kevin Bourrillion wrote: > > tee() stands out like a sore thumb. I'm not surprised that > Brian says > "this method is mostly for debugging." > > It just feels very, very strange to let the user inject a > side-effect > into the middle of their stream somewhere, for mysterious hidden > later > execution /maybe/. > > > If it really must stay, I think I do like "peek" or "observe" over > "tee". But I would love to drop it. > > -- > Kevin Bourrillion | Java Librarian | Google, Inc. > |kevinb at google.com > > > > > > > -- > Kevin Bourrillion | Java Librarian | Google, Inc. |kevinb at google.com > From kevinb at google.com Thu Jan 24 13:10:00 2013 From: kevinb at google.com (Kevin Bourrillion) Date: Thu, 24 Jan 2013 13:10:00 -0800 Subject: tee() In-Reply-To: <5101A229.2070505@oracle.com> References: <51019F36.9080309@oracle.com> <5101A229.2070505@oracle.com> Message-ID: Okay, what I'm about to suggest may be ridiculous; I don't know. But when I want to debug, I want to debug without having to make a code change! I even want to debug straight in production. So what I would wish that I could do is attach my debugger, flip some system property or static boolean somewhere, and that causes all stream operations to be executed eagerly as they are specified. Now I can break anywhere I want and see what I want to see. On Thu, Jan 24, 2013 at 1:05 PM, Brian Goetz wrote: > But streams *are* lazy. What confuses people is that when you say: > > stream.filter(...) > .map(...) > .reduce(...) > > the filtering, mapping, and reducing are all happening *at once*. Users > will look for a place to stick a breakpoint "after the filtering" and come > up empty. By putting a "peek()" call with debugging code, now they have a > place to put a breakpoint or print stuff out, so they can debug complex > pipelines stage by stage. > > > On 1/24/2013 4:02 PM, Kevin Bourrillion wrote: > >> On Thu, Jan 24, 2013 at 12:53 PM, Brian Goetz > > wrote: >> >> Don convinced me of the need for this one when he described >> experiences his team had adapting to lazy collections in their >> libraries. >> >> >> Depending on the details of that conversation, I either buy it or don't >> buy it. :-) >> >> Lazy collections are a whole other story; Collection gets returned >> from API X and passed into API Y and people don't realize it; that lazy >> evaluation happens god-knows-when. Stream makes that a lot better. >> >> >> >> On 1/24/2013 3:43 PM, Kevin Bourrillion wrote: >> >> tee() stands out like a sore thumb. I'm not surprised that >> Brian says >> "this method is mostly for debugging." >> >> It just feels very, very strange to let the user inject a >> side-effect >> into the middle of their stream somewhere, for mysterious hidden >> later >> execution /maybe/. >> >> >> If it really must stay, I think I do like "peek" or "observe" over >> "tee". But I would love to drop it. >> >> -- >> Kevin Bourrillion | Java Librarian | Google, Inc. >> |kevinb at google.com >> > >> >> >> >> >> >> -- >> Kevin Bourrillion | Java Librarian | Google, Inc. |kevinb at google.com >> >> > -- Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com From martijnverburg at gmail.com Thu Jan 24 13:56:58 2013 From: martijnverburg at gmail.com (Martijn Verburg) Date: Thu, 24 Jan 2013 21:56:58 +0000 Subject: tee() In-Reply-To: References: <51019F36.9080309@oracle.com> <5101A229.2070505@oracle.com> Message-ID: Hi all, We were discussing this from the IDE implementors perspective as well. They certainly would want to be able to hook in between the lazy operations when asked to debug. Putting on my pure developers hat I'd be OK with that being a development time only thing, the IDE could even weave in the equivalent of a peek() call. Putting on my realists "Oh crap I need to see what's going on in Live" hat I certainly see Kevin's point.. I'll leave it to the experts as to whether that's feasible or not though, I suspect I'd think this through in a somewhat naive manner :-). On 24 January 2013 21:10, Kevin Bourrillion wrote: > Okay, what I'm about to suggest may be ridiculous; I don't know. > > But when I want to debug, I want to debug without having to make a code > change! I even want to debug straight in production. So what I would wish > that I could do is attach my debugger, flip some system property > or static boolean somewhere, and that causes all stream operations to be > executed eagerly as they are specified. Now I can break anywhere I want > and see what I want to see. > > > On Thu, Jan 24, 2013 at 1:05 PM, Brian Goetz wrote: > >> But streams *are* lazy. What confuses people is that when you say: >> >> stream.filter(...) >> .map(...) >> .reduce(...) >> >> the filtering, mapping, and reducing are all happening *at once*. Users >> will look for a place to stick a breakpoint "after the filtering" and come >> up empty. By putting a "peek()" call with debugging code, now they have a >> place to put a breakpoint or print stuff out, so they can debug complex >> pipelines stage by stage. >> >> >> On 1/24/2013 4:02 PM, Kevin Bourrillion wrote: >> >>> On Thu, Jan 24, 2013 at 12:53 PM, Brian Goetz >> > wrote: >>> >>> Don convinced me of the need for this one when he described >>> experiences his team had adapting to lazy collections in their >>> libraries. >>> >>> >>> Depending on the details of that conversation, I either buy it or don't >>> buy it. :-) >>> >>> Lazy collections are a whole other story; Collection gets returned >>> from API X and passed into API Y and people don't realize it; that lazy >>> evaluation happens god-knows-when. Stream makes that a lot better. >>> >>> >>> >>> On 1/24/2013 3:43 PM, Kevin Bourrillion wrote: >>> >>> tee() stands out like a sore thumb. I'm not surprised that >>> Brian says >>> "this method is mostly for debugging." >>> >>> It just feels very, very strange to let the user inject a >>> side-effect >>> into the middle of their stream somewhere, for mysterious hidden >>> later >>> execution /maybe/. >>> >>> >>> If it really must stay, I think I do like "peek" or "observe" over >>> "tee". But I would love to drop it. >>> >>> -- >>> Kevin Bourrillion | Java Librarian | Google, Inc. >>> |kevinb at google.com >>> > >>> >>> >>> >>> >>> >>> -- >>> Kevin Bourrillion | Java Librarian | Google, Inc. |kevinb at google.com >>> >>> >> > > > -- > Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com From brian.goetz at oracle.com Thu Jan 24 14:05:15 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 24 Jan 2013 17:05:15 -0500 Subject: Stream method survey responses In-Reply-To: <5101706E.3030601@oracle.com> References: <5101706E.3030601@oracle.com> Message-ID: <5101B01B.1030200@oracle.com> I have: - renamed tee to peek - renamed uniqueElements to distinct - added no-arg sorted - adjusted parameter names in limit, substream Remaining to do: - more discussion on explode On 1/24/2013 12:33 PM, Brian Goetz wrote: > I've closed the survey on Stream methods. Here's the full data for > people to browse on their own: > > https://www.surveymonkey.com/sr.aspx?sm=Zv5N7TvvYN_2fcpK7zP3vLnAuM6F_2fvluAFb0eNub28P_2bw_3d > > > Here's a summary of comments. *PLEASE START SEPARATE, METHOD-SPECIFIC > THREADS FOR NONTRIVIAL REPLIES*. > > filter -- No comments > > map -- No comments > > explode -- Lot's of comments here, clearly we're not done yet. Most > comments had to do with confusing naming. > > uniqueElements -- "distinct" seemed preferred to "unique". Also, most > other method names are verby; I propose changing to "removeDuplicates" > or "filterDuplicates". > > sorted -- strong desire to add sorted() which uses natural order (or > throws). Similar to above, should we replace "sorted" with "sort" to be > more verby? > > forEach -- No comments. > > forEachUntil -- Sam prefers a content-based forEachUntil. Remi prefers > we outlaw infinite streams. > > tee -- Clear confusion over the name. This method is mostly for > debugging, since stream operations are jammed and therefore filtering, > mapping, and reducing all happen at once, while users might wanting to > see what happens after each stage. The tee() method lets users peek at > values that go by. The name "tee" suggests it creates a new stream, > which is wrong. Suggestions include "peek" or "tap". > > limit / substream -- Suggested to change method parameter names to not > suggest indexing. > > toArray -- Need code examples to illustrate array supplier. Suggestion > to use a Class literal instead of a lambda. (After extensive > reflection, I think this is a bad idea. Everything else in this API > uses constructor references, and we support array constructor references.) > > reduce -- suggest parameter rename identity -> base. Remi hates Optional. > > collect(Supplier, BiBlock, BiBlock) -- Don hates the name. Suggestion > to define in terms of collect(Collector). > > collect(Collector) -- various naming suggestions > > collectUnordered -- suggestion to use forEach instead > > min/max -- Some distaste for Optional only being used when stream is empty. > > xxxMatch -- no comments > > findXxx -- Same comment about Optional for empty streams only > > sequential / parallel -- minor naming nits. > > > From this, here's what I think is left to do: > - More work on explode needed > - Consider renaming uniqueElements / sorted / others for verbification > - Rename tee > - Parameter renaming > From brian.goetz at oracle.com Thu Jan 24 14:16:48 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 24 Jan 2013 17:16:48 -0500 Subject: Function type naming conventions In-Reply-To: References: <50E48919.2060408@oracle.com> <07F4CED6-B457-4573-B111-C7547FD15692@oracle.com> <0D1FD194-593A-46D2-BC11-5C7AA07A7420@oracle.com> <50F041D3.8070004@cs.oswego.edu> <2564DA2D-D372-45C1-A2B3-94DD214A3F5B@oracle.com> <50F04DDB.3050101@cs.oswego.edu> <510043DA.4040405@oracle.com> <5B350BAD-8F13-4C57-80ED-D2CA708729FE@gmail.com> <51005405.7020306@oracle.com> <7AE4DE90-2F81-4D31-B7A8-99B8219B4CF7@oracle.com> Message-ID: <5101B2D0.4020204@oracle.com> OK, I've completed: - {Int,Long,Double}Function -> ToXxxFunction - {Int,Long,Double}BiFunction -> ToXxxBiFunction - Obj{Int,Long,Double}Function -> XxxFunction The remaining weird ones are: ObjIntBiBlock (T, int) -> void These could stay ObjIntBiBlock, or, with the "arity unnecessary if all args are specialized" rule tweak, could become: ObjIntBlock Thoughts? On 1/24/2013 2:03 PM, Joe Bowbeer wrote: > On Thu, Jan 24, 2013 at 10:52 AM, Dan Smith > wrote: > > Let me propose a slightly different convention: if the base type is > parameterized in both its parameters and return, then the "To" > prefix is mandatory. If not, "To" is not used. > > > This works for me if the base name is descriptive enough. > IntSupplier, IntConsumer, even IntBlock (now that I know what a Block is). > > ?Dan > > From joe.bowbeer at gmail.com Thu Jan 24 14:20:52 2013 From: joe.bowbeer at gmail.com (Joe Bowbeer) Date: Thu, 24 Jan 2013 14:20:52 -0800 Subject: Function type naming conventions In-Reply-To: <5101B2D0.4020204@oracle.com> References: <50E48919.2060408@oracle.com> <07F4CED6-B457-4573-B111-C7547FD15692@oracle.com> <0D1FD194-593A-46D2-BC11-5C7AA07A7420@oracle.com> <50F041D3.8070004@cs.oswego.edu> <2564DA2D-D372-45C1-A2B3-94DD214A3F5B@oracle.com> <50F04DDB.3050101@cs.oswego.edu> <510043DA.4040405@oracle.com> <5B350BAD-8F13-4C57-80ED-D2CA708729FE@gmail.com> <51005405.7020306@oracle.com> <7AE4DE90-2F81-4D31-B7A8-99B8219B4CF7@oracle.com> <5101B2D0.4020204@oracle.com> Message-ID: +1 ObjIntBlock (or a more descriptive "Block" name if one is selected) On Thu, Jan 24, 2013 at 2:16 PM, Brian Goetz wrote: > OK, I've completed: > > - {Int,Long,Double}Function -> ToXxxFunction > - {Int,Long,Double}BiFunction -> ToXxxBiFunction > - Obj{Int,Long,Double}Function -> XxxFunction > > The remaining weird ones are: > > ObjIntBiBlock (T, int) -> void > > These could stay ObjIntBiBlock, or, with the "arity unnecessary if all > args are specialized" rule tweak, could become: > > ObjIntBlock > > Thoughts? > > > On 1/24/2013 2:03 PM, Joe Bowbeer wrote: > >> On Thu, Jan 24, 2013 at 10:52 AM, Dan Smith > >> wrote: >> >> Let me propose a slightly different convention: if the base type is >> parameterized in both its parameters and return, then the "To" >> prefix is mandatory. If not, "To" is not used. >> >> >> This works for me if the base name is descriptive enough. >> IntSupplier, IntConsumer, even IntBlock (now that I know what a Block >> is). >> >> ?Dan >> >> >> From kevinb at google.com Thu Jan 24 14:43:45 2013 From: kevinb at google.com (Kevin Bourrillion) Date: Thu, 24 Jan 2013 14:43:45 -0800 Subject: explode() Message-ID: explode() is aptly named for what it did to my brain when I encountered it. :-) A few things are adding up to make it impenetrable. 1. The vast majority of the Stream API, save the obvious exceptions like forEach(), is solidly in the functional style. Collectors aren't really, but as long as I use the prepackaged ones, it *feels* fairly functional anyway. Now suddenly there's explode, which doesn't feel functional at all. Instead, I need to think of the bit of code I provide to it as an active participant in a pipeline. Things are fed to me, I feed things on down the line. This makes it different, and different is automatically confusing. This can't really be *corrected*, but seems worth nothing; maybe we can account for the difference somehow. 2. In attempting to learn it, I'm confronted with a type, Stream.Downstream, which I won't have heard of before that moment, and whose name reveals little. Is there any particular reason that Sink/Consumer, with its accept(T) method, should not also have "default" methods implementing accept(Collection) and accept(Stream) and accept(T[])? I can't think of any; those sound just plain handy. And if we added those, would there be enough value in preserving Downstream as an identically-signatured type? We could dispense with it, as I've done below. 3. Except sometimes there *is* value in having a special type even if its signature is identical to another. I suspect that a custom type could help quite a bit in this case: interface Exploder { /** * Maps {code input} to zero or more instances of R, sending these * to {@code resultConsumer} to be included in the results of * {@link Stream#explode(Exploder)}. */ void explode(T input, Consumer resultConsumer); } 4. Then there is the name "explode". It's... okay. "unpack"? Nah. It seems maybe 40% possible that a great name is out there eluding us.... -- Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com From kevinb at google.com Thu Jan 24 14:45:22 2013 From: kevinb at google.com (Kevin Bourrillion) Date: Thu, 24 Jan 2013 14:45:22 -0800 Subject: Function type naming conventions In-Reply-To: References: <50E48919.2060408@oracle.com> <07F4CED6-B457-4573-B111-C7547FD15692@oracle.com> <0D1FD194-593A-46D2-BC11-5C7AA07A7420@oracle.com> <50F041D3.8070004@cs.oswego.edu> <2564DA2D-D372-45C1-A2B3-94DD214A3F5B@oracle.com> <50F04DDB.3050101@cs.oswego.edu> <510043DA.4040405@oracle.com> <5B350BAD-8F13-4C57-80ED-D2CA708729FE@gmail.com> <51005405.7020306@oracle.com> <7AE4DE90-2F81-4D31-B7A8-99B8219B4CF7@oracle.com> <5101B2D0.4020204@oracle.com> Message-ID: Yeah, I'm fine with that too. Does that make it generally true that we always omit Bi when it's clearly implied? On Thu, Jan 24, 2013 at 2:20 PM, Joe Bowbeer wrote: > +1 ObjIntBlock (or a more descriptive "Block" name if one is selected) > > > > > On Thu, Jan 24, 2013 at 2:16 PM, Brian Goetz wrote: > >> OK, I've completed: >> >> - {Int,Long,Double}Function -> ToXxxFunction >> - {Int,Long,Double}BiFunction -> ToXxxBiFunction >> - Obj{Int,Long,Double}Function -> XxxFunction >> >> The remaining weird ones are: >> >> ObjIntBiBlock (T, int) -> void >> >> These could stay ObjIntBiBlock, or, with the "arity unnecessary if all >> args are specialized" rule tweak, could become: >> >> ObjIntBlock >> >> Thoughts? >> >> >> On 1/24/2013 2:03 PM, Joe Bowbeer wrote: >> >>> On Thu, Jan 24, 2013 at 10:52 AM, Dan Smith >> >> wrote: >>> >>> Let me propose a slightly different convention: if the base type is >>> parameterized in both its parameters and return, then the "To" >>> prefix is mandatory. If not, "To" is not used. >>> >>> >>> This works for me if the base name is descriptive enough. >>> IntSupplier, IntConsumer, even IntBlock (now that I know what a Block >>> is). >>> >>> ?Dan >>> >>> >>> > -- Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com From tim at peierls.net Thu Jan 24 14:47:59 2013 From: tim at peierls.net (Tim Peierls) Date: Thu, 24 Jan 2013 17:47:59 -0500 Subject: Function type naming conventions In-Reply-To: References: <50E48919.2060408@oracle.com> <07F4CED6-B457-4573-B111-C7547FD15692@oracle.com> <0D1FD194-593A-46D2-BC11-5C7AA07A7420@oracle.com> <50F041D3.8070004@cs.oswego.edu> <2564DA2D-D372-45C1-A2B3-94DD214A3F5B@oracle.com> <50F04DDB.3050101@cs.oswego.edu> <510043DA.4040405@oracle.com> <5B350BAD-8F13-4C57-80ED-D2CA708729FE@gmail.com> <51005405.7020306@oracle.com> <7AE4DE90-2F81-4D31-B7A8-99B8219B4CF7@oracle.com> <5101B2D0.4020204@oracle.com> Message-ID: On Thu, Jan 24, 2013 at 5:20 PM, Joe Bowbeer wrote: > +1 ObjIntBlock (or a more descriptive "Block" name if one is selected) > Agreed. Works well in conjunction with Dan Smith's suggestion ('if the base type is parameterized in both its parameters and return, then the "To" prefix is mandatory. If not, "To" is not used.'), omitting To and Bi where they aren't needed. --tim From brian.goetz at oracle.com Thu Jan 24 16:27:05 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 24 Jan 2013 19:27:05 -0500 Subject: explode() In-Reply-To: References: Message-ID: <5101D159.70701@oracle.com> So, you sort of came full circle to where we started, where we passed a Sink/Block/Consumer representing the downstream. The big complaint against that was "What if I already have a Collection? I have to iterate it myself?" I'm unwilling to lard Sink/Block/Consumer with these extra methods just to avoid the extra interface; they're not intrinsic enough to Sink/Block/Consumer to be worth the confusion. Though we could have an interface that extends S/B/C that adds the extra methods. Also, there's something stream-specific to the current Downstream; while the default for send(Stream) just delegates to forEach, this will fail if an element is mapped to an infinite stream. However, a better implementation of send(Stream) could prevent this. I want to leave that door open. So, adding this all up: interface Exploder { void explodeInto(T input, FragmentCollector sink); } interface FragmentCollector extends Sink { // or better name // with accept methods for array/stream/Iterable } Stream explode(Exploder) Is this any better than the status quo? On 1/24/2013 5:43 PM, Kevin Bourrillion wrote: > explode() is aptly named for what it did to my brain when I encountered > it. :-) > > A few things are adding up to make it impenetrable. > > 1. The vast majority of the Stream API, save the obvious exceptions like > forEach(), is solidly in the functional style. Collectors aren't really, > but as long as I use the prepackaged ones, it /feels/ fairly functional > anyway. Now suddenly there's explode, which doesn't feel functional at > all. Instead, I need to think of the bit of code I provide to it as an > active participant in a pipeline. Things are fed to me, I feed things on > down the line. This makes it different, and different is automatically > confusing. This can't really be /corrected/, but seems worth nothing; > maybe we can account for the difference somehow. > > 2. In attempting to learn it, I'm confronted with a type, > Stream.Downstream, which I won't have heard of before that moment, > and whose name reveals little. Is there any particular reason that > Sink/Consumer, with its accept(T) method, should not also have "default" > methods implementing accept(Collection) and accept(Stream) and > accept(T[])? I can't think of any; those sound just plain handy. And > if we added those, would there be enough value in preserving Downstream > as an identically-signatured type? We could dispense with it, as I've > done below. > > 3. Except sometimes there /is/ value in having a special type even if > its signature is identical to another. I suspect that a custom type > could help quite a bit in this case: > > interface Exploder { > /** > * Maps {code input} to zero or more instances of R, sending these > * to {@code resultConsumer} to be included in the results of > * {@link Stream#explode(Exploder)}. > */ > void explode(T input, Consumer resultConsumer); > } > > > 4. Then there is the name "explode". It's... okay. "unpack"? Nah. It > seems maybe 40% possible that a great name is out there eluding us.... > > > > -- > Kevin Bourrillion | Java Librarian | Google, Inc. |kevinb at google.com > From dl at cs.oswego.edu Thu Jan 24 16:47:48 2013 From: dl at cs.oswego.edu (Doug Lea) Date: Thu, 24 Jan 2013 19:47:48 -0500 Subject: tee() In-Reply-To: References: Message-ID: <5101D634.2060009@cs.oswego.edu> On 01/24/13 15:43, Kevin Bourrillion wrote: > If it really must stay, I think I do like "peek" or "observe" over "tee". But I > would love to drop it. > In case it is not too late to vote to drop this, I vote to drop it. And if too late, I vote to name it something other than any of these. -Doug From joe.bowbeer at gmail.com Thu Jan 24 17:00:22 2013 From: joe.bowbeer at gmail.com (Joe Bowbeer) Date: Thu, 24 Jan 2013 17:00:22 -0800 Subject: tee() In-Reply-To: <5101D634.2060009@cs.oswego.edu> References: <5101D634.2060009@cs.oswego.edu> Message-ID: I'm OK with peek() even though it has other meanings on IO streams. Is the name leak() is more to your liking? Joe On Thu, Jan 24, 2013 at 4:47 PM, Doug Lea
wrote: > On 01/24/13 15:43, Kevin Bourrillion wrote: > > If it really must stay, I think I do like "peek" or "observe" over "tee". >> But I >> would love to drop it. >> >> > In case it is not too late to vote to drop this, I vote to drop it. > And if too late, I vote to name it something other than any of these. > > -Doug > > > From dl at cs.oswego.edu Thu Jan 24 17:11:54 2013 From: dl at cs.oswego.edu (Doug Lea) Date: Thu, 24 Jan 2013 20:11:54 -0500 Subject: tee() In-Reply-To: References: <5101D634.2060009@cs.oswego.edu> Message-ID: <5101DBDA.9060705@cs.oswego.edu> On 01/24/13 20:00, Joe Bowbeer wrote: > I'm OK with peek() even though it has other meanings on IO streams. > > Is the name leak() is more to your liking? I can't think of a good name for the the little idiom of ....map(x -> { use(x); return x; })... Which seems more like an IDE thing than an API thing anyway. But then again, I don't like practically all of the convenience methods, so discount my vote accordingly. -Doug > > Joe > > > On Thu, Jan 24, 2013 at 4:47 PM, Doug Lea
> wrote: > > On 01/24/13 15:43, Kevin Bourrillion wrote: > > If it really must stay, I think I do like "peek" or "observe" over > "tee". But I > would love to drop it. > > > In case it is not too late to vote to drop this, I vote to drop it. > And if too late, I vote to name it something other than any of these. > > -Doug > > > From kevinb at google.com Thu Jan 24 17:36:04 2013 From: kevinb at google.com (Kevin Bourrillion) Date: Thu, 24 Jan 2013 17:36:04 -0800 Subject: explode() In-Reply-To: <5101D159.70701@oracle.com> References: <5101D159.70701@oracle.com> Message-ID: On Thu, Jan 24, 2013 at 4:27 PM, Brian Goetz wrote: So, you sort of came full circle to where we started, where we passed a > Sink/Block/Consumer representing the downstream. The big complaint against > that was "What if I already have a Collection? I have to iterate it > myself?" > > I'm unwilling to lard Sink/Block/Consumer with these extra methods just to > avoid the extra interface; No, my point is that these methods are useful and natural and they belong in Consumer/Whatever, regardless. It's both convenient for the caller, and gives implementors opportunities for greater efficiency. (I don't see how this is full circle unless that exact idea was what was discussed, which it doesn't sound like.) they're not intrinsic enough to Sink/Block/Consumer to be worth the > confusion. Though we could have an interface that extends S/B/C that adds > the extra methods. > I would disagree with such a child interface, because the two are the same thing. > Also, there's something stream-specific to the current Downstream; while > the default for send(Stream) just delegates to forEach, this will fail if > an element is mapped to an infinite stream. However, a better > implementation of send(Stream) could prevent this. I want to leave that > door open. > Okay, then why not leave that door open for all Consumers, as I propose? > So, adding this all up: > > interface Exploder { > void explodeInto(T input, FragmentCollector sink); > } > > interface FragmentCollector extends Sink { // or better name > // with accept methods for array/stream/Iterable > } > > Stream explode(Exploder) > > Is this any better than the status quo? No, I dislike both. :-) And haven't yet heard any clear argument against my suggestion. I hope to hear others' opinions. > > On 1/24/2013 5:43 PM, Kevin Bourrillion wrote: > >> explode() is aptly named for what it did to my brain when I encountered >> it. :-) >> >> A few things are adding up to make it impenetrable. >> >> 1. The vast majority of the Stream API, save the obvious exceptions like >> forEach(), is solidly in the functional style. Collectors aren't really, >> but as long as I use the prepackaged ones, it /feels/ fairly functional >> >> anyway. Now suddenly there's explode, which doesn't feel functional at >> all. Instead, I need to think of the bit of code I provide to it as an >> active participant in a pipeline. Things are fed to me, I feed things on >> down the line. This makes it different, and different is automatically >> confusing. This can't really be /corrected/, but seems worth nothing; >> >> maybe we can account for the difference somehow. >> >> 2. In attempting to learn it, I'm confronted with a type, >> Stream.Downstream, which I won't have heard of before that moment, >> and whose name reveals little. Is there any particular reason that >> Sink/Consumer, with its accept(T) method, should not also have "default" >> methods implementing accept(Collection) and accept(Stream) and >> accept(T[])? I can't think of any; those sound just plain handy. And >> if we added those, would there be enough value in preserving Downstream >> as an identically-signatured type? We could dispense with it, as I've >> done below. >> >> 3. Except sometimes there /is/ value in having a special type even if >> >> its signature is identical to another. I suspect that a custom type >> could help quite a bit in this case: >> >> interface Exploder { >> /** >> * Maps {code input} to zero or more instances of R, sending these >> * to {@code resultConsumer} to be included in the results of >> * {@link Stream#explode(Exploder)}. >> */ >> void explode(T input, Consumer resultConsumer); >> } >> >> >> 4. Then there is the name "explode". It's... okay. "unpack"? Nah. It >> seems maybe 40% possible that a great name is out there eluding us.... >> >> >> >> -- >> Kevin Bourrillion | Java Librarian | Google, Inc. |kevinb at google.com >> >> > -- Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com From joe.bowbeer at gmail.com Thu Jan 24 18:05:58 2013 From: joe.bowbeer at gmail.com (Joe Bowbeer) Date: Thu, 24 Jan 2013 18:05:58 -0800 Subject: explode() In-Reply-To: References: <5101D159.70701@oracle.com> Message-ID: Kevin, To clarify this for me, how would Arul's sample look with your proposed change? https://github.com/aruld/java-oneliners/blob/master/src/main/java/com/aruld/oneliners/Item10.java // Merge tracks from all albums List allTracks = albums.stream() .explode((Downstream downstream, Album element) -> downstream.send(element.tracks)) .collect(Collectors.toList()); --Joe On Thu, Jan 24, 2013 at 5:36 PM, Kevin Bourrillion wrote: > On Thu, Jan 24, 2013 at 4:27 PM, Brian Goetz wrote: > > So, you sort of came full circle to where we started, where we passed a >> Sink/Block/Consumer representing the downstream. The big complaint against >> that was "What if I already have a Collection? I have to iterate it >> myself?" >> >> I'm unwilling to lard Sink/Block/Consumer with these extra methods just >> to avoid the extra interface; > > > No, my point is that these methods are useful and natural and they belong > in Consumer/Whatever, regardless. It's both convenient for the caller, and > gives implementors opportunities for greater efficiency. (I don't see how > this is full circle unless that exact idea was what was discussed, which it > doesn't sound like.) > > > they're not intrinsic enough to Sink/Block/Consumer to be worth the >> confusion. Though we could have an interface that extends S/B/C that adds >> the extra methods. >> > > I would disagree with such a child interface, because the two are the same > thing. > > > >> Also, there's something stream-specific to the current Downstream; while >> the default for send(Stream) just delegates to forEach, this will fail if >> an element is mapped to an infinite stream. However, a better >> implementation of send(Stream) could prevent this. I want to leave that >> door open. >> > > Okay, then why not leave that door open for all Consumers, as I propose? > > > >> So, adding this all up: >> >> interface Exploder { >> void explodeInto(T input, FragmentCollector sink); >> } >> >> interface FragmentCollector extends Sink { // or better name >> // with accept methods for array/stream/Iterable >> } >> >> Stream explode(Exploder) >> >> Is this any better than the status quo? > > > No, I dislike both. :-) And haven't yet heard any clear argument against > my suggestion. I hope to hear others' opinions. > > > >> >> On 1/24/2013 5:43 PM, Kevin Bourrillion wrote: >> >>> explode() is aptly named for what it did to my brain when I encountered >>> it. :-) >>> >>> A few things are adding up to make it impenetrable. >>> >>> 1. The vast majority of the Stream API, save the obvious exceptions like >>> forEach(), is solidly in the functional style. Collectors aren't really, >>> but as long as I use the prepackaged ones, it /feels/ fairly functional >>> >>> anyway. Now suddenly there's explode, which doesn't feel functional at >>> all. Instead, I need to think of the bit of code I provide to it as an >>> active participant in a pipeline. Things are fed to me, I feed things on >>> down the line. This makes it different, and different is automatically >>> confusing. This can't really be /corrected/, but seems worth nothing; >>> >>> maybe we can account for the difference somehow. >>> >>> 2. In attempting to learn it, I'm confronted with a type, >>> Stream.Downstream, which I won't have heard of before that moment, >>> and whose name reveals little. Is there any particular reason that >>> Sink/Consumer, with its accept(T) method, should not also have "default" >>> methods implementing accept(Collection) and accept(Stream) and >>> accept(T[])? I can't think of any; those sound just plain handy. And >>> if we added those, would there be enough value in preserving Downstream >>> as an identically-signatured type? We could dispense with it, as I've >>> done below. >>> >>> 3. Except sometimes there /is/ value in having a special type even if >>> >>> its signature is identical to another. I suspect that a custom type >>> could help quite a bit in this case: >>> >>> interface Exploder { >>> /** >>> * Maps {code input} to zero or more instances of R, sending these >>> * to {@code resultConsumer} to be included in the results of >>> * {@link Stream#explode(Exploder)}. >>> */ >>> void explode(T input, Consumer resultConsumer); >>> } >>> >>> >>> 4. Then there is the name "explode". It's... okay. "unpack"? Nah. It >>> seems maybe 40% possible that a great name is out there eluding us.... >>> >>> >>> >>> -- >>> Kevin Bourrillion | Java Librarian | Google, Inc. |kevinb at google.com >>> >>> >> > > > -- > Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com > From kevinb at google.com Thu Jan 24 18:26:15 2013 From: kevinb at google.com (Kevin Bourrillion) Date: Thu, 24 Jan 2013 18:26:15 -0800 Subject: explode() In-Reply-To: References: <5101D159.70701@oracle.com> Message-ID: Oh, it would not look that different: List allTracks = albums.stream() .explode((Album element, Consumer into) -> into.accept(element.tracks)) .collect(Collectors.toList()); On Thu, Jan 24, 2013 at 6:05 PM, Joe Bowbeer wrote: > Kevin, > > To clarify this for me, how would Arul's sample look with your proposed > change? > > > https://github.com/aruld/java-oneliners/blob/master/src/main/java/com/aruld/oneliners/Item10.java > > > > // Merge tracks from all albums > List allTracks = albums.stream() > > .explode((Downstream downstream, Album element) -> downstream.send(element.tracks)) > > .collect(Collectors.toList()); > > > > --Joe > > > On Thu, Jan 24, 2013 at 5:36 PM, Kevin Bourrillion wrote: > >> On Thu, Jan 24, 2013 at 4:27 PM, Brian Goetz wrote: >> >> So, you sort of came full circle to where we started, where we passed a >>> Sink/Block/Consumer representing the downstream. The big complaint against >>> that was "What if I already have a Collection? I have to iterate it >>> myself?" >>> >>> I'm unwilling to lard Sink/Block/Consumer with these extra methods just >>> to avoid the extra interface; >> >> >> No, my point is that these methods are useful and natural and they belong >> in Consumer/Whatever, regardless. It's both convenient for the caller, and >> gives implementors opportunities for greater efficiency. (I don't see how >> this is full circle unless that exact idea was what was discussed, which it >> doesn't sound like.) >> >> >> they're not intrinsic enough to Sink/Block/Consumer to be worth the >>> confusion. Though we could have an interface that extends S/B/C that adds >>> the extra methods. >>> >> >> I would disagree with such a child interface, because the two are the >> same thing. >> >> >> >>> Also, there's something stream-specific to the current Downstream; while >>> the default for send(Stream) just delegates to forEach, this will fail if >>> an element is mapped to an infinite stream. However, a better >>> implementation of send(Stream) could prevent this. I want to leave that >>> door open. >>> >> >> Okay, then why not leave that door open for all Consumers, as I propose? >> >> >> >>> So, adding this all up: >>> >>> interface Exploder { >>> void explodeInto(T input, FragmentCollector sink); >>> } >>> >>> interface FragmentCollector extends Sink { // or better name >>> // with accept methods for array/stream/Iterable >>> } >>> >>> Stream explode(Exploder) >>> >>> Is this any better than the status quo? >> >> >> No, I dislike both. :-) And haven't yet heard any clear argument against >> my suggestion. I hope to hear others' opinions. >> >> >> >>> >>> On 1/24/2013 5:43 PM, Kevin Bourrillion wrote: >>> >>>> explode() is aptly named for what it did to my brain when I encountered >>>> it. :-) >>>> >>>> A few things are adding up to make it impenetrable. >>>> >>>> 1. The vast majority of the Stream API, save the obvious exceptions like >>>> forEach(), is solidly in the functional style. Collectors aren't really, >>>> but as long as I use the prepackaged ones, it /feels/ fairly functional >>>> >>>> anyway. Now suddenly there's explode, which doesn't feel functional at >>>> all. Instead, I need to think of the bit of code I provide to it as an >>>> active participant in a pipeline. Things are fed to me, I feed things on >>>> down the line. This makes it different, and different is automatically >>>> confusing. This can't really be /corrected/, but seems worth nothing; >>>> >>>> maybe we can account for the difference somehow. >>>> >>>> 2. In attempting to learn it, I'm confronted with a type, >>>> Stream.Downstream, which I won't have heard of before that moment, >>>> and whose name reveals little. Is there any particular reason that >>>> Sink/Consumer, with its accept(T) method, should not also have "default" >>>> methods implementing accept(Collection) and accept(Stream) and >>>> accept(T[])? I can't think of any; those sound just plain handy. And >>>> if we added those, would there be enough value in preserving Downstream >>>> as an identically-signatured type? We could dispense with it, as I've >>>> done below. >>>> >>>> 3. Except sometimes there /is/ value in having a special type even if >>>> >>>> its signature is identical to another. I suspect that a custom type >>>> could help quite a bit in this case: >>>> >>>> interface Exploder { >>>> /** >>>> * Maps {code input} to zero or more instances of R, sending >>>> these >>>> * to {@code resultConsumer} to be included in the results of >>>> * {@link Stream#explode(Exploder)}. >>>> */ >>>> void explode(T input, Consumer resultConsumer); >>>> } >>>> >>>> >>>> 4. Then there is the name "explode". It's... okay. "unpack"? Nah. It >>>> seems maybe 40% possible that a great name is out there eluding us.... >>>> >>>> >>>> >>>> -- >>>> Kevin Bourrillion | Java Librarian | Google, Inc. |kevinb at google.com >>>> >>>> >>> >> >> >> -- >> Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com >> > > -- Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com From tim at peierls.net Thu Jan 24 19:34:17 2013 From: tim at peierls.net (Tim Peierls) Date: Thu, 24 Jan 2013 22:34:17 -0500 Subject: explode() In-Reply-To: References: <5101D159.70701@oracle.com> Message-ID: On Thu, Jan 24, 2013 at 9:26 PM, Kevin Bourrillion wrote: > Oh, it would not look that different: > > List allTracks = albums.stream() > .explode((Album element, Consumer into) -> > into.accept(element.tracks)) > .collect(Collectors.toList()); > Nice! The use of the name Consumer helps a lot. explode(Exploder) is better than explode(Downstream). Other pairs from the thesaurus: distribute(Distributor) allocate(Allocator) --tim From paul.sandoz at oracle.com Fri Jan 25 00:44:48 2013 From: paul.sandoz at oracle.com (Paul Sandoz) Date: Fri, 25 Jan 2013 09:44:48 +0100 Subject: tee() In-Reply-To: <5101DBDA.9060705@cs.oswego.edu> References: <5101D634.2060009@cs.oswego.edu> <5101DBDA.9060705@cs.oswego.edu> Message-ID: On Jan 25, 2013, at 2:11 AM, Doug Lea
wrote: > On 01/24/13 20:00, Joe Bowbeer wrote: >> I'm OK with peek() even though it has other meanings on IO streams. >> >> Is the name leak() is more to your liking? > > I can't think of a good name for the the little idiom of > ....map(x -> { use(x); return x; })... > Although map affects the properties downstream in ways that tee/peek (observe?) does not. If the upstream is known to be sorted and/or distinct then the map operation will clear those properties so the downstream is not known to be sorted and/or distinct. Paul. From dl at cs.oswego.edu Fri Jan 25 04:51:18 2013 From: dl at cs.oswego.edu (Doug Lea) Date: Fri, 25 Jan 2013 07:51:18 -0500 Subject: tee() In-Reply-To: References: <5101D634.2060009@cs.oswego.edu> <5101DBDA.9060705@cs.oswego.edu> Message-ID: <51027FC6.8080603@cs.oswego.edu> On 01/25/13 03:44, Paul Sandoz wrote: > > On Jan 25, 2013, at 2:11 AM, Doug Lea
wrote: > >> On 01/24/13 20:00, Joe Bowbeer wrote: >>> I'm OK with peek() even though it has other meanings on IO streams. >>> >>> Is the name leak() is more to your liking? >> >> I can't think of a good name for the the little idiom of >> ....map(x -> { use(x); return x; })... >> > > Although map affects the properties downstream in ways that tee/peek (observe?) does not. > > If the upstream is known to be sorted and/or distinct then the map operation will clear those properties so the downstream is not known to be sorted and/or distinct. In which case tee() needs more than the usual number of disclaimers about please not side-effecting the source... And/or map() needs disclaimers about not knowing about property-preserving-ness (which leads into some familiar territory for Brian, Paul and me: who knows/tracks such properties?) -Doug From brian.goetz at oracle.com Fri Jan 25 07:59:04 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 25 Jan 2013 10:59:04 -0500 Subject: tee() In-Reply-To: <51027FC6.8080603@cs.oswego.edu> References: <5101D634.2060009@cs.oswego.edu> <5101DBDA.9060705@cs.oswego.edu> <51027FC6.8080603@cs.oswego.edu> Message-ID: <5102ABC8.7040807@oracle.com> > In which case tee() needs more than the usual number of disclaimers about > please not side-effecting the source... The exact same number of disclaimers -- it is never OK -- but we can remind everyonhe that the purpose of the peek() method is for debugging, not for side-effecting anything that counts. Even less so than forEach. From brian.goetz at oracle.com Fri Jan 25 08:44:55 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 25 Jan 2013 11:44:55 -0500 Subject: Hopefully the last message on Block Message-ID: <5102B687.3040904@oracle.com> The poll results are in: https://www.surveymonkey.com/sr.aspx?sm=7JZdXUDIBZ6bFD_2bCY7jEghy5Rv_2b7iObJtf9AUFPp63k_3d My interpretation of the results: Supplier / Block (the status quo): OK, people clearly don't like it. Supplier / Sink (change Block to Sink): Better than above -- more like, less hate -- but not overwhelming support. Clearly dominates #1. Supplier / Consumer (change Block to Consumer): Strong support, little hate. Clearly dominates #1 and #2. Source / Sink: Strong support (arguably the strongest, but see below.) Producer / Consumer: Comparable to Supplier / Consumer, but with some caveats. Any of the last three choices would be supported by the data. It was commented that Source/Supplier is better than Producer because Producer more strongly implies a *new* item is being produced (same reason we switched Factory -> Supplier.) I think this is a strong enough argument to remove it from the group of supportable candidates. Source/Sink had more votes, but if you remove the Oracle votes (of which there were a lot), its pretty clear that this distorted the results. (Use Crosstab to filter the results by Collector; the Oracle folks all used the web collector because our mail system seems to filter out the surveys.) So I think the strongest choice is Supplier / Consumer (simply renaming Block to Consumer.) People like Supplier already, and seem to be able to deal with Consumer. If you've got extra paint left, there will be more bikesheds coming, don't worry. From kevinb at google.com Fri Jan 25 08:47:52 2013 From: kevinb at google.com (Kevin Bourrillion) Date: Fri, 25 Jan 2013 08:47:52 -0800 Subject: Hopefully the last message on Block In-Reply-To: <5102B687.3040904@oracle.com> References: <5102B687.3040904@oracle.com> Message-ID: Fabulous. Good-bye, Block. We thank you for your term of service. On Fri, Jan 25, 2013 at 8:44 AM, Brian Goetz wrote: > The poll results are in: > > > https://www.surveymonkey.com/**sr.aspx?sm=7JZdXUDIBZ6bFD_**2bCY7jEghy5Rv_* > *2b7iObJtf9AUFPp63k_3d > > My interpretation of the results: > > Supplier / Block (the status quo): OK, people clearly don't like it. > > Supplier / Sink (change Block to Sink): Better than above -- more like, > less hate -- but not overwhelming support. Clearly dominates #1. > > Supplier / Consumer (change Block to Consumer): Strong support, little > hate. Clearly dominates #1 and #2. > > Source / Sink: Strong support (arguably the strongest, but see below.) > > Producer / Consumer: Comparable to Supplier / Consumer, but with some > caveats. > > > Any of the last three choices would be supported by the data. It was > commented that Source/Supplier is better than Producer because Producer > more strongly implies a *new* item is being produced (same reason we > switched Factory -> Supplier.) I think this is a strong enough argument to > remove it from the group of supportable candidates. > > Source/Sink had more votes, but if you remove the Oracle votes (of which > there were a lot), its pretty clear that this distorted the results. (Use > Crosstab to filter the results by Collector; the Oracle folks all used the > web collector because our mail system seems to filter out the surveys.) > > So I think the strongest choice is Supplier / Consumer (simply renaming > Block to Consumer.) People like Supplier already, and seem to be able to > deal with Consumer. > > If you've got extra paint left, there will be more bikesheds coming, don't > worry. > > -- Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com From brian.goetz at oracle.com Fri Jan 25 09:43:40 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 25 Jan 2013 12:43:40 -0500 Subject: Fwd: hg: lambda/lambda/langtools: Enhancement: switch to graph inference by default when using -source 8 In-Reply-To: <20130125173145.CFF3F4756F@hg.openjdk.java.net> References: <20130125173145.CFF3F4756F@hg.openjdk.java.net> Message-ID: <5102C44C.9010503@oracle.com> This is a pretty important milestone for the compiler, turning on a number of type inference improvements. This should help with issues like the need for explicit type witnesses (Foo.bar) and explicit type parameters on ctor refs (Foo::new instead of Foo::new). -------- Original Message -------- Subject: hg: lambda/lambda/langtools: Enhancement: switch to graph inference by default when using -source 8 Date: Fri, 25 Jan 2013 17:31:37 +0000 From: maurizio.cimadamore at oracle.com To: lambda-dev at openjdk.java.net Changeset: 34105e37fa55 Author: mcimadamore Date: 2013-01-25 17:31 +0000 URL: http://hg.openjdk.java.net/lambda/lambda/langtools/rev/34105e37fa55 Enhancement: switch to graph inference by default when using -source 8 The graph inference engine allows for more precise inference results in nested method call contexts, by allowing propagation of inference constraints (and stuck expression) outwards. In case of problems, the old inference scheme can be enabled by using the flag '-XDuseLegacyInference'. ! src/share/classes/com/sun/tools/javac/code/Type.java ! src/share/classes/com/sun/tools/javac/comp/Attr.java ! src/share/classes/com/sun/tools/javac/comp/DeferredAttr.java ! src/share/classes/com/sun/tools/javac/comp/GraphInfer.java ! src/share/classes/com/sun/tools/javac/comp/Infer.java ! src/share/classes/com/sun/tools/javac/comp/InferFactory.java ! src/share/classes/com/sun/tools/javac/comp/LegacyInfer.java ! src/share/classes/com/sun/tools/javac/comp/Resolve.java ! src/share/classes/com/sun/tools/javac/main/Main.java ! src/share/classes/com/sun/tools/javac/util/List.java ! test/tools/javac/6758789/T6758789b.out ! test/tools/javac/Diagnostics/6799605/T6799605.out ! test/tools/javac/diags/examples/CantApplyDiamond1.java ! test/tools/javac/diags/examples/InferredDoNotConformToEq.java ! test/tools/javac/diags/examples/InferredDoNotConformToUpper.java ! test/tools/javac/diags/examples/WhereFreshTvar.java ! test/tools/javac/generics/7015430/T7015430.out ! test/tools/javac/generics/7151802/T7151802.out ! test/tools/javac/generics/diamond/neg/Neg06.out ! test/tools/javac/generics/inference/6278587/T6278587Neg.java ! test/tools/javac/generics/inference/6638712/T6638712d.out ! test/tools/javac/generics/inference/6638712/T6638712e.out ! test/tools/javac/generics/inference/7154127/T7154127.java ! test/tools/javac/generics/inference/7154127/T7154127.out ! test/tools/javac/generics/inference/7177306/T7177306a.out ! test/tools/javac/generics/inference/7177306/T7177306e.java ! test/tools/javac/generics/inference/7177306/T7177306e.out ! test/tools/javac/generics/odersky/BadTest4.java ! test/tools/javac/lambda/LambdaParserTest.java ! test/tools/javac/lambda/TargetType10.java ! test/tools/javac/lambda/TargetType10.out ! test/tools/javac/lambda/TargetType14.out ! test/tools/javac/lambda/TargetType20.java ! test/tools/javac/lambda/TargetType20.out ! test/tools/javac/lambda/TargetType28.out ! test/tools/javac/lambda/TargetType50.java ! test/tools/javac/lambda/TargetType50.out ! test/tools/javac/lambda/mostSpecific/StructuralMostSpecificTest.java ! test/tools/javac/multicatch/Neg07.out From brian.goetz at oracle.com Fri Jan 25 10:11:18 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 25 Jan 2013 13:11:18 -0500 Subject: Fwd: enhanced type-inference In-Reply-To: <5102C593.5070805@oracle.com> References: <5102C593.5070805@oracle.com> Message-ID: <5102CAC6.4050108@oracle.com> More info on new type inference. -------- Original Message -------- Subject: enhanced type-inference Date: Fri, 25 Jan 2013 17:49:07 +0000 From: Maurizio Cimadamore Organization: Oracle To: lambda-dev Dear lambdackers, I've just pushed a patch that enables a more general inference support for nested generic method calls/stuck expressions. This scheme has been available for a while in lambda-repo (when using the hidden flag -XDuseGraphInference), but we have now decided it's time to flip the switch and make it the default when using JDK 8. In the past few weeks I've been hunting down as many bugs in the new inference scheme as possible, in order to provide a smooth transition from the old world to the new one. I hope the transition is indeed smooth - but, given the nature of the change, I also expect bugs to pop up here and there, so please, keep throwing the kitchen sink at javac and report your experience back to us; without your valuable feedback and dedication we would never have gotten thus far. Example of things that now work: Stream si = ... List l1 = si.into(new ArrayList<>()); //not really - too late for that ;-) List l2 = si.collect(toList()); List l3 = si.collect(toCollection(ArrayList::new)); Thanks Maurizio From sam at sampullara.com Fri Jan 25 11:02:06 2013 From: sam at sampullara.com (Sam Pullara) Date: Fri, 25 Jan 2013 11:02:06 -0800 Subject: tee() In-Reply-To: <5102ABC8.7040807@oracle.com> References: <5101D634.2060009@cs.oswego.edu> <5101DBDA.9060705@cs.oswego.edu> <51027FC6.8080603@cs.oswego.edu> <5102ABC8.7040807@oracle.com> Message-ID: Why not just make a true tee() that duplicates the stream and you can use that for debugging? Sam On Fri, Jan 25, 2013 at 7:59 AM, Brian Goetz wrote: >> In which case tee() needs more than the usual number of disclaimers about >> please not side-effecting the source... > > > The exact same number of disclaimers -- it is never OK -- but we can remind > everyonhe that the purpose of the peek() method is for debugging, not for > side-effecting anything that counts. Even less so than forEach. > > From brian.goetz at oracle.com Fri Jan 25 11:08:59 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 25 Jan 2013 14:08:59 -0500 Subject: tee() In-Reply-To: References: <5101D634.2060009@cs.oswego.edu> <5101DBDA.9060705@cs.oswego.edu> <51027FC6.8080603@cs.oswego.edu> <5102ABC8.7040807@oracle.com> Message-ID: <5102D84B.1060200@oracle.com> Try writing a parallel version that consumes from an infinite IO source, and you'll see why :) On 1/25/2013 2:02 PM, Sam Pullara wrote: > Why not just make a true tee() that duplicates the stream and you can > use that for debugging? > > Sam > > On Fri, Jan 25, 2013 at 7:59 AM, Brian Goetz wrote: >>> In which case tee() needs more than the usual number of disclaimers about >>> please not side-effecting the source... >> >> >> The exact same number of disclaimers -- it is never OK -- but we can remind >> everyonhe that the purpose of the peek() method is for debugging, not for >> side-effecting anything that counts. Even less so than forEach. >> >> From kevinb at google.com Tue Jan 29 08:48:03 2013 From: kevinb at google.com (Kevin Bourrillion) Date: Tue, 29 Jan 2013 08:48:03 -0800 Subject: Function type naming conventions In-Reply-To: References: <50E48919.2060408@oracle.com> <07F4CED6-B457-4573-B111-C7547FD15692@oracle.com> <0D1FD194-593A-46D2-BC11-5C7AA07A7420@oracle.com> <50F041D3.8070004@cs.oswego.edu> <2564DA2D-D372-45C1-A2B3-94DD214A3F5B@oracle.com> <50F04DDB.3050101@cs.oswego.edu> <510043DA.4040405@oracle.com> <5B350BAD-8F13-4C57-80ED-D2CA708729FE@gmail.com> <51005405.7020306@oracle.com> <7AE4DE90-2F81-4D31-B7A8-99B8219B4CF7@oracle.com> <5101B2D0.4020204@oracle.com> Message-ID: Does anyone have a complete and current taxonomy of where exactly we've ended up with all this (leaving out all the history of how we got here)? On Thu, Jan 24, 2013 at 2:47 PM, Tim Peierls wrote: > On Thu, Jan 24, 2013 at 5:20 PM, Joe Bowbeer wrote: > >> +1 ObjIntBlock (or a more descriptive "Block" name if one is selected) >> > > Agreed. > > Works well in conjunction with Dan Smith's suggestion ('if the base type > is parameterized in both its parameters and return, then the "To" prefix is > mandatory. If not, "To" is not used.'), omitting To and Bi where they > aren't needed. > > --tim > -- Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com From paul.sandoz at oracle.com Tue Jan 29 09:06:51 2013 From: paul.sandoz at oracle.com (Paul Sandoz) Date: Tue, 29 Jan 2013 18:06:51 +0100 Subject: Function type naming conventions In-Reply-To: References: <50E48919.2060408@oracle.com> <07F4CED6-B457-4573-B111-C7547FD15692@oracle.com> <0D1FD194-593A-46D2-BC11-5C7AA07A7420@oracle.com> <50F041D3.8070004@cs.oswego.edu> <2564DA2D-D372-45C1-A2B3-94DD214A3F5B@oracle.com> <50F04DDB.3050101@cs.oswego.edu> <510043DA.4040405@oracle.com> <5B350BAD-8F13-4C57-80ED-D2CA708729FE@gmail.com> <51005405.7020306@oracle.com> <7AE4DE90-2F81-4D31-B7A8-99B8219B4CF7@oracle.com> <5101B2D0.4020204@oracle.com> Message-ID: On Jan 29, 2013, at 5:48 PM, Kevin Bourrillion wrote: > Does anyone have a complete and current taxonomy of where exactly we've > ended up with all this (leaving out all the history of how we got here)? Does the following suffice? http://hg.openjdk.java.net/lambda/lambda/jdk/file/5d4167b7bf8c/src/share/classes/java/util/function/package-info.java Paul. > > > On Thu, Jan 24, 2013 at 2:47 PM, Tim Peierls wrote: > >> On Thu, Jan 24, 2013 at 5:20 PM, Joe Bowbeer wrote: >> >>> +1 ObjIntBlock (or a more descriptive "Block" name if one is selected) >>> >> >> Agreed. >> >> Works well in conjunction with Dan Smith's suggestion ('if the base type >> is parameterized in both its parameters and return, then the "To" prefix is >> mandatory. If not, "To" is not used.'), omitting To and Bi where they >> aren't needed. >> >> --tim >> > > > > -- > Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com From paul.sandoz at oracle.com Tue Jan 29 09:29:00 2013 From: paul.sandoz at oracle.com (Paul Sandoz) Date: Tue, 29 Jan 2013 18:29:00 +0100 Subject: Encounter order Message-ID: Hi, Below is description of encounter order for the Streams API. The implementation in the lambda repo currently conforms to this documentation although it is not implemented exactly as described. Paul. -- A source has or does not have encounter order. List and arrays are sources that have encounter order (arrays can be said to also have a spatial order). HashSet is a source that does not have encounter order (another example is PriorityQueue). A terminal operation preserves or does not preserve encounter order when producing a result. Non-preserving terminal operations are forEach, forEachUntil, findAny, match{Any, None, All}, collect(toHashSet()) and collectUnordered. An intermediate operation may inject encounter order down-stream. The sorted() operation injects encounter order when the natural comparator is used to sort elements. An intermediate operation may clear encounter order down-stream. There are no such operations implemented. (Previously the unordered() operation cleared encounter order.) Otherwise an intermediate operation must preserve encounter order if required to do so (see next paragraphs). An intermediate operation may choose to apply a different algorithm if encounter order of the elements output from the intermediate operation must be preserved or not. The distinct() operation will, when evaluating in parallel, use a ConcurrentHashMap to store unique elements if encounter order does not need to be preserved, otherwise if encounter order needs to be preserved a fold will be performed (equivalent of, in parallel, map each element to a singleton set then associatively reduce the sets to one set). An intermediate operation should preserve encounter order of the output elements if: a.1) the upstream elements input to the intermediate operation has an encounter order (either because the source has encounter order or because an upstream operation injected encounter order); and a.2) the terminal operation preserves encounter order. An intermediate operation does not need to preserve encounter order of the output elements if: b.1) the upstream elements input to the intermediate operation has no encounter order (either because the source has no encounter order or because an upstream operation cleared encounter order); or b.2) the terminal operation does not preserve encounter order *and* the intermediate operation is in a sequence of operations, to be evaluated, where the last operation in the sequence is the terminal operation and all operations in the sequence are evaluated in parallel. Rule b.2 above ensures that for the following pipeline encounter order is preserved on the sequential forEach: list.parallelStream().distinct().sequential().forEach() i.e. the distinct() operation will preserve the encounter order of the list From tim at peierls.net Tue Jan 29 09:30:12 2013 From: tim at peierls.net (Tim Peierls) Date: Tue, 29 Jan 2013 12:30:12 -0500 Subject: Function type naming conventions In-Reply-To: References: <50E48919.2060408@oracle.com> <07F4CED6-B457-4573-B111-C7547FD15692@oracle.com> <0D1FD194-593A-46D2-BC11-5C7AA07A7420@oracle.com> <50F041D3.8070004@cs.oswego.edu> <2564DA2D-D372-45C1-A2B3-94DD214A3F5B@oracle.com> <50F04DDB.3050101@cs.oswego.edu> <510043DA.4040405@oracle.com> <5B350BAD-8F13-4C57-80ED-D2CA708729FE@gmail.com> <51005405.7020306@oracle.com> <7AE4DE90-2F81-4D31-B7A8-99B8219B4CF7@oracle.com> <5101B2D0.4020204@oracle.com> Message-ID: That's a good start. (Typo in lambda signature after Supplier.) Still some holes, e.g., doesn't tell me which of the following is right: ObjObjToIntFunction BiToIntFunction ToIntBiFunction --tim On Tue, Jan 29, 2013 at 12:06 PM, Paul Sandoz wrote: > > On Jan 29, 2013, at 5:48 PM, Kevin Bourrillion wrote: > > Does anyone have a complete and current taxonomy of where exactly we've > ended up with all this (leaving out all the history of how we got here)? > > > Does the following suffice? > > > http://hg.openjdk.java.net/lambda/lambda/jdk/file/5d4167b7bf8c/src/share/classes/java/util/function/package-info.java > > Paul. > > > > On Thu, Jan 24, 2013 at 2:47 PM, Tim Peierls wrote: > > On Thu, Jan 24, 2013 at 5:20 PM, Joe Bowbeer wrote: > > +1 ObjIntBlock (or a more descriptive "Block" name if one is selected) > > > Agreed. > > Works well in conjunction with Dan Smith's suggestion ('if the base type > is parameterized in both its parameters and return, then the "To" prefix is > mandatory. If not, "To" is not used.'), omitting To and Bi where they > aren't needed. > > --tim > > > > > -- > Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com > > > From brian.goetz at oracle.com Tue Jan 29 09:36:21 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 29 Jan 2013 12:36:21 -0500 Subject: Function type naming conventions In-Reply-To: References: <50E48919.2060408@oracle.com> <07F4CED6-B457-4573-B111-C7547FD15692@oracle.com> <0D1FD194-593A-46D2-BC11-5C7AA07A7420@oracle.com> <50F041D3.8070004@cs.oswego.edu> <2564DA2D-D372-45C1-A2B3-94DD214A3F5B@oracle.com> <50F04DDB.3050101@cs.oswego.edu> <510043DA.4040405@oracle.com> <5B350BAD-8F13-4C57-80ED-D2CA708729FE@gmail.com> <51005405.7020306@oracle.com> <7AE4DE90-2F81-4D31-B7A8-99B8219B4CF7@oracle.com> <5101B2D0.4020204@oracle.com> Message-ID: <51080895.6090805@oracle.com> > Still some holes, e.g., doesn't tell me which of the following is right: > > ObjObjToIntFunction > BiToIntFunction > ToIntBiFunction The second one is definitely wrong because we put type modifiers first. So somewhere it should say: [ type arg specializations ] [ return specialization ] [ arity ] base Of the remaining (and many like it), there are always going to be multiple ways to name the same thing. So its not that (1) is wrong and (3) is right. Both are consistent (as is ObjObjToIntBiFunction). But we should have some rules for which we prefer. > > --tim > > On Tue, Jan 29, 2013 at 12:06 PM, Paul Sandoz > wrote: > > > On Jan 29, 2013, at 5:48 PM, Kevin Bourrillion > wrote: > >> Does anyone have a complete and current taxonomy of where exactly >> we've >> ended up with all this (leaving out all the history of how we got >> here)? > > Does the following suffice? > > http://hg.openjdk.java.net/lambda/lambda/jdk/file/5d4167b7bf8c/src/share/classes/java/util/function/package-info.java > > Paul. > >> >> >> On Thu, Jan 24, 2013 at 2:47 PM, Tim Peierls > > wrote: >> >>> On Thu, Jan 24, 2013 at 5:20 PM, Joe Bowbeer >>> >wrote: >>> >>>> +1 ObjIntBlock (or a more descriptive "Block" name if one is >>>> selected) >>>> >>> >>> Agreed. >>> >>> Works well in conjunction with Dan Smith's suggestion ('if the >>> base type >>> is parameterized in both its parameters and return, then the "To" >>> prefix is >>> mandatory. If not, "To" is not used.'), omitting To and Bi where >>> they >>> aren't needed. >>> >>> --tim >>> >> >> >> >> -- >> Kevin Bourrillion | Java Librarian | Google, Inc. | >> kevinb at google.com > > From kevinb at google.com Tue Jan 29 10:01:03 2013 From: kevinb at google.com (Kevin Bourrillion) Date: Tue, 29 Jan 2013 10:01:03 -0800 Subject: explode() In-Reply-To: References: Message-ID: Hmm. So the methods I proposed, Consumer.accept(Collection) and Consumer.accept(Stream), are functionally identical to Collection.forEach(consumer) and Stream.forEach(consumer), and thus have little benefit except in that various Consumer implementations might be able to override them and do something more efficient. Which in turn means that these forEach() methods would have to delegate straight to them; they become pure conveniences only. So, I guess either we can imagine enough use cases of a consumer that wants to optimize these, to make the whole idea seem worthwhile, or we can't, and we can just drop it. But when considering those use cases, one of them may arise if drop Downstream and have explode() use Consumer instead. In that case, does it break explode() if we have users just using stream.forEach(consumer) and collection.forEach(consumer) instead of downstream.send(stream) and downstream.send(collection)? On Thu, Jan 24, 2013 at 2:43 PM, Kevin Bourrillion wrote: > explode() is aptly named for what it did to my brain when I encountered > it. :-) > > A few things are adding up to make it impenetrable. > > 1. The vast majority of the Stream API, save the obvious exceptions like > forEach(), is solidly in the functional style. Collectors aren't really, > but as long as I use the prepackaged ones, it *feels* fairly functional > anyway. Now suddenly there's explode, which doesn't feel functional at all. > Instead, I need to think of the bit of code I provide to it as an active > participant in a pipeline. Things are fed to me, I feed things on down the > line. This makes it different, and different is automatically confusing. > This can't really be *corrected*, but seems worth nothing; maybe we can > account for the difference somehow. > > 2. In attempting to learn it, I'm confronted with a type, > Stream.Downstream, which I won't have heard of before that moment, and > whose name reveals little. Is there any particular reason that > Sink/Consumer, with its accept(T) method, should not also have "default" > methods implementing accept(Collection) and accept(Stream) and > accept(T[])? I can't think of any; those sound just plain handy. And if > we added those, would there be enough value in preserving Downstream as an > identically-signatured type? We could dispense with it, as I've done below. > > 3. Except sometimes there *is* value in having a special type even if its > signature is identical to another. I suspect that a custom type could help > quite a bit in this case: > > interface Exploder { > /** > * Maps {code input} to zero or more instances of R, sending these > * to {@code resultConsumer} to be included in the results of > * {@link Stream#explode(Exploder)}. > */ > void explode(T input, Consumer resultConsumer); > } > > > 4. Then there is the name "explode". It's... okay. "unpack"? Nah. It seems > maybe 40% possible that a great name is out there eluding us.... > > > > -- > Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com > -- Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com From brian.goetz at oracle.com Tue Jan 29 10:14:09 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 29 Jan 2013 13:14:09 -0500 Subject: explode() In-Reply-To: References: Message-ID: <51081171.2040004@oracle.com> > Hmm. So the methods I proposed, Consumer.accept(Collection) and > Consumer.accept(Stream), are functionally identical to > Collection.forEach(consumer) and Stream.forEach(consumer), and thus have > little benefit except in that various Consumer implementations might be > able to override them and do something more efficient. Which in turn > means that these forEach() methods would have to delegate straight to > them; they become pure conveniences only. Mostly so, though I could imagine (I realize I'm undermining my earlier position) that Sink.acceptAll(Stream) might be able to do something better in the case when we explode an element to an infinite stream. Currently, exploding an element to an infinite stream: ints.explode(i -> Streams.repeat(() -> i)).getFirst() would never terminate, even though it theoretically could. Having either the acceptAll() method on Consumer OR the send(Stream) method on DownstreamThingie, would leave us a door to eventually fix that issue; asking users to do stream.forEach(consumer) would make it harder to do so. My worry is that, especially given the late date, I'd like to minimize the number of default methods on SAM types. > But when considering those use cases, one of them may arise if drop > Downstream and have explode() use Consumer instead. In that case, does > it break explode() if we have users just using stream.forEach(consumer) > and collection.forEach(consumer) instead of downstream.send(stream) and > downstream.send(collection)? Another possibility is to return to an intermediate position, where there was a SAM for "function from singleton to multi", such as the Exploder you suggest. Once we do that, then putting a renamed Downstream in Exploder makes it clearer: interface Exploder { void explode(T input, ShrapnelCatcher catcher); interface ShrapnelCatcher { void accept(U u); default void acceptAll(Collection c) { ... } ... } } > > > On Thu, Jan 24, 2013 at 2:43 PM, Kevin Bourrillion > wrote: > > explode() is aptly named for what it did to my brain when I > encountered it. :-) > > A few things are adding up to make it impenetrable. > > 1. The vast majority of the Stream API, save the obvious exceptions > like forEach(), is solidly in the functional style. Collectors > aren't really, but as long as I use the prepackaged ones, it > /feels/ fairly functional anyway. Now suddenly there's explode, > which doesn't feel functional at all. Instead, I need to think of > the bit of code I provide to it as an active participant in a > pipeline. Things are fed to me, I feed things on down the line. This > makes it different, and different is automatically confusing. This > can't really be /corrected/, but seems worth nothing; maybe we can > account for the difference somehow. > > 2. In attempting to learn it, I'm confronted with a type, > Stream.Downstream, which I won't have heard of before that > moment, and whose name reveals little. Is there any particular > reason that Sink/Consumer, with its accept(T) method, should > not also have "default" methods implementing accept(Collection) > and accept(Stream) and accept(T[])? I can't think of any; those > sound just plain handy. And if we added those, would there be > enough value in preserving Downstream as an identically-signatured > type? We could dispense with it, as I've done below. > > 3. Except sometimes there /is/ value in having a special type even > if its signature is identical to another. I suspect that a custom > type could help quite a bit in this case: > > interface Exploder { > /** > * Maps {code input} to zero or more instances of R, sending > these > * to {@code resultConsumer} to be included in the results of > * {@link Stream#explode(Exploder)}. > */ > void explode(T input, Consumer resultConsumer); > } > > > 4. Then there is the name "explode". It's... okay. "unpack"? Nah. It > seems maybe 40% possible that a great name is out there eluding us.... > > > > -- > Kevin Bourrillion | Java Librarian | Google, Inc. |kevinb at google.com > > > > > > -- > Kevin Bourrillion | Java Librarian | Google, Inc. |kevinb at google.com > From brian.goetz at oracle.com Tue Jan 29 10:19:22 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 29 Jan 2013 13:19:22 -0500 Subject: explode() In-Reply-To: <51081171.2040004@oracle.com> References: <51081171.2040004@oracle.com> Message-ID: <510812AA.8030702@oracle.com> The other alternative is more overloading: explode(Exploder) // current explodeToCollection(Function>) explodeToStream(Function>) explodeToArray(Function) Note that many people think "all they want" is the second one, but they're wrong. IF we can only have one, there's no discussion -- it's the first one, since one can relatively easily derive the others from the first. However, the first does tend to induce head-explosion. On 1/29/2013 1:14 PM, Brian Goetz wrote: >> Hmm. So the methods I proposed, Consumer.accept(Collection) and >> Consumer.accept(Stream), are functionally identical to >> Collection.forEach(consumer) and Stream.forEach(consumer), and thus have >> little benefit except in that various Consumer implementations might be >> able to override them and do something more efficient. Which in turn >> means that these forEach() methods would have to delegate straight to >> them; they become pure conveniences only. > > Mostly so, though I could imagine (I realize I'm undermining my earlier > position) that Sink.acceptAll(Stream) might be able to do something > better in the case when we explode an element to an infinite stream. > Currently, exploding an element to an infinite stream: > > ints.explode(i -> Streams.repeat(() -> i)).getFirst() > > would never terminate, even though it theoretically could. > > Having either the acceptAll() method on Consumer OR the send(Stream) > method on DownstreamThingie, would leave us a door to eventually fix > that issue; asking users to do stream.forEach(consumer) would make it > harder to do so. > > My worry is that, especially given the late date, I'd like to minimize > the number of default methods on SAM types. > >> But when considering those use cases, one of them may arise if drop >> Downstream and have explode() use Consumer instead. In that case, does >> it break explode() if we have users just using stream.forEach(consumer) >> and collection.forEach(consumer) instead of downstream.send(stream) and >> downstream.send(collection)? > > Another possibility is to return to an intermediate position, where > there was a SAM for "function from singleton to multi", such as the > Exploder you suggest. Once we do that, then putting a renamed > Downstream in Exploder makes it clearer: > > interface Exploder { > void explode(T input, ShrapnelCatcher catcher); > > interface ShrapnelCatcher { > void accept(U u); > default void acceptAll(Collection c) { ... } > ... > } > } > > > >> >> >> On Thu, Jan 24, 2013 at 2:43 PM, Kevin Bourrillion > > wrote: >> >> explode() is aptly named for what it did to my brain when I >> encountered it. :-) >> >> A few things are adding up to make it impenetrable. >> >> 1. The vast majority of the Stream API, save the obvious exceptions >> like forEach(), is solidly in the functional style. Collectors >> aren't really, but as long as I use the prepackaged ones, it >> /feels/ fairly functional anyway. Now suddenly there's explode, >> which doesn't feel functional at all. Instead, I need to think of >> the bit of code I provide to it as an active participant in a >> pipeline. Things are fed to me, I feed things on down the line. This >> makes it different, and different is automatically confusing. This >> can't really be /corrected/, but seems worth nothing; maybe we can >> account for the difference somehow. >> >> 2. In attempting to learn it, I'm confronted with a type, >> Stream.Downstream, which I won't have heard of before that >> moment, and whose name reveals little. Is there any particular >> reason that Sink/Consumer, with its accept(T) method, should >> not also have "default" methods implementing accept(Collection) >> and accept(Stream) and accept(T[])? I can't think of any; those >> sound just plain handy. And if we added those, would there be >> enough value in preserving Downstream as an identically-signatured >> type? We could dispense with it, as I've done below. >> >> 3. Except sometimes there /is/ value in having a special type even >> if its signature is identical to another. I suspect that a custom >> type could help quite a bit in this case: >> >> interface Exploder { >> /** >> * Maps {code input} to zero or more instances of R, sending >> these >> * to {@code resultConsumer} to be included in the results of >> * {@link Stream#explode(Exploder)}. >> */ >> void explode(T input, Consumer resultConsumer); >> } >> >> >> 4. Then there is the name "explode". It's... okay. "unpack"? Nah. It >> seems maybe 40% possible that a great name is out there eluding >> us.... >> >> >> >> -- >> Kevin Bourrillion | Java Librarian | Google, Inc. |kevinb at google.com >> >> >> >> >> >> -- >> Kevin Bourrillion | Java Librarian | Google, Inc. |kevinb at google.com >> From kevinb at google.com Tue Jan 29 10:31:02 2013 From: kevinb at google.com (Kevin Bourrillion) Date: Tue, 29 Jan 2013 10:31:02 -0800 Subject: Function type naming conventions In-Reply-To: <51080895.6090805@oracle.com> References: <50E48919.2060408@oracle.com> <07F4CED6-B457-4573-B111-C7547FD15692@oracle.com> <0D1FD194-593A-46D2-BC11-5C7AA07A7420@oracle.com> <50F041D3.8070004@cs.oswego.edu> <2564DA2D-D372-45C1-A2B3-94DD214A3F5B@oracle.com> <50F04DDB.3050101@cs.oswego.edu> <510043DA.4040405@oracle.com> <5B350BAD-8F13-4C57-80ED-D2CA708729FE@gmail.com> <51005405.7020306@oracle.com> <7AE4DE90-2F81-4D31-B7A8-99B8219B4CF7@oracle.com> <5101B2D0.4020204@oracle.com> <51080895.6090805@oracle.com> Message-ID: All right, how many mistakes am I already making here? :-) (I keep forgetting if there is a url where I can browse the very-latest lambda sources as checked in.) Functions: * To Object To int To doubleFrom Object Function ToIntFunction ToDoubleFunction From int IntFunctionIntToIntFunction IntToDoubleFunction From doubleDoubleFunction DoubleToIntFunction DoubleToDoubleFunction BiConsumers:* * Then Object Then int Then doubleFirst Object BiConsumer ObjIntConsumer ObjDoubleConsumer First int -IntIntConsumer* IntDoubleConsumer First double- - DoubleDoubleConsumer * * or is it IntBiConsumer? On Tue, Jan 29, 2013 at 9:36 AM, Brian Goetz wrote: > Still some holes, e.g., doesn't tell me which of the following is right: >> >> ObjObjToIntFunction >> BiToIntFunction >> ToIntBiFunction >> > > The second one is definitely wrong because we put type modifiers first. > So somewhere it should say: > > [ type arg specializations ] [ return specialization ] [ arity ] base > > Of the remaining (and many like it), there are always going to be multiple > ways to name the same thing. So its not that (1) is wrong and (3) is > right. Both are consistent (as is ObjObjToIntBiFunction). But we should > have some rules for which we prefer. > > >> --tim >> >> On Tue, Jan 29, 2013 at 12:06 PM, Paul Sandoz > > wrote: >> >> >> On Jan 29, 2013, at 5:48 PM, Kevin Bourrillion > > wrote: >> >> Does anyone have a complete and current taxonomy of where exactly >>> we've >>> ended up with all this (leaving out all the history of how we got >>> here)? >>> >> >> Does the following suffice? >> >> http://hg.openjdk.java.net/**lambda/lambda/jdk/file/** >> 5d4167b7bf8c/src/share/**classes/java/util/function/**package-info.java >> >> Paul. >> >> >>> >>> On Thu, Jan 24, 2013 at 2:47 PM, Tim Peierls >> > wrote: >>> >>> On Thu, Jan 24, 2013 at 5:20 PM, Joe Bowbeer >>>> **>wrote: >>>> >>>> >>>> +1 ObjIntBlock (or a more descriptive "Block" name if one is >>>>> selected) >>>>> >>>>> >>>> Agreed. >>>> >>>> Works well in conjunction with Dan Smith's suggestion ('if the >>>> base type >>>> is parameterized in both its parameters and return, then the "To" >>>> prefix is >>>> mandatory. If not, "To" is not used.'), omitting To and Bi where >>>> they >>>> aren't needed. >>>> >>>> --tim >>>> >>>> >>> >>> >>> -- >>> Kevin Bourrillion | Java Librarian | Google, Inc. | >>> kevinb at google.com >>> >> >> >> -- Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com From brian.goetz at oracle.com Tue Jan 29 10:43:33 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 29 Jan 2013 13:43:33 -0500 Subject: Function type naming conventions In-Reply-To: References: <50E48919.2060408@oracle.com> <07F4CED6-B457-4573-B111-C7547FD15692@oracle.com> <0D1FD194-593A-46D2-BC11-5C7AA07A7420@oracle.com> <50F041D3.8070004@cs.oswego.edu> <2564DA2D-D372-45C1-A2B3-94DD214A3F5B@oracle.com> <50F04DDB.3050101@cs.oswego.edu> <510043DA.4040405@oracle.com> <5B350BAD-8F13-4C57-80ED-D2CA708729FE@gmail.com> <51005405.7020306@oracle.com> <7AE4DE90-2F81-4D31-B7A8-99B8219B4CF7@oracle.com> <5101B2D0.4020204@oracle.com> <51080895.6090805@oracle.com> Message-ID: <51081855.7000808@oracle.com> On 1/29/2013 1:31 PM, Kevin Bourrillion wrote: > All right, how many mistakes am I already making here? :-) Top table is right. Bottom table is missing IntBiConsumer (alternately IntObjConsumer), DoubleBiConsumer, and DoubleIntConsumer. * entry would be IntIntBiConsumer if we dropped the pending-consideration rule about eliding arity specializations on "fully specialized" types. > > (I keep forgetting if there is a url where I can browse the > very-latest lambda sources as checked in.) Go here: http://hg.openjdk.java.net/lambda/lambda/jdk/ and navigate from the "manifest" link. > Functions: > * > > > To Object To int To double > From Object Function ToIntFunction ToDoubleFunction > From int IntFunction IntToIntFunction IntToDoubleFunction > From double DoubleFunction DoubleToIntFunction DoubleToDoubleFunction > > > > BiConsumers:* > * > > Then Object Then int Then double > First Object BiConsumer ObjIntConsumer ObjDoubleConsumer > First int - IntIntConsumer* IntDoubleConsumer > First double - - DoubleDoubleConsumer > > > * > * or is it IntBiConsumer? > > On Tue, Jan 29, 2013 at 9:36 AM, Brian Goetz > wrote: > > Still some holes, e.g., doesn't tell me which of the following > is right: > > ObjObjToIntFunction > BiToIntFunction > ToIntBiFunction > > > The second one is definitely wrong because we put type modifiers > first. So somewhere it should say: > > [ type arg specializations ] [ return specialization ] [ arity ] base > > Of the remaining (and many like it), there are always going to be > multiple ways to name the same thing. So its not that (1) is > wrong and (3) is right. Both are consistent (as is > ObjObjToIntBiFunction). But we should have some rules for which > we prefer. > > > --tim > > On Tue, Jan 29, 2013 at 12:06 PM, Paul Sandoz > > >> wrote: > > > On Jan 29, 2013, at 5:48 PM, Kevin Bourrillion > > >> wrote: > > Does anyone have a complete and current taxonomy of > where exactly > we've > ended up with all this (leaving out all the history of > how we got > here)? > > > Does the following suffice? > > http://hg.openjdk.java.net/lambda/lambda/jdk/file/5d4167b7bf8c/src/share/classes/java/util/function/package-info.java > > Paul. > > > > On Thu, Jan 24, 2013 at 2:47 PM, Tim Peierls > > >> wrote: > > On Thu, Jan 24, 2013 at 5:20 PM, Joe Bowbeer > > >>wrote: > > > +1 ObjIntBlock (or a more descriptive "Block" > name if one is > selected) > > > Agreed. > > Works well in conjunction with Dan Smith's > suggestion ('if the > base type > is parameterized in both its parameters and > return, then the "To" > prefix is > mandatory. If not, "To" is not used.'), omitting > To and Bi where > they > aren't needed. > > --tim > > > > > -- > Kevin Bourrillion | Java Librarian | Google, Inc. | > kevinb at google.com > > > > > > > > > -- > Kevin Bourrillion | Java Librarian | Google, Inc. |kevinb at google.com > From forax at univ-mlv.fr Tue Jan 29 13:47:00 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Tue, 29 Jan 2013 22:47:00 +0100 Subject: Function type naming conventions In-Reply-To: References: <50E48919.2060408@oracle.com> <07F4CED6-B457-4573-B111-C7547FD15692@oracle.com> <0D1FD194-593A-46D2-BC11-5C7AA07A7420@oracle.com> <50F041D3.8070004@cs.oswego.edu> <2564DA2D-D372-45C1-A2B3-94DD214A3F5B@oracle.com> <50F04DDB.3050101@cs.oswego.edu> <510043DA.4040405@oracle.com> <5B350BAD-8F13-4C57-80ED-D2CA708729FE@gmail.com> <51005405.7020306@oracle.com> <7AE4DE90-2F81-4D31-B7A8-99B8219B4CF7@oracle.com> <5101B2D0.4020204@oracle.com> <51080895.6090805@oracle.com> Message-ID: <51084354.2010906@univ-mlv.fr> On 01/29/2013 07:31 PM, Kevin Bourrillion wrote: > All right, how many mistakes am I already making here? :-) > > (I keep forgetting if there is a url where I can browse the > very-latest lambda sources as checked in.) > > Functions: > * > > > To Object To int To double > From Object Function ToIntFunction ToDoubleFunction > From int IntFunction IntToIntFunction IntToDoubleFunction > From double DoubleFunction DoubleToIntFunction DoubleToDoubleFunction > > > > BiConsumers:* > * > > Then Object Then int Then double > First Object BiConsumer ObjIntConsumer ObjDoubleConsumer > First int - IntIntConsumer* IntDoubleConsumer > First double - - DoubleDoubleConsumer > > > * > * or is it IntBiConsumer? and DoubleDoubleConsumer can be DoubleBiConsumer too, may be we should use the BiPrefix only if it's ObjectObject R?mi > > On Tue, Jan 29, 2013 at 9:36 AM, Brian Goetz > wrote: > > Still some holes, e.g., doesn't tell me which of the following > is right: > > ObjObjToIntFunction > BiToIntFunction > ToIntBiFunction > > > The second one is definitely wrong because we put type modifiers > first. So somewhere it should say: > > [ type arg specializations ] [ return specialization ] [ arity ] base > > Of the remaining (and many like it), there are always going to be > multiple ways to name the same thing. So its not that (1) is > wrong and (3) is right. Both are consistent (as is > ObjObjToIntBiFunction). But we should have some rules for which > we prefer. > > > --tim > > On Tue, Jan 29, 2013 at 12:06 PM, Paul Sandoz > > >> wrote: > > > On Jan 29, 2013, at 5:48 PM, Kevin Bourrillion > > >> wrote: > > Does anyone have a complete and current taxonomy of > where exactly > we've > ended up with all this (leaving out all the history of > how we got > here)? > > > Does the following suffice? > > http://hg.openjdk.java.net/lambda/lambda/jdk/file/5d4167b7bf8c/src/share/classes/java/util/function/package-info.java > > Paul. > > > > On Thu, Jan 24, 2013 at 2:47 PM, Tim Peierls > > >> wrote: > > On Thu, Jan 24, 2013 at 5:20 PM, Joe Bowbeer > > >>wrote: > > > +1 ObjIntBlock (or a more descriptive "Block" > name if one is > selected) > > > Agreed. > > Works well in conjunction with Dan Smith's > suggestion ('if the > base type > is parameterized in both its parameters and > return, then the "To" > prefix is > mandatory. If not, "To" is not used.'), omitting > To and Bi where > they > aren't needed. > > --tim > > > > > -- > Kevin Bourrillion | Java Librarian | Google, Inc. | > kevinb at google.com > > > > > > > > > -- > Kevin Bourrillion | Java Librarian | Google, Inc. |kevinb at google.com > From forax at univ-mlv.fr Tue Jan 29 13:57:35 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Tue, 29 Jan 2013 22:57:35 +0100 Subject: explode() In-Reply-To: <510812AA.8030702@oracle.com> References: <51081171.2040004@oracle.com> <510812AA.8030702@oracle.com> Message-ID: <510845CF.1010505@univ-mlv.fr> On 01/29/2013 07:19 PM, Brian Goetz wrote: > The other alternative is more overloading: > > explode(Exploder) // current > explodeToCollection(Function>) > explodeToStream(Function>) > explodeToArray(Function) > > Note that many people think "all they want" is the second one, but > they're wrong. > > IF we can only have one, there's no discussion -- it's the first one, > since one can relatively easily derive the others from the first. > However, the first does tend to induce head-explosion. like Collector, you can have one explode(Exploder) and several way to create Exploders like explode(Exploder.toCollection(User::getPermissions)) R?mi > > > > On 1/29/2013 1:14 PM, Brian Goetz wrote: >>> Hmm. So the methods I proposed, Consumer.accept(Collection) and >>> Consumer.accept(Stream), are functionally identical to >>> Collection.forEach(consumer) and Stream.forEach(consumer), and thus >>> have >>> little benefit except in that various Consumer implementations might be >>> able to override them and do something more efficient. Which in turn >>> means that these forEach() methods would have to delegate straight to >>> them; they become pure conveniences only. >> >> Mostly so, though I could imagine (I realize I'm undermining my earlier >> position) that Sink.acceptAll(Stream) might be able to do something >> better in the case when we explode an element to an infinite stream. >> Currently, exploding an element to an infinite stream: >> >> ints.explode(i -> Streams.repeat(() -> i)).getFirst() >> >> would never terminate, even though it theoretically could. >> >> Having either the acceptAll() method on Consumer OR the send(Stream) >> method on DownstreamThingie, would leave us a door to eventually fix >> that issue; asking users to do stream.forEach(consumer) would make it >> harder to do so. >> >> My worry is that, especially given the late date, I'd like to minimize >> the number of default methods on SAM types. >> >>> But when considering those use cases, one of them may arise if drop >>> Downstream and have explode() use Consumer instead. In that case, does >>> it break explode() if we have users just using stream.forEach(consumer) >>> and collection.forEach(consumer) instead of downstream.send(stream) and >>> downstream.send(collection)? >> >> Another possibility is to return to an intermediate position, where >> there was a SAM for "function from singleton to multi", such as the >> Exploder you suggest. Once we do that, then putting a renamed >> Downstream in Exploder makes it clearer: >> >> interface Exploder { >> void explode(T input, ShrapnelCatcher catcher); >> >> interface ShrapnelCatcher { >> void accept(U u); >> default void acceptAll(Collection c) { ... } >> ... >> } >> } >> >> >> >>> >>> >>> On Thu, Jan 24, 2013 at 2:43 PM, Kevin Bourrillion >> > wrote: >>> >>> explode() is aptly named for what it did to my brain when I >>> encountered it. :-) >>> >>> A few things are adding up to make it impenetrable. >>> >>> 1. The vast majority of the Stream API, save the obvious exceptions >>> like forEach(), is solidly in the functional style. Collectors >>> aren't really, but as long as I use the prepackaged ones, it >>> /feels/ fairly functional anyway. Now suddenly there's explode, >>> which doesn't feel functional at all. Instead, I need to think of >>> the bit of code I provide to it as an active participant in a >>> pipeline. Things are fed to me, I feed things on down the line. >>> This >>> makes it different, and different is automatically confusing. This >>> can't really be /corrected/, but seems worth nothing; maybe we can >>> account for the difference somehow. >>> >>> 2. In attempting to learn it, I'm confronted with a type, >>> Stream.Downstream, which I won't have heard of before that >>> moment, and whose name reveals little. Is there any particular >>> reason that Sink/Consumer, with its accept(T) method, should >>> not also have "default" methods implementing accept(Collection) >>> and accept(Stream) and accept(T[])? I can't think of any; those >>> sound just plain handy. And if we added those, would there be >>> enough value in preserving Downstream as an identically-signatured >>> type? We could dispense with it, as I've done below. >>> >>> 3. Except sometimes there /is/ value in having a special type even >>> if its signature is identical to another. I suspect that a custom >>> type could help quite a bit in this case: >>> >>> interface Exploder { >>> /** >>> * Maps {code input} to zero or more instances of R, sending >>> these >>> * to {@code resultConsumer} to be included in the >>> results of >>> * {@link Stream#explode(Exploder)}. >>> */ >>> void explode(T input, Consumer resultConsumer); >>> } >>> >>> >>> 4. Then there is the name "explode". It's... okay. "unpack"? >>> Nah. It >>> seems maybe 40% possible that a great name is out there eluding >>> us.... >>> >>> >>> >>> -- >>> Kevin Bourrillion | Java Librarian | Google, Inc. >>> |kevinb at google.com >>> >>> >>> >>> >>> >>> -- >>> Kevin Bourrillion | Java Librarian | Google, Inc. |kevinb at google.com >>> From brian.goetz at oracle.com Tue Jan 29 14:34:52 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 29 Jan 2013 17:34:52 -0500 Subject: Collectors update Message-ID: <51084E8C.2060403@oracle.com> I've done some refactoring in Collectors to address some issues, including approachability issues that were raised by Kevin. See if you like them. 1. Rename to more verby names. We now have groupingBy groupingReduce joiningWith partitioningBy instead of groupBy/mappedTo/partition. This is designed to make it more clear what is going on when you read code like: stream.collect(groupingBy(Foo::getBar)) by making it clear that groupingBy is not an action but a way of describing the desired collecting. 2. Break groupBy into two groups, groupingBy and groupingReduce. While the distinction is somewhat arbitrary since groupBy *is* a reduce, it is now more consistent with the method naming on Stream. 3. Rename mappedTo(T->U) to joiningWith. 4. Rejigger Partition to return an array again, with an explicit lambda (which will likely be an array ctor ref) to make the array. Eliminated the silly Partition class. 5. Add more tests -- we now have full test coverage on groupingXxx but not yet the others. From Donald.Raab at gs.com Tue Jan 29 14:55:09 2013 From: Donald.Raab at gs.com (Raab, Donald) Date: Tue, 29 Jan 2013 17:55:09 -0500 Subject: Collectors update In-Reply-To: <51084E8C.2060403@oracle.com> References: <51084E8C.2060403@oracle.com> Message-ID: <6712820CB52CFB4D842561213A77C05404C3A88DCE@GSCMAMP09EX.firmwide.corp.gs.com> Having the method name "collect" is going to make it very hard to teach folks to use the streams API with GS Collections as it means something different than our "collect" method. It may equally cause problems for folks using Groovy, Apache Commons Collections and JRuby as well. http://groovy.codehaus.org/groovy-jdk/java/util/Collection.html#collect(groovy.lang.Closure) http://commons.apache.org/collections/api-release/org/apache/commons/collections/CollectionUtils.html We've gone through the names accumulate, tabulate (not sure if there are others)... I know there is a dwindling list of good names, and I'll settle when there are none better, but this particular name has 30+ years of prior art in Smalltalk influenced circles. It is one of the most commonly used methods in our library and these other languages/libraries. Again, I'd like to propose "aggregate" as an alternative. stream.aggregate(groupingBy(Foo::getBar)) > -----Original Message----- > From: lambda-libs-spec-experts-bounces at openjdk.java.net [mailto:lambda- > libs-spec-experts-bounces at openjdk.java.net] On Behalf Of Brian Goetz > Sent: Tuesday, January 29, 2013 5:35 PM > To: lambda-libs-spec-experts at openjdk.java.net > Subject: Collectors update > > I've done some refactoring in Collectors to address some issues, > including approachability issues that were raised by Kevin. See if you > like them. > > 1. Rename to more verby names. We now have > groupingBy > groupingReduce > joiningWith > partitioningBy > > instead of groupBy/mappedTo/partition. This is designed to make it > more clear what is going on when you read code like: > > stream.collect(groupingBy(Foo::getBar)) > > by making it clear that groupingBy is not an action but a way of > describing the desired collecting. > > 2. Break groupBy into two groups, groupingBy and groupingReduce. > While the distinction is somewhat arbitrary since groupBy *is* a > reduce, it is now more consistent with the method naming on Stream. > > 3. Rename mappedTo(T->U) to joiningWith. > > 4. Rejigger Partition to return an array again, with an explicit > lambda (which will likely be an array ctor ref) to make the array. > Eliminated the silly Partition class. > > 5. Add more tests -- we now have full test coverage on groupingXxx but > not yet the others. From forax at univ-mlv.fr Tue Jan 29 15:13:12 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Wed, 30 Jan 2013 00:13:12 +0100 Subject: Collectors update In-Reply-To: <51084E8C.2060403@oracle.com> References: <51084E8C.2060403@oracle.com> Message-ID: <51085788.4080705@univ-mlv.fr> On 01/29/2013 11:34 PM, Brian Goetz wrote: > I've done some refactoring in Collectors to address some issues, > including approachability issues that were raised by Kevin. See if > you like them. > > 4. Rejigger Partition to return an array again, with an explicit > lambda (which will likely be an array ctor ref) to make the array. > Eliminated the silly Partition class. Please don't do that, it's pure evil. public static Collector[]> partitioningBy(Predicate predicate, IntFunction[]> arraySupplier) { in order to call this method users have to send a lambda that creates an array of parametrized type something which will be unsafe (the only way to get type safety is to create a class that inherits from Collection and to provide a type argument something like class Foo extends ArrayList {}) so while the code of the library is typesafe, no user code will never be typesafe. and BTW, the IntFunction will be always called with 2 as argument. R?mi From brian.goetz at oracle.com Tue Jan 29 15:22:02 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 29 Jan 2013 18:22:02 -0500 Subject: Collectors update In-Reply-To: <51085788.4080705@univ-mlv.fr> References: <51084E8C.2060403@oracle.com> <51085788.4080705@univ-mlv.fr> Message-ID: <5108599A.8080305@oracle.com> >> 4. Rejigger Partition to return an array again, with an explicit >> lambda (which will likely be an array ctor ref) to make the array. >> Eliminated the silly Partition class. > > Please don't do that, it's pure evil. The silly Partition class was also evil. We're in the "lesser of evils" business here. > public static Collector[]> > partitioningBy(Predicate predicate, IntFunction[]> > arraySupplier) { > > in order to call this method users have to send a lambda that creates an > array of parametrized type something which will be unsafe (the only way > to get type safety is to create a class that inherits from Collection > and to provide a type argument something like class Foo extends > ArrayList {}) so while the code of the library is typesafe, no > user code will never be typesafe. Yes, let's have this discussion. The above is true, but is this really a problem? The array is simply a box for carrying the result back. The typical use case will be: - call partition - scoop out the two collections / reductions / whatever - throw away the box So the box is intended to be a short-lived container (essentially a workaround for the lack of tuples.) This is not the same case as Stream.toArray. > and BTW, the IntFunction will be always called with 2 as argument. Yes. Is that bad? From joe.bowbeer at gmail.com Tue Jan 29 15:23:41 2013 From: joe.bowbeer at gmail.com (Joe Bowbeer) Date: Tue, 29 Jan 2013 15:23:41 -0800 Subject: Collectors update In-Reply-To: <6712820CB52CFB4D842561213A77C05404C3A88DCE@GSCMAMP09EX.firmwide.corp.gs.com> References: <51084E8C.2060403@oracle.com> <6712820CB52CFB4D842561213A77C05404C3A88DCE@GSCMAMP09EX.firmwide.corp.gs.com> Message-ID: I like the way collect() parses. I think aggregate() is more descriptive but also less appealing. Given that collect() is a method of Stream, I don't think it will be particularly challenging to teach. I wouldn't object to aggregate() though. On Tue, Jan 29, 2013 at 2:55 PM, Raab, Donald wrote: > Having the method name "collect" is going to make it very hard to teach > folks to use the streams API with GS Collections as it means something > different than our "collect" method. It may equally cause problems for > folks using Groovy, Apache Commons Collections and JRuby as well. > > > http://groovy.codehaus.org/groovy-jdk/java/util/Collection.html#collect(groovy.lang.Closure) > > http://commons.apache.org/collections/api-release/org/apache/commons/collections/CollectionUtils.html > > We've gone through the names accumulate, tabulate (not sure if there are > others)... > > I know there is a dwindling list of good names, and I'll settle when there > are none better, but this particular name has 30+ years of prior art in > Smalltalk influenced circles. It is one of the most commonly used methods > in our library and these other languages/libraries. > > Again, I'd like to propose "aggregate" as an alternative. > > stream.aggregate(groupingBy(Foo::getBar)) > > > > -----Original Message----- > > From: lambda-libs-spec-experts-bounces at openjdk.java.net [mailto:lambda- > > libs-spec-experts-bounces at openjdk.java.net] On Behalf Of Brian Goetz > > Sent: Tuesday, January 29, 2013 5:35 PM > > To: lambda-libs-spec-experts at openjdk.java.net > > Subject: Collectors update > > > > I've done some refactoring in Collectors to address some issues, > > including approachability issues that were raised by Kevin. See if you > > like them. > > > > 1. Rename to more verby names. We now have > > groupingBy > > groupingReduce > > joiningWith > > partitioningBy > > > > instead of groupBy/mappedTo/partition. This is designed to make it > > more clear what is going on when you read code like: > > > > stream.collect(groupingBy(Foo::getBar)) > > > > by making it clear that groupingBy is not an action but a way of > > describing the desired collecting. > > > > 2. Break groupBy into two groups, groupingBy and groupingReduce. > > While the distinction is somewhat arbitrary since groupBy *is* a > > reduce, it is now more consistent with the method naming on Stream. > > > > 3. Rename mappedTo(T->U) to joiningWith. > > > > 4. Rejigger Partition to return an array again, with an explicit > > lambda (which will likely be an array ctor ref) to make the array. > > Eliminated the silly Partition class. > > > > 5. Add more tests -- we now have full test coverage on groupingXxx but > > not yet the others. > From Donald.Raab at gs.com Tue Jan 29 15:49:00 2013 From: Donald.Raab at gs.com (Raab, Donald) Date: Tue, 29 Jan 2013 18:49:00 -0500 Subject: Collectors update In-Reply-To: References: <51084E8C.2060403@oracle.com> <6712820CB52CFB4D842561213A77C05404C3A88DCE@GSCMAMP09EX.firmwide.corp.gs.com> Message-ID: <6712820CB52CFB4D842561213A77C05404C3A88DD8@GSCMAMP09EX.firmwide.corp.gs.com> Folks using GS Collections or Apache Commons can write the following: collection.collect(Thing::getFoo).stream().collect(groupingBy(Foo::getBar)) CollectionUtils.collect(collection, Thing::getFoo).stream().collect(groupingBy(Foo::getBar)) I'm not exactly sure how Groovy and Java 8 will mix syntax wise so can't give an example. I would assume that no one would like to use the name "map" here, as it would be hard to teach map, because the name would be overloaded: collection.map(Thing::getFoo).stream().map(groupingBy(Foo::getBar)) The method "map" has a meaning in languages/libraries outside of Streams and retains more or less the same meaning inside of Streams in our case. In Smalltalk, Groovy, Ruby, Apache Commons Collections, GS Collections the method named "collect" is synonymous to "map" and "transform". That's what I believe will make it hard to teach/use here. From: Joe Bowbeer [mailto:joe.bowbeer at gmail.com] Sent: Tuesday, January 29, 2013 6:24 PM To: Raab, Donald [Tech] Cc: Brian Goetz; lambda-libs-spec-experts at openjdk.java.net; Motlin, Craig P. [Tech] Subject: Re: Collectors update I like the way collect() parses. I think aggregate() is more descriptive but also less appealing. Given that collect() is a method of Stream, I don't think it will be particularly challenging to teach. I wouldn't object to aggregate() though. On Tue, Jan 29, 2013 at 2:55 PM, Raab, Donald > wrote: Having the method name "collect" is going to make it very hard to teach folks to use the streams API with GS Collections as it means something different than our "collect" method. It may equally cause problems for folks using Groovy, Apache Commons Collections and JRuby as well. http://groovy.codehaus.org/groovy-jdk/java/util/Collection.html#collect(groovy.lang.Closure) http://commons.apache.org/collections/api-release/org/apache/commons/collections/CollectionUtils.html We've gone through the names accumulate, tabulate (not sure if there are others)... I know there is a dwindling list of good names, and I'll settle when there are none better, but this particular name has 30+ years of prior art in Smalltalk influenced circles. It is one of the most commonly used methods in our library and these other languages/libraries. Again, I'd like to propose "aggregate" as an alternative. stream.aggregate(groupingBy(Foo::getBar)) > -----Original Message----- > From: lambda-libs-spec-experts-bounces at openjdk.java.net [mailto:lambda- > libs-spec-experts-bounces at openjdk.java.net] On Behalf Of Brian Goetz > Sent: Tuesday, January 29, 2013 5:35 PM > To: lambda-libs-spec-experts at openjdk.java.net > Subject: Collectors update > > I've done some refactoring in Collectors to address some issues, > including approachability issues that were raised by Kevin. See if you > like them. > > 1. Rename to more verby names. We now have > groupingBy > groupingReduce > joiningWith > partitioningBy > > instead of groupBy/mappedTo/partition. This is designed to make it > more clear what is going on when you read code like: > > stream.collect(groupingBy(Foo::getBar)) > > by making it clear that groupingBy is not an action but a way of > describing the desired collecting. > > 2. Break groupBy into two groups, groupingBy and groupingReduce. > While the distinction is somewhat arbitrary since groupBy *is* a > reduce, it is now more consistent with the method naming on Stream. > > 3. Rename mappedTo(T->U) to joiningWith. > > 4. Rejigger Partition to return an array again, with an explicit > lambda (which will likely be an array ctor ref) to make the array. > Eliminated the silly Partition class. > > 5. Add more tests -- we now have full test coverage on groupingXxx but > not yet the others. From joe.bowbeer at gmail.com Tue Jan 29 15:58:51 2013 From: joe.bowbeer at gmail.com (Joe Bowbeer) Date: Tue, 29 Jan 2013 15:58:51 -0800 Subject: Collectors update In-Reply-To: <6712820CB52CFB4D842561213A77C05404C3A88DD8@GSCMAMP09EX.firmwide.corp.gs.com> References: <51084E8C.2060403@oracle.com> <6712820CB52CFB4D842561213A77C05404C3A88DCE@GSCMAMP09EX.firmwide.corp.gs.com> <6712820CB52CFB4D842561213A77C05404C3A88DD8@GSCMAMP09EX.firmwide.corp.gs.com> Message-ID: In Groovy, collect() is sometimes used like map() and sometimes like toCollection(). I don't know why they didn't just call it map(), but oh well... I would be upset if we wanted to use map() for something other than map() but I'm not bothered by our use of collect(). In my view, collect() has no standard meaning. Inconsistencies cannot be avoided. On Tue, Jan 29, 2013 at 3:49 PM, Raab, Donald wrote: > Folks using GS Collections or Apache Commons can write the following:**** > > ** ** > > collection.collect(Thing::getFoo).stream().collect(groupingBy(Foo::getBar)) > **** > > CollectionUtils.collect(collection, > Thing::getFoo).stream().collect(groupingBy(Foo::getBar))**** > > ** ** > > I?m not exactly sure how Groovy and Java 8 will mix syntax wise so can?t > give an example.**** > > ** ** > > I would assume that no one would like to use the name ?map? here, as it > would be hard to teach map, because the name would be overloaded:**** > > ** ** > > collection.map(Thing::getFoo).stream().map(groupingBy(Foo::getBar))**** > > ** ** > > The method ?map? has a meaning in languages/libraries outside of Streams > and retains more or less the same meaning inside of Streams in our case.** > ** > > ** ** > > In Smalltalk, Groovy, Ruby, Apache Commons Collections, GS Collections the > method named ?collect? is synonymous to ?map? and ?transform?. That?s what > I believe will make it hard to teach/use here.**** > > ** ** > > ** ** > > *From:* Joe Bowbeer [mailto:joe.bowbeer at gmail.com] > *Sent:* Tuesday, January 29, 2013 6:24 PM > *To:* Raab, Donald [Tech] > *Cc:* Brian Goetz; lambda-libs-spec-experts at openjdk.java.net; Motlin, > Craig P. [Tech] > *Subject:* Re: Collectors update**** > > ** ** > > I like the way collect() parses. I think aggregate() is more descriptive > but also less appealing.**** > > ** ** > > Given that collect() is a method of Stream, I don't think it will be > particularly challenging to teach.**** > > ** ** > > I wouldn't object to aggregate() though.**** > > ** ** > > On Tue, Jan 29, 2013 at 2:55 PM, Raab, Donald wrote:* > *** > > Having the method name "collect" is going to make it very hard to teach > folks to use the streams API with GS Collections as it means something > different than our "collect" method. It may equally cause problems for > folks using Groovy, Apache Commons Collections and JRuby as well. > > > http://groovy.codehaus.org/groovy-jdk/java/util/Collection.html#collect(groovy.lang.Closure) > > http://commons.apache.org/collections/api-release/org/apache/commons/collections/CollectionUtils.html > > We've gone through the names accumulate, tabulate (not sure if there are > others)... > > I know there is a dwindling list of good names, and I'll settle when there > are none better, but this particular name has 30+ years of prior art in > Smalltalk influenced circles. It is one of the most commonly used methods > in our library and these other languages/libraries. > > Again, I'd like to propose "aggregate" as an alternative. > > stream.aggregate(groupingBy(Foo::getBar))**** > > > > > -----Original Message----- > > From: lambda-libs-spec-experts-bounces at openjdk.java.net [mailto:lambda- > > libs-spec-experts-bounces at openjdk.java.net] On Behalf Of Brian Goetz > > Sent: Tuesday, January 29, 2013 5:35 PM > > To: lambda-libs-spec-experts at openjdk.java.net > > Subject: Collectors update > > > > I've done some refactoring in Collectors to address some issues, > > including approachability issues that were raised by Kevin. See if you > > like them. > > > > 1. Rename to more verby names. We now have > > groupingBy > > groupingReduce > > joiningWith > > partitioningBy > > > > instead of groupBy/mappedTo/partition. This is designed to make it > > more clear what is going on when you read code like: > > > > stream.collect(groupingBy(Foo::getBar)) > > > > by making it clear that groupingBy is not an action but a way of > > describing the desired collecting. > > > > 2. Break groupBy into two groups, groupingBy and groupingReduce. > > While the distinction is somewhat arbitrary since groupBy *is* a > > reduce, it is now more consistent with the method naming on Stream. > > > > 3. Rename mappedTo(T->U) to joiningWith. > > > > 4. Rejigger Partition to return an array again, with an explicit > > lambda (which will likely be an array ctor ref) to make the array. > > Eliminated the silly Partition class. > > > > 5. Add more tests -- we now have full test coverage on groupingXxx but > > not yet the others.**** > > ** ** > From dl at cs.oswego.edu Tue Jan 29 16:03:43 2013 From: dl at cs.oswego.edu (Doug Lea) Date: Tue, 29 Jan 2013 19:03:43 -0500 Subject: Collectors update In-Reply-To: <6712820CB52CFB4D842561213A77C05404C3A88DCE@GSCMAMP09EX.firmwide.corp.gs.com> References: <51084E8C.2060403@oracle.com> <6712820CB52CFB4D842561213A77C05404C3A88DCE@GSCMAMP09EX.firmwide.corp.gs.com> Message-ID: <5108635F.5030701@cs.oswego.edu> On 01/29/13 17:55, Raab, Donald wrote: > Having the method name "collect" is going to make it very hard to teach folks to use the streams API with GS Collections as it means something different than our "collect" method. It may equally cause problems for folks using Groovy, Apache Commons Collections and JRuby as well. > > http://groovy.codehaus.org/groovy-jdk/java/util/Collection.html#collect(groovy.lang.Closure) > http://commons.apache.org/collections/api-release/org/apache/commons/collections/CollectionUtils.html > > We've gone through the names accumulate, tabulate (not sure if there are others)... > > I know there is a dwindling list of good names, and I'll settle when there are none better, but this particular name has 30+ years of prior art in Smalltalk influenced circles. It is one of the most commonly used methods in our library and these other languages/libraries. > > Again, I'd like to propose "aggregate" as an alternative. > > stream.aggregate(groupingBy(Foo::getBar)) > One reason that I'm actively hostile to using "aggregate" here is that Aggregate is the only appropriate name left to use for a possible Java9 Collections overhaul. And "accumulate" and "tabulate" are semantically strained in this role. And like most people, I find "collect" one of the few uncontroversially easy to understand method names in the Stream API. So, sorry; I think requiring some groovy/ruby/gs users to adapt is still the best option. -Doug From Donald.Raab at gs.com Tue Jan 29 16:24:04 2013 From: Donald.Raab at gs.com (Raab, Donald) Date: Tue, 29 Jan 2013 19:24:04 -0500 Subject: Collectors update In-Reply-To: References: <51084E8C.2060403@oracle.com> <6712820CB52CFB4D842561213A77C05404C3A88DCE@GSCMAMP09EX.firmwide.corp.gs.com> <6712820CB52CFB4D842561213A77C05404C3A88DD8@GSCMAMP09EX.firmwide.corp.gs.com> Message-ID: <6712820CB52CFB4D842561213A77C05404C3A88DD9@GSCMAMP09EX.firmwide.corp.gs.com> FWIW, I believe the Object Management Group defines "collect" on Collections in the Object Constraint Language (OCL). They are a standards body. I believe the meaning was borrowed directly from Smalltalk. From: Joe Bowbeer [mailto:joe.bowbeer at gmail.com] Sent: Tuesday, January 29, 2013 6:59 PM To: Raab, Donald [Tech] Cc: Brian Goetz; lambda-libs-spec-experts at openjdk.java.net; Motlin, Craig P. [Tech] Subject: Re: Collectors update In Groovy, collect() is sometimes used like map() and sometimes like toCollection(). I don't know why they didn't just call it map(), but oh well... I would be upset if we wanted to use map() for something other than map() but I'm not bothered by our use of collect(). In my view, collect() has no standard meaning. Inconsistencies cannot be avoided. On Tue, Jan 29, 2013 at 3:49 PM, Raab, Donald > wrote: Folks using GS Collections or Apache Commons can write the following: collection.collect(Thing::getFoo).stream().collect(groupingBy(Foo::getBar)) CollectionUtils.collect(collection, Thing::getFoo).stream().collect(groupingBy(Foo::getBar)) I'm not exactly sure how Groovy and Java 8 will mix syntax wise so can't give an example. I would assume that no one would like to use the name "map" here, as it would be hard to teach map, because the name would be overloaded: collection.map(Thing::getFoo).stream().map(groupingBy(Foo::getBar)) The method "map" has a meaning in languages/libraries outside of Streams and retains more or less the same meaning inside of Streams in our case. In Smalltalk, Groovy, Ruby, Apache Commons Collections, GS Collections the method named "collect" is synonymous to "map" and "transform". That's what I believe will make it hard to teach/use here. From: Joe Bowbeer [mailto:joe.bowbeer at gmail.com] Sent: Tuesday, January 29, 2013 6:24 PM To: Raab, Donald [Tech] Cc: Brian Goetz; lambda-libs-spec-experts at openjdk.java.net; Motlin, Craig P. [Tech] Subject: Re: Collectors update I like the way collect() parses. I think aggregate() is more descriptive but also less appealing. Given that collect() is a method of Stream, I don't think it will be particularly challenging to teach. I wouldn't object to aggregate() though. On Tue, Jan 29, 2013 at 2:55 PM, Raab, Donald > wrote: Having the method name "collect" is going to make it very hard to teach folks to use the streams API with GS Collections as it means something different than our "collect" method. It may equally cause problems for folks using Groovy, Apache Commons Collections and JRuby as well. http://groovy.codehaus.org/groovy-jdk/java/util/Collection.html#collect(groovy.lang.Closure) http://commons.apache.org/collections/api-release/org/apache/commons/collections/CollectionUtils.html We've gone through the names accumulate, tabulate (not sure if there are others)... I know there is a dwindling list of good names, and I'll settle when there are none better, but this particular name has 30+ years of prior art in Smalltalk influenced circles. It is one of the most commonly used methods in our library and these other languages/libraries. Again, I'd like to propose "aggregate" as an alternative. stream.aggregate(groupingBy(Foo::getBar)) > -----Original Message----- > From: lambda-libs-spec-experts-bounces at openjdk.java.net [mailto:lambda- > libs-spec-experts-bounces at openjdk.java.net] On Behalf Of Brian Goetz > Sent: Tuesday, January 29, 2013 5:35 PM > To: lambda-libs-spec-experts at openjdk.java.net > Subject: Collectors update > > I've done some refactoring in Collectors to address some issues, > including approachability issues that were raised by Kevin. See if you > like them. > > 1. Rename to more verby names. We now have > groupingBy > groupingReduce > joiningWith > partitioningBy > > instead of groupBy/mappedTo/partition. This is designed to make it > more clear what is going on when you read code like: > > stream.collect(groupingBy(Foo::getBar)) > > by making it clear that groupingBy is not an action but a way of > describing the desired collecting. > > 2. Break groupBy into two groups, groupingBy and groupingReduce. > While the distinction is somewhat arbitrary since groupBy *is* a > reduce, it is now more consistent with the method naming on Stream. > > 3. Rename mappedTo(T->U) to joiningWith. > > 4. Rejigger Partition to return an array again, with an explicit > lambda (which will likely be an array ctor ref) to make the array. > Eliminated the silly Partition class. > > 5. Add more tests -- we now have full test coverage on groupingXxx but > not yet the others. From dl at cs.oswego.edu Tue Jan 29 16:35:36 2013 From: dl at cs.oswego.edu (Doug Lea) Date: Tue, 29 Jan 2013 19:35:36 -0500 Subject: Collectors update In-Reply-To: <5108635F.5030701@cs.oswego.edu> References: <51084E8C.2060403@oracle.com> <6712820CB52CFB4D842561213A77C05404C3A88DCE@GSCMAMP09EX.firmwide.corp.gs.com> <5108635F.5030701@cs.oswego.edu> Message-ID: <51086AD8.5090309@cs.oswego.edu> On 01/29/13 19:03, Doug Lea wrote: > So, sorry; I think requiring some groovy/ruby/gs users > to adapt is still the best option. > Well, the best option is that collect-haters find a name that is clearly better, not worse. -Doug From forax at univ-mlv.fr Tue Jan 29 16:34:42 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Wed, 30 Jan 2013 01:34:42 +0100 Subject: Collectors update In-Reply-To: <5108599A.8080305@oracle.com> References: <51084E8C.2060403@oracle.com> <51085788.4080705@univ-mlv.fr> <5108599A.8080305@oracle.com> Message-ID: <51086AA2.4030105@univ-mlv.fr> On 01/30/2013 12:22 AM, Brian Goetz wrote: >>> 4. Rejigger Partition to return an array again, with an explicit >>> lambda (which will likely be an array ctor ref) to make the array. >>> Eliminated the silly Partition class. >> >> Please don't do that, it's pure evil. > > The silly Partition class was also evil. We're in the "lesser of > evils" business here. > >> public static Collector[]> >> partitioningBy(Predicate predicate, IntFunction[]> >> arraySupplier) { >> >> in order to call this method users have to send a lambda that creates an >> array of parametrized type something which will be unsafe (the only way >> to get type safety is to create a class that inherits from Collection >> and to provide a type argument something like class Foo extends >> ArrayList {}) so while the code of the library is typesafe, no >> user code will never be typesafe. > > Yes, let's have this discussion. The above is true, but is this > really a problem? yes, it is. Arrays of parametrized type are unsafe to create unless you can subclass the component type. By example, enum Foo { } Enum[] array = Foo.values(); is safe. but Enum[] array = new Enum<>[2]; is not safe, and should even not compile because you can not use the diamond syntax on an array. > The array is simply a box for carrying the result back. The typical > use case will be: > - call partition > - scoop out the two collections / reductions / whatever > - throw away the box > > So the box is intended to be a short-lived container (essentially a > workaround for the lack of tuples.) if you want a tupe, just create a class Tuple (with a private constructor), the other solution is to not include partioningBy in jdk8 and wait 9. Now, why it's unsafe, let say we have: Collection[] csa = stream.collect(Collectors.partioningBy( ...)); one can write Object[] array = csa; array[0] = Arrays.asList(1); and later String s = csa[0].get(); // ClassCastException > This is not the same case as Stream.toArray. no, it's not. toArray can be used in a safely manner or not. partionningBy is (almost) never safe. > >> and BTW, the IntFunction will be always called with 2 as argument. > > Yes. Is that bad? > why not using a Supplier ? R?mi From Donald.Raab at gs.com Tue Jan 29 17:03:21 2013 From: Donald.Raab at gs.com (Raab, Donald) Date: Tue, 29 Jan 2013 20:03:21 -0500 Subject: Collectors update In-Reply-To: <51086AD8.5090309@cs.oswego.edu> References: <51084E8C.2060403@oracle.com> <6712820CB52CFB4D842561213A77C05404C3A88DCE@GSCMAMP09EX.firmwide.corp.gs.com> <5108635F.5030701@cs.oswego.edu> <51086AD8.5090309@cs.oswego.edu> Message-ID: <6712820CB52CFB4D842561213A77C05404C3A88DDA@GSCMAMP09EX.firmwide.corp.gs.com> I like collect very much. Just not in this context, with such a variety of end-results. Your point here is reasonable. You don't like aggregate because of something in the future plans for Java 9. I don't have the future plans for Java 9 in front of me, so it seemed like a reasonable alternative at first. I will do my best and find an alternative that everyone else here likes. > Well, the best option is that collect-haters find a name that is > clearly better, not worse. > > -Doug > From brian.goetz at oracle.com Tue Jan 29 17:09:29 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 29 Jan 2013 20:09:29 -0500 Subject: Collectors update In-Reply-To: <51086AA2.4030105@univ-mlv.fr> References: <51084E8C.2060403@oracle.com> <51085788.4080705@univ-mlv.fr> <5108599A.8080305@oracle.com> <51086AA2.4030105@univ-mlv.fr> Message-ID: <510872C9.6040100@oracle.com> > why not using a Supplier ? How is that better? It still has all the safety problems, PLUS the risk that the user serves up an array other than size 2. And prevents the user from using array ctor refs (Foo[]::new). From brian.goetz at oracle.com Tue Jan 29 17:15:25 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 29 Jan 2013 20:15:25 -0500 Subject: Collectors update In-Reply-To: <51086AA2.4030105@univ-mlv.fr> References: <51084E8C.2060403@oracle.com> <51085788.4080705@univ-mlv.fr> <5108599A.8080305@oracle.com> <51086AA2.4030105@univ-mlv.fr> Message-ID: <5108742D.8020201@oracle.com> >> The silly Partition class was also evil. We're in the "lesser of >> evils" business here. Would a Map be better? More boxing, but we can use an EnumMap-like implementation, and no ctor needed. From forax at univ-mlv.fr Tue Jan 29 17:21:54 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Wed, 30 Jan 2013 02:21:54 +0100 Subject: Collectors update In-Reply-To: <510872C9.6040100@oracle.com> References: <51084E8C.2060403@oracle.com> <51085788.4080705@univ-mlv.fr> <5108599A.8080305@oracle.com> <51086AA2.4030105@univ-mlv.fr> <510872C9.6040100@oracle.com> Message-ID: <510875B2.8030505@univ-mlv.fr> On 01/30/2013 02:09 AM, Brian Goetz wrote: >> why not using a Supplier ? > > How is that better? yes, I was just answering to why using IntFunction, it doesn't solve the type safety issue. > It still has all the safety problems, PLUS the risk that the user > serves up an array other than size 2. even if the argument is 2, user can create an array of 1. > And prevents the user from using array ctor refs (Foo[]::new). > yes. R?mi From forax at univ-mlv.fr Tue Jan 29 17:24:55 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Wed, 30 Jan 2013 02:24:55 +0100 Subject: Collectors update In-Reply-To: <5108742D.8020201@oracle.com> References: <51084E8C.2060403@oracle.com> <51085788.4080705@univ-mlv.fr> <5108599A.8080305@oracle.com> <51086AA2.4030105@univ-mlv.fr> <5108742D.8020201@oracle.com> Message-ID: <51087667.9070508@univ-mlv.fr> On 01/30/2013 02:15 AM, Brian Goetz wrote: >>> The silly Partition class was also evil. We're in the "lesser of >>> evils" business here. > > Would a Map be better? More boxing, but we can use an > EnumMap-like implementation, and no ctor needed. > > > yes, wrapping an array as a Map will work, but I think I prefer a BooleanFunction (boolean -> T), there is less method to implement. R?mi From brian.goetz at oracle.com Tue Jan 29 17:34:50 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 29 Jan 2013 20:34:50 -0500 Subject: Collectors update In-Reply-To: <51087667.9070508@univ-mlv.fr> References: <51084E8C.2060403@oracle.com> <51085788.4080705@univ-mlv.fr> <5108599A.8080305@oracle.com> <51086AA2.4030105@univ-mlv.fr> <5108742D.8020201@oracle.com> <51087667.9070508@univ-mlv.fr> Message-ID: <510878BA.90509@oracle.com> Now, that's an interesting idea. On the one hand, it is perfect: you get a function Boolean -> T, which you can query as many times as you like, and not worry at all about where/how the data is stored. On the other hand, it might well be confusing to Java developers, who are used to getting data structures (arrays, collections, maps) back from such functions. And this would be the sole counterexample. On 1/29/2013 8:24 PM, Remi Forax wrote: > On 01/30/2013 02:15 AM, Brian Goetz wrote: >>>> The silly Partition class was also evil. We're in the "lesser of >>>> evils" business here. >> >> Would a Map be better? More boxing, but we can use an >> EnumMap-like implementation, and no ctor needed. >> >> >> > > yes, wrapping an array as a Map will work, > but I think I prefer a BooleanFunction (boolean -> T), there is less > method to implement. > > R?mi > From dl at cs.oswego.edu Wed Jan 30 05:08:25 2013 From: dl at cs.oswego.edu (Doug Lea) Date: Wed, 30 Jan 2013 08:08:25 -0500 Subject: Collectors update In-Reply-To: <6712820CB52CFB4D842561213A77C05404C3A88DDA@GSCMAMP09EX.firmwide.corp.gs.com> References: <51084E8C.2060403@oracle.com> <6712820CB52CFB4D842561213A77C05404C3A88DCE@GSCMAMP09EX.firmwide.corp.gs.com> <5108635F.5030701@cs.oswego.edu> <51086AD8.5090309@cs.oswego.edu> <6712820CB52CFB4D842561213A77C05404C3A88DDA@GSCMAMP09EX.firmwide.corp.gs.com> Message-ID: <51091B49.6040904@cs.oswego.edu> On 01/29/13 20:03, Raab, Donald wrote: > Your point here is reasonable. You don't like aggregate because of something > in the future plans for Java 9. I don't have the future plans for Java 9 in > front of me, No one does; there are none that I know of. But after being responsible for bad un-future-proofed decisions in the past, some of us are especially cautious about not making more of them. > I will do > my best and find an alternative that everyone else here likes. Thanks. -Doug From brian.goetz at oracle.com Wed Jan 30 10:21:03 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 30 Jan 2013 13:21:03 -0500 Subject: Collectors update In-Reply-To: <51084E8C.2060403@oracle.com> References: <51084E8C.2060403@oracle.com> Message-ID: <5109648F.9000004@oracle.com> Are there any other comments besides the concern about using arrays for partition? On 1/29/2013 5:34 PM, Brian Goetz wrote: > I've done some refactoring in Collectors to address some issues, > including approachability issues that were raised by Kevin. See if you > like them. > > 1. Rename to more verby names. We now have > groupingBy > groupingReduce > joiningWith > partitioningBy > > instead of groupBy/mappedTo/partition. This is designed to make it more > clear what is going on when you read code like: > > stream.collect(groupingBy(Foo::getBar)) > > by making it clear that groupingBy is not an action but a way of > describing the desired collecting. > > 2. Break groupBy into two groups, groupingBy and groupingReduce. While > the distinction is somewhat arbitrary since groupBy *is* a reduce, it is > now more consistent with the method naming on Stream. > > 3. Rename mappedTo(T->U) to joiningWith. > > 4. Rejigger Partition to return an array again, with an explicit lambda > (which will likely be an array ctor ref) to make the array. Eliminated > the silly Partition class. > > 5. Add more tests -- we now have full test coverage on groupingXxx but > not yet the others. From alahijani at gmail.com Wed Jan 30 12:32:38 2013 From: alahijani at gmail.com (Ali Lahijani) Date: Thu, 31 Jan 2013 00:02:38 +0330 Subject: Should Stream be covariant? Message-ID: As a mental experiment, I tried to create a "constructive proof" that Stream is covariant in the variable T. My basic idea is that if it is covariant, it should be possible to elevate an expression of type Stream to an expression of type Stream in a suitable "mechanical" fashion. So I tried to implemented a function public static Stream elevate(Stream delegate)... To implement the above method, one of course first needs to prove that Spliterator, Iterator and Optional are covariant in T. But they are quite straightforward. public static Optional elevate(Optional delegate)... public static Iterator elevate(Iterator delegate)... public static Spliterator elevate(Spliterator delegate)... The result of the experiment is pasted below. Almost all methods pass the test, which, I suppose, means that the Stream API has an exceptionally clean design. (It is worth mentioning that not all previous versions of Stream passed the same test so smoothly.) But as the compiler will tell you, there is a problem with two methods, both cases because they use BinaryOperator: public T reduce(T identity, BinaryOperator reducer); public Optional reduce(BinaryOperator reducer); BinaryOperator is invariant in X, so passing T to BinaryOperator removes covariance of Stream. The first method is not needed, it is just a sugar around the third from of reduce which takes three parameters: public T reduce(T identity, BinaryOperator reducer) { return reduce(identity, reducer, reducer); } But to be able to emulate the second form, reduce(BinaryOperator), there should be a method to decompose a Stream as a head/tail pair. Given that facility, one can emulate reduce(reducer) as tail.reduce(head, reducer, reducer). So here is my question: is it possible that the non-covariant reduce methods be removed, and a method for head/tail decomposition is added instead? Best Ali From howard.lovatt at gmail.com Wed Jan 30 12:27:30 2013 From: howard.lovatt at gmail.com (Howard Lovatt) Date: Thu, 31 Jan 2013 07:27:30 +1100 Subject: tee() In-Reply-To: <5101DBDA.9060705@cs.oswego.edu> References: <5101D634.2060009@cs.oswego.edu> <5101DBDA.9060705@cs.oswego.edu> Message-ID: <91B8E417-E3C8-433C-AAF4-918013883E54@gmail.com> In my own stream library I have a toString method that forces evaluation. For debugging I use the equivalent of (neglecting generics): Stream s = collection.stream(); Stream f = s.filter( ... ); Stream r = f.reduce( ... ); Then when debugging I step over each line and examine the intermediate stages (usually by hovering over the variable which causes my non-lazy toString to be called). Not ideal, but the best I have come up with and no need for tee(). -- Howard Lovatt +61 419 971 263 (sent from my PDA) On 25/01/2013, at 12:11 PM, Doug Lea
wrote: > On 01/24/13 20:00, Joe Bowbeer wrote: >> I'm OK with peek() even though it has other meanings on IO streams. >> >> Is the name leak() is more to your liking? > > I can't think of a good name for the the little idiom of > ....map(x -> { use(x); return x; })... > > Which seems more like an IDE thing than an API thing anyway. > > But then again, I don't like practically all of the > convenience methods, so discount my vote accordingly. > > -Doug > > > >> >> Joe >> >> >> On Thu, Jan 24, 2013 at 4:47 PM, Doug Lea
> > wrote: >> >> On 01/24/13 15:43, Kevin Bourrillion wrote: >> >> If it really must stay, I think I do like "peek" or "observe" over >> "tee". But I >> would love to drop it. >> >> >> In case it is not too late to vote to drop this, I vote to drop it. >> And if too late, I vote to name it something other than any of these. >> >> -Doug > From alahijani at gmail.com Wed Jan 30 12:36:15 2013 From: alahijani at gmail.com (Ali Lahijani) Date: Thu, 31 Jan 2013 00:06:15 +0330 Subject: Should Stream be covariant? In-Reply-To: References: Message-ID: Here is the code: public class Covariance { /** * Lemma: {@code Optional} is covariant in {@code T} */ public static Optional elevate(final Optional delegate) { return delegate.isPresent() ? Optional.of(delegate.get()) : Optional.empty(); } /** * Lemma: {@code Iterator} is covariant in {@code T} */ public static Iterator elevate(final Iterator delegate) { return new Iterator() { @Override public boolean hasNext() { return delegate.hasNext(); } @Override public T next() { return delegate.next(); } @Override public void remove() { delegate.remove(); } }; } /** * Lemma: {@code Spliterator} is covariant in {@code T} */ public static Spliterator elevate(final Spliterator delegate) { return new Spliterator() { public int getNaturalSplits() { return delegate.getNaturalSplits(); } public Spliterator split() { return elevate(delegate.split()); } public Iterator iterator() { return elevate(delegate.iterator()); } public boolean isPredictableSplits() { return delegate.isPredictableSplits(); } public void forEach(Block block) { delegate.forEach(block); } public long getSizeIfKnown() { return delegate.getSizeIfKnown(); } public long estimateSize() { return delegate.estimateSize(); } }; } /** * Theorem: {@code Stream} is covariant in {@code T} */ public static Stream elevate(final Stream delegate) { return new Stream() { @Override public Stream filter(Predicate predicate) { return elevate(delegate.filter(predicate)); } @Override public Stream map(Function mapper) { return delegate.map(mapper); } @Override public IntStream map(ToIntFunction mapper) { return delegate.map(mapper); } @Override public LongStream map(ToLongFunction mapper) { return delegate.map(mapper); } @Override public DoubleStream map(ToDoubleFunction mapper) { return delegate.map(mapper); } @Override public Stream explode(BiConsumer, ? super T> exploder) { return delegate.explode(exploder); } @Override public Stream distinct() { return elevate(delegate.distinct()); } @Override public Stream sorted() { return elevate(delegate.sorted()); } @Override public Stream sorted(Comparator comparator) { return elevate(delegate.sorted(comparator)); } @Override public void forEach(Consumer consumer) { delegate.forEach(consumer); } @Override public void forEachUntil(Consumer consumer, BooleanSupplier until) { delegate.forEachUntil(consumer, until); } @Override public Stream peek(Consumer consumer) { return elevate(delegate.peek(consumer)); } @Override public Stream limit(long maxSize) { return elevate(delegate.limit(maxSize)); } @Override public Stream substream(long startingOffset) { return elevate(delegate.substream(startingOffset)); } @Override public Stream substream(long startingOffset, long endingOffset) { return elevate(delegate.substream(startingOffset, endingOffset)); } @Override protected Object[] toArray() { return delegate.toArray(); } @Override public A[] toArray(IntFunction generator) { return delegate.toArray(generator); } @Override public T reduce(T identity, BinaryOperator reducer) { /* * TODO this one does work, but the implementation is not "mechanically generated" */ // return delegate.reduce(identity, reducer, reducer); return delegate.reduce(identity, reducer); } @Override public Optional reduce(BinaryOperator reducer) { /* * TODO there is no hope for this one */ return delegate.reduce(reducer); } @Override public U reduce(U identity, BiFunction accumulator, BinaryOperator reducer) { return delegate.reduce(identity, accumulator, reducer); } @Override public R collect(Supplier resultFactory, BiConsumer accumulator, BiConsumer reducer) { return delegate.collect(resultFactory, accumulator, reducer); } @Override public R collect(Collector collector) { return delegate.collect(collector); } @Override public R collectUnordered(Collector collector) { return delegate.collectUnordered(collector); } @Override protected Optional max(Comparator comparator) { return elevate(delegate.max(comparator)); } @Override protected Optional min(Comparator comparator) { return elevate(delegate.min(comparator)); } @Override public boolean anyMatch(Predicate predicate) { return delegate.anyMatch(predicate); } @Override public boolean allMatch(Predicate predicate) { return delegate.allMatch(predicate); } @Override public boolean noneMatch(Predicate predicate) { return delegate.noneMatch(predicate); } @Override public Optional findFirst() { return elevate(delegate.findFirst()); } @Override public Optional findAny() { return elevate(delegate.findAny()); } @Override public Stream sequential() { return elevate(delegate.sequential()); } @Override public Stream parallel() { return elevate(delegate.parallel()); } @Override public Iterator iterator() { return elevate(delegate.iterator()); } @Override public Spliterator spliterator() { return elevate(delegate.spliterator()); } @Override public boolean isParallel() { return delegate.isParallel(); } @Override public int getStreamFlags() { return delegate.getStreamFlags(); } }; } } From Donald.Raab at gs.com Wed Jan 30 12:42:27 2013 From: Donald.Raab at gs.com (Raab, Donald) Date: Wed, 30 Jan 2013 15:42:27 -0500 Subject: Collectors update In-Reply-To: <51091B49.6040904@cs.oswego.edu> References: <51084E8C.2060403@oracle.com> <6712820CB52CFB4D842561213A77C05404C3A88DCE@GSCMAMP09EX.firmwide.corp.gs.com> <5108635F.5030701@cs.oswego.edu> <51086AD8.5090309@cs.oswego.edu> <6712820CB52CFB4D842561213A77C05404C3A88DDA@GSCMAMP09EX.firmwide.corp.gs.com> <51091B49.6040904@cs.oswego.edu> Message-ID: <6712820CB52CFB4D842561213A77C05404C3A88E74@GSCMAMP09EX.firmwide.corp.gs.com> In my opinion, collect should return a collection. It should not reduce to any result. In the interest of time, here's a stab at an alternative list I came up with using the powers of thesaurus yesterday: into gather assemble summarize The functionality currently called collect feels more like injectInto/inject in Smalltalk/Ruby/Groovy, but nothing is being injected into the method collect directly, but by the Collector (the R in makeResult()). InjectInto/inject is the equivalent of foldLeft. I would be less concerned over using injectInto or inject than collect, as at least it seems similar enough in that it can return any value, determined by the injector (currently called Collector). But folks here might consider injectInto and foldLeft too cryptic, so I decided to just shorten to into in the above list. http://groovy.codehaus.org/groovy-jdk/java/util/Collection.html#inject(groovy.lang.Closure) In the binary release I have (not sure if this is different in current source), two of the overloaded versions of the method collect create a FoldOp today (a hint), and the Collector interface has a method called accumulate and combine and is called MutableReducer in the javadoc. The methods named reduce also create FoldOp instances. This makes reduce and collect seem eerily similar. I find this a little confusing, but I have tried my best anyway to name that which by any other name seems to be more like injectInto/mapReduce/foldL/aggregate/etc. to me. Thoughts? > > I will do > > my best and find an alternative that everyone else here likes. > > Thanks. > > -Doug From brian.goetz at oracle.com Wed Jan 30 12:55:47 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 30 Jan 2013 15:55:47 -0500 Subject: Should Stream be covariant? In-Reply-To: References: Message-ID: <510988D3.8080506@oracle.com> Of course it is possible to remove the invariant BinaryOperator -- it could be replaced with BiFunction Clearly that would be less convenient in some cases, and would have some cost in API readability, since everyone knows what a BinaryOperator is but one has to read more carefully to figure out what the BiFunction is supposed to do. So I'll turn your question around to you: what benefits would we get from doing so, that would make it worth suffering the consequences of the above? On 1/30/2013 3:32 PM, Ali Lahijani wrote: > As a mental experiment, I tried to create a "constructive proof" > that Stream is covariant in the variable T. > My basic idea is that if it is covariant, it should be possible to elevate > an expression of type Stream to an expression of type > Stream in a suitable "mechanical" fashion. So I tried to implemented a > function > > public static Stream elevate(Stream delegate)... > > To implement the above method, one of course first needs to prove > that Spliterator, Iterator and Optional are covariant in T. But > they are quite straightforward. > > public static Optional elevate(Optional delegate)... > public static Iterator elevate(Iterator delegate)... > public static Spliterator elevate(Spliterator > delegate)... > > The result of the experiment is pasted below. Almost all methods pass the > test, which, I suppose, means that the Stream API has an exceptionally > clean design. > (It is worth mentioning that not all previous versions of Stream passed the > same test so smoothly.) > > But as the compiler will tell you, there is a problem with two methods, > both cases because they use BinaryOperator: > > public T reduce(T identity, BinaryOperator reducer); > public Optional reduce(BinaryOperator reducer); > > BinaryOperator is invariant in X, so passing T to BinaryOperator removes > covariance of Stream. The first method is not needed, it is just a sugar > around the third from of reduce which takes three parameters: > > public T reduce(T identity, BinaryOperator reducer) { return > reduce(identity, reducer, reducer); } > > But to be able to emulate the second form, reduce(BinaryOperator), there > should be a method to decompose a Stream as a head/tail pair. Given that > facility, one can emulate reduce(reducer) as tail.reduce(head, reducer, > reducer). > > So here is my question: is it possible that the non-covariant reduce > methods be removed, and a method for head/tail decomposition is added > instead? > > Best > Ali > From talden at gmail.com Wed Jan 30 13:04:44 2013 From: talden at gmail.com (Talden) Date: Thu, 31 Jan 2013 10:04:44 +1300 Subject: tee() In-Reply-To: <91B8E417-E3C8-433C-AAF4-918013883E54@gmail.com> References: <5101D634.2060009@cs.oswego.edu> <5101DBDA.9060705@cs.oswego.edu> <91B8E417-E3C8-433C-AAF4-918013883E54@gmail.com> Message-ID: In my JDK8-stream-like API my tee() takes a Block and I pass in a no-op block when we're not debugging (which since the debugging one logs output is decided by the level of the logger intended for this fine level of logging). Given another opportunity I'd name it peek() in my API (it's really the only way in which it's useful) but I certainly wouldn't remove it - the debugging has proven very useful. It's only used in some very specific circumstances where other debugging would not be detailed enough - most stream usage doesn't use it... you still need the tool in the toolbox occasionally though - I certainly don't want the debugging to break the streaminess of the operation by forcing full stream evaluation. -- Aaron Scott-Boddendijk On Thu, Jan 31, 2013 at 9:27 AM, Howard Lovatt wrote: > In my own stream library I have a toString method that forces evaluation. > For debugging I use the equivalent of (neglecting generics): > > Stream s = collection.stream(); > Stream f = s.filter( ... ); > Stream r = f.reduce( ... ); > > Then when debugging I step over each line and examine the intermediate > stages (usually by hovering over the variable which causes my non-lazy > toString to be called). > > Not ideal, but the best I have come up with and no need for tee(). > > -- Howard Lovatt +61 419 971 263 (sent from my PDA) > > On 25/01/2013, at 12:11 PM, Doug Lea
wrote: > > > On 01/24/13 20:00, Joe Bowbeer wrote: > >> I'm OK with peek() even though it has other meanings on IO streams. > >> > >> Is the name leak() is more to your liking? > > > > I can't think of a good name for the the little idiom of > > ....map(x -> { use(x); return x; })... > > > > Which seems more like an IDE thing than an API thing anyway. > > > > But then again, I don't like practically all of the > > convenience methods, so discount my vote accordingly. > > > > -Doug > > > > > > > >> > >> Joe > >> > >> > >> On Thu, Jan 24, 2013 at 4:47 PM, Doug Lea
>> > wrote: > >> > >> On 01/24/13 15:43, Kevin Bourrillion wrote: > >> > >> If it really must stay, I think I do like "peek" or "observe" over > >> "tee". But I > >> would love to drop it. > >> > >> > >> In case it is not too late to vote to drop this, I vote to drop it. > >> And if too late, I vote to name it something other than any of these. > >> > >> -Doug > > > From alahijani at gmail.com Wed Jan 30 14:23:13 2013 From: alahijani at gmail.com (Ali Lahijani) Date: Thu, 31 Jan 2013 01:53:13 +0330 Subject: Should Stream be covariant? In-Reply-To: References: <510988D3.8080506@oracle.com> Message-ID: On Thu, Jan 31, 2013 at 1:14 AM, Ali Lahijani wrote: > On Thu, Jan 31, 2013 at 12:25 AM, Brian Goetz wrote: > >> Of course it is possible to remove the invariant BinaryOperator -- it >> could be replaced with >> >> BiFunction >> >> Clearly that would be less convenient in some cases, and would have some >> cost in API readability, since everyone knows what a BinaryOperator is but >> one has to read more carefully to figure out what the BiFunction is >> supposed to do. >> >> So I'll turn your question around to you: what benefits would we get from >> doing so, that would make it worth suffering the consequences of the above? >> > > To be able to do a reduce even when what you have is a mere Stream extends T>, not a full Stream. > > Currently there is no type-safe way to do a seedless reduce like this: > > Stream stream = ... > Optional total = stream.reduce((s1, s2) -> s1.toString() + > s2.toString()); > > I suspect even BiFunction wouldn't > solve that. > I also kind of hoped, when that elevate() method can be implemented, it could be added as an static method in Stream. That way Stream and Stream would be truly interchangeable. Sort of declaration-site covariance for free. I mean one direction is already there: Stream is assignable from Stream. The other direction, from Stream to Stream can be added as a library method, if Stream becomes fully covariant. Streams aside, I think adding static methods to Iterator, Spliterator, Iterable, etc. which do the same kind of elevation might be useful as well. Best From joe.bowbeer at gmail.com Wed Jan 30 15:23:40 2013 From: joe.bowbeer at gmail.com (Joe Bowbeer) Date: Wed, 30 Jan 2013 15:23:40 -0800 Subject: enhanced type-inference In-Reply-To: <5102CAC6.4050108@oracle.com> References: <5102C593.5070805@oracle.com> <5102CAC6.4050108@oracle.com> Message-ID: Thanks! I picked up these changes when I installed binary snapshot b75. I updated my samples(*) accordingly and now the following static imports are working without addition type annotation: Collectors.toList ConcurrentCollectors.groupBy Character.compare Nits: 1. orElse(null) is still a nuisance. Will this be resolved? 2. ConcurrentCollectors.groupBy vs. Collectors.groupingBy (should be groupBy?) http://download.java.net/lambda/b75/docs/api/java/util/stream/ConcurrentCollectors.html#groupBy(java.util.function.Function) http://download.java.net/lambda/b75/docs/api/java/util/stream/Collectors.html#groupingBy(java.util.function.Function) 3. NetBeans jdk8lambda build #1612 is not as smart as the new compiler and flags the graph-inference lines as errors. http://bertram2.netbeans.org:8080/job/jdk8lambda/ (*) Sample projects: bitbucket.org/joebowbeer/anagrams bitbucket.org/joebowbeer/stringcompare bitbucket.org/joebowbeer/wordchainkata --Joe On Fri, Jan 25, 2013 at 10:11 AM, Brian Goetz wrote: > More info on new type inference. > > > -------- Original Message -------- > Subject: enhanced type-inference > Date: Fri, 25 Jan 2013 17:49:07 +0000 > From: Maurizio Cimadamore > > > Organization: Oracle > To: lambda-dev > > Dear lambdackers, > I've just pushed a patch that enables a more general inference support > for nested generic method calls/stuck expressions. This scheme has been > available for a while in lambda-repo (when using the hidden flag > -XDuseGraphInference), but we have now decided it's time to flip the > switch and make it the default when using JDK 8. In the past few weeks > I've been hunting down as many bugs in the new inference scheme as > possible, in order to provide a smooth transition from the old world to > the new one. I hope the transition is indeed smooth - but, given the > nature of the change, I also expect bugs to pop up here and there, so > please, keep throwing the kitchen sink at javac and report your > experience back to us; without your valuable feedback and dedication we > would never have gotten thus far. > > Example of things that now work: > > Stream si = ... > List l1 = si.into(new ArrayList<>()); //not really - too > late for that ;-) > List l2 = si.collect(toList()); > List l3 = si.collect(toCollection(**ArrayList::new)); > > Thanks > Maurizio > > > > > From brian.goetz at oracle.com Wed Jan 30 17:34:09 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 30 Jan 2013 20:34:09 -0500 Subject: enhanced type-inference In-Reply-To: References: <5102C593.5070805@oracle.com> <5102CAC6.4050108@oracle.com> Message-ID: <5109CA11.4090905@oracle.com> > 1. orElse(null) is still a nuisance. Will this be resolved? Thanks for the reminder. > 2. ConcurrentCollectors.groupBy vs. Collectors.groupingBy (should be > groupBy?) Planning to finish Collectors and then make CColectors match it. From Donald.Raab at gs.com Wed Jan 30 21:40:00 2013 From: Donald.Raab at gs.com (Raab, Donald) Date: Thu, 31 Jan 2013 00:40:00 -0500 Subject: enhanced type-inference In-Reply-To: <5102CAC6.4050108@oracle.com> References: <5102C593.5070805@oracle.com> <5102CAC6.4050108@oracle.com> Message-ID: <6712820CB52CFB4D842561213A77C05404C3A88EB0@GSCMAMP09EX.firmwide.corp.gs.com> Updated to b75 tonight and tried out the enhanced inference with the gsc kata. Much Better! Will be really nice when IntelliJ includes the same level of inference now. Here's the result with the usual complex example: Map> multimap = this.company.getCustomers() .stream() .collect(groupingBy(customer -> customer.getOrders() .stream() .explode((Stream.Downstream downstream, Order order) -> { downstream.send(order.getLineItems()); }) .map(LineItem::getValue) .reduce(0.0, (x, y) -> Math.max(x, y)))); > -----Original Message----- > From: lambda-libs-spec-experts-bounces at openjdk.java.net [mailto:lambda- > libs-spec-experts-bounces at openjdk.java.net] On Behalf Of Brian Goetz > Sent: Friday, January 25, 2013 1:11 PM > To: lambda-libs-spec-experts at openjdk.java.net > Subject: Fwd: enhanced type-inference > > More info on new type inference. > > > -------- Original Message -------- > Subject: enhanced type-inference > Date: Fri, 25 Jan 2013 17:49:07 +0000 > From: Maurizio Cimadamore > Organization: Oracle > To: lambda-dev > > Dear lambdackers, > I've just pushed a patch that enables a more general inference support for > nested generic method calls/stuck expressions. This scheme has been > available for a while in lambda-repo (when using the hidden flag - > XDuseGraphInference), but we have now decided it's time to flip the switch > and make it the default when using JDK 8. In the past few weeks I've been > hunting down as many bugs in the new inference scheme as possible, in order > to provide a smooth transition from the old world to the new one. I hope > the transition is indeed smooth - but, given the nature of the change, I > also expect bugs to pop up here and there, so please, keep throwing the > kitchen sink at javac and report your experience back to us; without your > valuable feedback and dedication we would never have gotten thus far. > > Example of things that now work: > > Stream si = ... > List l1 = si.into(new ArrayList<>()); //not really - too > late for that ;-) > List l2 = si.collect(toList()); > List l3 = si.collect(toCollection(ArrayList::new)); > > Thanks > Maurizio > > > From paul.sandoz at oracle.com Thu Jan 31 03:50:18 2013 From: paul.sandoz at oracle.com (Paul Sandoz) Date: Thu, 31 Jan 2013 12:50:18 +0100 Subject: Encounter order: take 2 Message-ID: <3843CA7B-CA6F-425A-990B-40D1250CF1AB@oracle.com> Hi, Here is an update. Thanks to Brian and Neal for their feedback. Previously i told a minor mistruth. For the terminal operation collect(toSet()) we currently do not have a way for the Collector to state that encounter order is not preserved and thus propagate that information to the collect terminal operation. Clarification: although the rules for an intermediate operation to preserve encounter order may seem complex it is cheap to implement. Paul. -- An input stream is a stream of elements input to an intermediate operation or terminal operation. An output stream is a stream of elements output from an intermediate operation. A stream source is a stream of elements input to the first operation in a sequence of zero or more intermediate operations and one final terminal operation. An intermediate operation is a computation that transforms an input stream to an output stream. A terminal operation is a computation that produces a result from an input stream. A stream source has or does not have encounter order. A stream source has encounter order if traversal of all its elements will always result in encountering those elements in the same order. A stream source does not have encounter order if traversal of all its elements may not always result in encountering those elements in the same order. List and arrays are sources that have encounter order (arrays can be said to also have a spatial order). HashSet and PriorityQueue are sources that do not have encounter order. A terminal operation preserves or does not preserve encounter order when computing a result. If the input stream has encounter order and the terminal operation preserves encounter order then the computation must respect encounter order and associativity when producing the result. (Elements may be arbitrarily grouped but order must be respected for those elements and intermediate results computed from groups of elements or previously computed intermediate results.) If the input stream has encounter order and the terminal operation does not preserve encounter order then the computation may not respect encounter order and is free to process elements of the input stream in any order when producing the result. If the input stream does not have encounter order and the terminal operation preserves encounter order then the computation may impute encounter order (directly from traversal of the input stream) when producing the result. If the input stream does not have encounter order and the terminal operation does not preserve encounter order then the computation is free to process elements of the input stream in any order when producing the result. Non-preserving terminal operations are forEach, forEachUntil, findAny, match{Any, None, All}, collect(toSet()) and collectUnordered. An intermediate operation may inject encounter order so that the output stream and corresponding input stream to the next intermediate operation or terminal operation has encounter order. The sorted() operation injects encounter. An intermediate operation may clear encounter order so that the output stream and corresponding input stream to the next intermediate operation or terminal operation does not have encounter order. There are no such operations implemented. (Previously the unordered() operation cleared encounter order.) Otherwise an intermediate operation must preserve encounter order if required to do so (see next paragraphs). An intermediate operation may choose to apply a different algorithm if encounter order of input stream must be preserved or not. The distinct() operation will, when evaluating in parallel, use a ConcurrentHashMap to store unique elements if encounter order does not need to be preserved, otherwise if encounter order needs to be preserved a fold will be performed (equivalent of, in parallel, map each element to a singleton linked set then associatively reduce, left-to-right, the linked sets to one linked set). An intermediate operation must preserve encounter order of output stream if: a.1) the input stream to the intermediate operation has encounter order (either because the stream source has encounter order or because a previous intermediate operation injects encounter order); and a.2) the terminal operation preserves encounter order. An intermediate operation may not preserve encounter order of the output stream if: b.1) the input stream to the intermediate operation does not have encounter order (either because the stream source does not have encounter order or because a previous intermediate operation clears encounter order); or b.2) the terminal operation does not preserve encounter order *and* the intermediate operation is in a sequence of operations, to be computed, where the last operation in the sequence is the terminal operation and all operations in the sequence are computed in parallel. Rule b.2 above ensures that encounter order is preserved for the following pipeline on the sequential().forEach(): list.parallelStream().distinct().sequential().forEach() i.e. the distinct() intermediate operation will preserve the encounter order of the list stream source. From tim at peierls.net Thu Jan 31 05:20:03 2013 From: tim at peierls.net (Tim Peierls) Date: Thu, 31 Jan 2013 08:20:03 -0500 Subject: Encounter order: take 2 In-Reply-To: <3843CA7B-CA6F-425A-990B-40D1250CF1AB@oracle.com> References: <3843CA7B-CA6F-425A-990B-40D1250CF1AB@oracle.com> Message-ID: On Thu, Jan 31, 2013 at 6:50 AM, Paul Sandoz wrote: > An intermediate operation must preserve encounter order of output stream > if: > > a.1) the input stream to the intermediate operation has encounter order > (either because the stream source has encounter order or because a previous > intermediate operation injects encounter order); and > a.2) the terminal operation preserves encounter order. > > An intermediate operation may not preserve encounter order of the output > stream if: > > b.1) the input stream to the intermediate operation does not have > encounter order (either because the stream source does not have encounter > order or because a previous intermediate operation clears encounter order); > or > b.2) the terminal operation does not preserve encounter order *and* the > intermediate operation is in a sequence of operations, to be computed, > where the last operation in the sequence is the terminal operation and all > operations in the sequence are computed in parallel. > Shouldn't a1 && a2 == !(b1 || b2) ? The extra condition in b2 isn't reflected in the a side. --tim From kevinb at google.com Thu Jan 31 14:24:20 2013 From: kevinb at google.com (Kevin Bourrillion) Date: Thu, 31 Jan 2013 14:24:20 -0800 Subject: Collectors update In-Reply-To: <6712820CB52CFB4D842561213A77C05404C3A88E74@GSCMAMP09EX.firmwide.corp.gs.com> References: <51084E8C.2060403@oracle.com> <6712820CB52CFB4D842561213A77C05404C3A88DCE@GSCMAMP09EX.firmwide.corp.gs.com> <5108635F.5030701@cs.oswego.edu> <51086AD8.5090309@cs.oswego.edu> <6712820CB52CFB4D842561213A77C05404C3A88DDA@GSCMAMP09EX.firmwide.corp.gs.com> <51091B49.6040904@cs.oswego.edu> <6712820CB52CFB4D842561213A77C05404C3A88E74@GSCMAMP09EX.firmwide.corp.gs.com> Message-ID: Just throwing this out there: Have we already strongly considered the heretical idea of just naming it reduce()? It's distinguished from the others by the type it takes (and that's not a lambda-able type so no worries on that front). It may not be *implemented* functionally, as 'reduce' usually is (to those users already familiar with the term), but other than that... it's conceptually very similar. If we were uncomfortable calling the type accepted Reducer, perhaps there's... MutatingReducer. Bleh. Of the others, I think aggregate(Aggregator) should be on the table. I also think collect(Collector) is still on the table. I don't think it's * too* bad, but "collect with a collector to get a collection" is pretty overloaded... On Wed, Jan 30, 2013 at 12:42 PM, Raab, Donald wrote: > In my opinion, collect should return a collection. It should not reduce > to any result. In the interest of time, here's a stab at an alternative > list I came up with using the powers of thesaurus yesterday: > > into > gather > assemble > summarize > > The functionality currently called collect feels more like > injectInto/inject in Smalltalk/Ruby/Groovy, but nothing is being injected > into the method collect directly, but by the Collector (the R in > makeResult()). InjectInto/inject is the equivalent of foldLeft. I would > be less concerned over using injectInto or inject than collect, as at least > it seems similar enough in that it can return any value, determined by the > injector (currently called Collector). But folks here might consider > injectInto and foldLeft too cryptic, so I decided to just shorten to into > in the above list. > > > http://groovy.codehaus.org/groovy-jdk/java/util/Collection.html#inject(groovy.lang.Closure) > > In the binary release I have (not sure if this is different in current > source), two of the overloaded versions of the method collect create a > FoldOp today (a hint), and the Collector interface has a method called > accumulate and combine and is called MutableReducer in the javadoc. The > methods named reduce also create FoldOp instances. This makes reduce and > collect seem eerily similar. > > I find this a little confusing, but I have tried my best anyway to name > that which by any other name seems to be more like > injectInto/mapReduce/foldL/aggregate/etc. to me. > > Thoughts? > > > > I will do > > > my best and find an alternative that everyone else here likes. > > > > Thanks. > > > > -Doug > > -- Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com From brian.goetz at oracle.com Thu Jan 31 14:37:08 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 31 Jan 2013 17:37:08 -0500 Subject: Collectors update In-Reply-To: References: <51084E8C.2060403@oracle.com> <6712820CB52CFB4D842561213A77C05404C3A88DCE@GSCMAMP09EX.firmwide.corp.gs.com> <5108635F.5030701@cs.oswego.edu> <51086AD8.5090309@cs.oswego.edu> <6712820CB52CFB4D842561213A77C05404C3A88DDA@GSCMAMP09EX.firmwide.corp.gs.com> <51091B49.6040904@cs.oswego.edu> <6712820CB52CFB4D842561213A77C05404C3A88E74@GSCMAMP09EX.firmwide.corp.gs.com> Message-ID: <510AF214.1030702@oracle.com> > Just throwing this out there: Have we already strongly considered the > heretical idea of just naming it reduce()? Yes. There are a few reasons we leaned towards a different name here. 1. (Pedagogical): The *mutable* reduce-like/fold-like methods have a different feel than the more functional analogues. 2. (Practical): Overloading methods that take the same-shape lambdas causes use-site ambiguity problems. Since there are reduce forms that look suspiciously like collect forms, this was an issue. > It's distinguished from the others by the type it takes (and that's not > a lambda-able type so no worries on that front). No, because there's the multi-arg collect() form. Which is not only useful (collect(ArrayList::new, ArrayList::add, ArrayList::addAll)) but also usefully pedagogically (in that the single-arg collect then becomes clearly a generalization of the three-arg one.) > If we were uncomfortable calling the type accepted Reducer, perhaps > there's... MutatingReducer. Bleh. We had that once! MutableReduce, it was. Though mutableReduceUnordered is a mouthful. From paul.sandoz at oracle.com Thu Jan 31 14:42:19 2013 From: paul.sandoz at oracle.com (Paul Sandoz) Date: Thu, 31 Jan 2013 23:42:19 +0100 Subject: Encounter order: take 2 In-Reply-To: References: <3843CA7B-CA6F-425A-990B-40D1250CF1AB@oracle.com> Message-ID: <1F89BCC8-B2CE-4BEE-B09A-DB9E266D3150@oracle.com> On Jan 31, 2013, at 2:20 PM, Tim Peierls wrote: > On Thu, Jan 31, 2013 at 6:50 AM, Paul Sandoz wrote: > An intermediate operation must preserve encounter order of output stream if: > > a.1) the input stream to the intermediate operation has encounter order (either because the stream source has encounter order or because a previous intermediate operation injects encounter order); and > a.2) the terminal operation preserves encounter order. > > An intermediate operation may not preserve encounter order of the output stream if: > > b.1) the input stream to the intermediate operation does not have encounter order (either because the stream source does not have encounter order or because a previous intermediate operation clears encounter order); or > b.2) the terminal operation does not preserve encounter order *and* the intermediate operation is in a sequence of operations, to be computed, where the last operation in the sequence is the terminal operation and all operations in the sequence are computed in parallel. > > Shouldn't a1 && a2 == !(b1 || b2) ? The extra condition in b2 isn't reflected in the a side. > Well spotted finding the hole. I think it may be easier to state something like: -- An intermediate operation must preserve encounter order of the output stream unless one of the following conditions is true, which if so the intermediate operation may not preserve encounter order: 1) the input stream to the intermediate operation does not have encounter order (either because the stream source does not have encounter order or because a previous intermediate operation clears encounter order). 2) the terminal operation does not preserve encounter order *and* the intermediate operation is in a sequence of operations, to be computed, where the last operation in the sequence is the terminal operation and all operations in the sequence are computed in parallel. -- Paul. From tim at peierls.net Thu Jan 31 14:55:09 2013 From: tim at peierls.net (Tim Peierls) Date: Thu, 31 Jan 2013 17:55:09 -0500 Subject: Encounter order: take 2 In-Reply-To: <1F89BCC8-B2CE-4BEE-B09A-DB9E266D3150@oracle.com> References: <3843CA7B-CA6F-425A-990B-40D1250CF1AB@oracle.com> <1F89BCC8-B2CE-4BEE-B09A-DB9E266D3150@oracle.com> Message-ID: On Thu, Jan 31, 2013 at 5:42 PM, Paul Sandoz wrote: > An intermediate operation must preserve encounter order of the output > stream unless one of the following conditions is true, which if so the > intermediate operation may not preserve encounter order: > How about: An intermediate operation must preserve encounter order of the output stream only if neither of the following conditions is true: I think it's pretty clear that the logical negation of "must preserve" is "need not preserve". Spelling it out only makes the reader wonder if they missing something. --tim From kevinb at google.com Thu Jan 31 15:02:14 2013 From: kevinb at google.com (Kevin Bourrillion) Date: Thu, 31 Jan 2013 15:02:14 -0800 Subject: Collectors update In-Reply-To: <510AF214.1030702@oracle.com> References: <51084E8C.2060403@oracle.com> <6712820CB52CFB4D842561213A77C05404C3A88DCE@GSCMAMP09EX.firmwide.corp.gs.com> <5108635F.5030701@cs.oswego.edu> <51086AD8.5090309@cs.oswego.edu> <6712820CB52CFB4D842561213A77C05404C3A88DDA@GSCMAMP09EX.firmwide.corp.gs.com> <51091B49.6040904@cs.oswego.edu> <6712820CB52CFB4D842561213A77C05404C3A88E74@GSCMAMP09EX.firmwide.corp.gs.com> <510AF214.1030702@oracle.com> Message-ID: I'm going to have to frequently apologize for the circular nature of many of my comments. I wish that I'd always had time to follow the discussions from the start. On Thu, Jan 31, 2013 at 2:37 PM, Brian Goetz wrote: > Just throwing this out there: Have we already strongly considered the >> heretical idea of just naming it reduce()? >> > > Yes. There are a few reasons we leaned towards a different name here. > > 1. (Pedagogical): The *mutable* reduce-like/fold-like methods have a > different feel than the more functional analogues. > Do they truly, from the consuming perspective? I'm not sure. > 2. (Practical): Overloading methods that take the same-shape lambdas > causes use-site ambiguity problems. Since there are reduce forms that look > suspiciously like collect forms, this was an issue. > > It's distinguished from the others by the type it takes (and that's not >> a lambda-able type so no worries on that front). >> > > No, because there's the multi-arg collect() form. Which is not only > useful (collect(ArrayList::new, ArrayList::add, ArrayList::addAll)) but > also usefully pedagogically (in that the single-arg collect then becomes > clearly a generalization of the three-arg one.) I strongly question the idea that that three-arg collect() method is helping anyone pedagogically. I've come across it many times and even I don't have the patience to sit down and try to puzzle out what it does and why I need it. (That's my reaction to probably 50% of the API, btw.) Why is it a collect() method instead of just another way to get a Collector? If we were uncomfortable calling the type accepted Reducer, perhaps > >> there's... MutatingReducer. Bleh. >> > > We had that once! MutableReduce, it was. Though mutableReduceUnordered > is a mouthful. > And again, I'm not sure that the extra word is needed from the consumer's perspective; more for the implementor (of the thing now called Collector). -- Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com From mike.duigou at oracle.com Thu Jan 31 15:27:17 2013 From: mike.duigou at oracle.com (Mike Duigou) Date: Thu, 31 Jan 2013 15:27:17 -0800 Subject: Encounter order: take 2 In-Reply-To: <3843CA7B-CA6F-425A-990B-40D1250CF1AB@oracle.com> References: <3843CA7B-CA6F-425A-990B-40D1250CF1AB@oracle.com> Message-ID: On Jan 31 2013, at 03:50 , Paul Sandoz wrote: > Hi, > > Here is an update. Thanks to Brian and Neal for their feedback. > > Previously i told a minor mistruth. For the terminal operation collect(toSet()) we currently do not have a way for the Collector to state that encounter order is not preserved and thus propagate that information to the collect terminal operation. > > Clarification: although the rules for an intermediate operation to preserve encounter order may seem complex it is cheap to implement. > > Paul. > > -- > > An input stream is a stream of elements input to an intermediate operation or terminal operation. > > An output stream is a stream of elements output from an intermediate operation. > > A stream source is a stream of elements input to the first operation in a sequence of zero or more intermediate operations and one final terminal operation. > > An intermediate operation is a computation that transforms an input stream to an output stream. > > A terminal operation is a computation that produces a result from an input stream. > > A stream source has or does not have encounter order. > A stream source has encounter order if traversal of all its elements will always result in encountering those elements in the same order. > A stream source does not have encounter order if traversal of all its elements may not always result in encountering those elements in the same order. > List and arrays are sources that have encounter order (arrays can be said to also have a spatial order). > HashSet and PriorityQueue are sources that do not have encounter order. > > A terminal operation preserves or does not preserve encounter order when computing a result. > If the input stream has encounter order and the terminal operation preserves encounter order then the computation must respect encounter order and associativity when producing the result. (Elements may be arbitrarily grouped but order must be respected for those elements and intermediate results computed from groups of elements or previously computed intermediate results.) > If the input stream has encounter order and the terminal operation does not preserve encounter order then the computation may not respect encounter order and is free to process elements of the input stream in any order when producing the result. > If the input stream does not have encounter order and the terminal operation preserves encounter order then the computation may impute encounter order (directly from traversal of the input stream) when producing the result. > If the input stream does not have encounter order and the terminal operation does not preserve encounter order then the computation is free to process elements of the input stream in any order when producing the result. > Non-preserving terminal operations are forEach, forEachUntil, findAny, match{Any, None, All}, collect(toSet()) and collectUnordered. > > An intermediate operation may inject encounter order so that the output stream and corresponding input stream to the next intermediate operation or terminal operation has encounter order. > The sorted() operation injects encounter. encounter order. > > An intermediate operation may clear encounter order so that the output stream and corresponding input stream to the next intermediate operation or terminal operation does not have encounter order. > There are no such operations implemented. > (Previously the unordered() operation cleared encounter order.) > > Otherwise an intermediate operation must preserve encounter order if required to do so (see next paragraphs). > > An intermediate operation may choose to apply a different algorithm if encounter order of input stream must be preserved or not. > The distinct() operation will, when evaluating in parallel, use a ConcurrentHashMap to store unique elements if encounter order does not need to be preserved, otherwise if encounter order needs to be preserved a fold will be performed (equivalent of, in parallel, map each element to a singleton linked set then associatively reduce, left-to-right, the linked sets to one linked set). Without unordered() how is the CHM version accessed if the source is an ArrayList? > An intermediate operation must preserve encounter order of output stream if: > > a.1) the input stream to the intermediate operation has encounter order (either because the stream source has encounter order or because a previous intermediate operation injects encounter order); and > a.2) the terminal operation preserves encounter order. > > An intermediate operation may not preserve encounter order of the output stream if: > > b.1) the input stream to the intermediate operation does not have encounter order (either because the stream source does not have encounter order or because a previous intermediate operation clears encounter order); or > b.2) the terminal operation does not preserve encounter order *and* the intermediate operation is in a sequence of operations, to be computed, where the last operation in the sequence is the terminal operation and all operations in the sequence are computed in parallel. > > Rule b.2 above ensures that encounter order is preserved for the following pipeline on the sequential().forEach(): > > list.parallelStream().distinct().sequential().forEach() > > i.e. the distinct() intermediate operation will preserve the encounter order of the list stream source. I find this result surprising (and disappointing). Users are going to be surprised by the poor performance of using parallelStream in this case. Mike. From brian.goetz at oracle.com Thu Jan 31 18:31:31 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 31 Jan 2013 21:31:31 -0500 Subject: Javadoc conventions in the presence of default methods Message-ID: <510B2903.4070000@oracle.com> We've tried this thread a few times without success, so let's try it again. There have been a number of cases where its not obvious how to document default methods. After some analysis, this appears to be just another case where the complexity was present in Java from day 1, and default methods simply bring it to the fore because the confusing cases are expected to come up more often. The following applies equally well to methods in abstract classes (or concrete classes) as to defaults. There are lots of things we might want to document about a method in an API. Historically we've framed them as either being "specification" (e.g., necessary postconditions) or "implementation notes" (e.g., hints that give the user an idea what's going on under the hood.) But really, there are four boxes (and we've been cramming them into two): { API, implementation } x { specification, notes } (We sometimes use the terms normative/informative to describe the difference between specification/notes.) As background, here are some example uses for default methods which vary in their "expected prevalence of overriding". I think the variety of use cases here have contributed to the confusion on how to document implementation characteristics. (Note that all of these have analogues in abstract classes too, one can find examples in Abstract{List,Map,Set}.) 1. Optional methods. This is when the default implementation is barely conformant, such as the following from Iterator: public default void remove() { throw new UnsupportedOperationException("remove"); } It adheres to its contract, because the contract is explicitly weak, but any class that cares about removal will definitely want to override it. 2. Methods with *reasonable* defaults but which might well be overridden by implementations that care enough. For example, again from Iterator: default void forEach(Consumer consumer) { while (hasNext()) consumer.accept(next()); } This implementation is perfectly fine for most implementations, but some classes (e.g., ArrayList) might have the chance to do better, if their maintainers are sufficiently motivated to do so. The new methods on Map (e.g., putIfAbsent) are also in this bucket. 3. Methods where its pretty unlikely anyone will ever override them, such as this method from Predicate: public default Predicate and(Predicate p) { Objects.requireNonNull(p); return (T t) -> test(t) && p.test(t); } These are all common enough cases. The primary reason that the Javadoc needs to provide some information about the implementation, separate from the API specification, is so that those who would extend these classes or interfaces can know which methods they need to / want to override. It should be clear from the doc that anyone who implements Iterator MUST implement remove() if they want removal to happen, CAN override forEach if they think it will result in better performance, and almost certainly don't need to override Predicate.and(). The question is made more complicated by the prevalent use of the ambiguous phrase "this implementation." We often use "this implementation" to describe both normative and informative aspects of the implementation, and readers are left to guess which. (Does "this implementation" mean all versions of Oracle's JDK forever? The current version in Oracle's JDK? All versions of all JDKs? The implementation in a specific class? Could IBM's JDK throw a different exception from UOE from the default of Iterator.remove()? What happens when the doc is @inheritDoc'ed into a subclass that overrides the method? Etc. The phrase is too vague to be useful, and this vagueness has been the subject of many bug report.) I think one measure of success of this effort should be "can we replace all uses of 'this implementation' with something that is more informative and fits neatly within the model." As said earlier, there are four boxes. Here are some descriptions of what belongs in each box. 1. API specification. This is the one we know and love; a description that applies equally to all valid implementations of the method, including preconditions, postconditions, etc. 2. API notes. Commentary, rationale, or examples pertaining to the API. 3. Implementation specification. This is where we say what it means to be a valid default implementation (or an overrideable implementation in a class), such as "throws UOE." Similarly this is where we'd describe what the default for putIfAbsent does. It is from this box that the would-be-implementer gets enough information to make a sensible decision as to whether or not to override. 4. Implementation notes. Informative notes about the implementation, such as performance characteristics that are specific to the implementation in this class in this JDK in this version, and might change. These things are allowed to vary across platforms, vendors and versions. Once we recognize that these are the four boxes, I think everything gets simpler. Strawman Proposal ----------------- As a strawman proposal, here's one way to explicitly label the four boxes: add three new Javadoc tags, @apinote, @implspec, and @implnote. (The remaining box, API Spec, needs no new tag, since that's how Javadoc is used already.) @impl{spec,note} can apply equally well to a concrete method in a class or a default method in an interface. (Rule of engagement: bikeshedding the names will be interpreted as a waiver to ever say anything about the model or the semantics. So you may bikeshed, but it must be your last word on the topic.) /** * ... API specifications ... * * @apinote * ... API notes ... * * @implspec * ... implementation specification ... * * @implnote * ... implementation notes ... * * @param ... * @return ... */ Applying this to some existing Javadoc, take AbstractMap.putAll: Copies all of the mappings from the specified map to this map (optional operation). The effect of this call is equivalent to that of calling put(k, v) on this map once for each mapping from key k to value v in the specified map. The behavior of this operation is undefined if the specified map is modified while the operation is in progress. This implementation iterates over the specified map's entrySet() collection, and calls this map's put operation once for each entry returned by the iteration. The first paragraph is API specification and the second is implementation *specification*, as users expect the implementation in AbstractMap, regardless of version or vendor, to behave this way. The change here would be to replace "This implementation" with @implspec, and the ambiguity over "this implementation" goes away. The doc for Iterator.remove could be: /** * Removes from the underlying collection the last element returned by * this iterator (optional operation). This method can be called only * once per call to next(). The behavior of an iterator is unspecified * if the underlying collection is modified while the iteration is in * progress in any way other than by calling this method. * * @implspec * The default implementation must throw UnsupportedOperationException. * * @implnote * For purposes of efficiency, the same UnsupportedOperationException * instance is always thrown. [*] */ [*] We don't really intend to implement it this way; this is just an example of an @implnote. The doc for Map.putIfAbsent could be: /** * If the specified key is not already associated with a value, associates * it with the given value. * * @implspec * Th default behaves as if: *
 {@code
    * if (!map.containsKey(key))
    *   return map.put(key, value);
    * else
    *   return map.get(key);
    * } 
* * @implnote * This default implementation is implemented essentially as described * in the API note. This operation is not atomic. Atomicity, if desired, * must be provided by a subclass implementation. */ Secondary: one can build on this to eliminate some common inheritance anomalies by making these inheritable separately, where @inheritDoc is interpreted as "inherit the stuff from the corresponding section." This is backward compatible because these sections do not yet exist in old docs. SO to inherit API spec and implementation spec, you would do: /** * {@inheritDoc} * @implspec * {@inheritDoc} * ... */ From Donald.Raab at gs.com Thu Jan 31 20:29:57 2013 From: Donald.Raab at gs.com (Raab, Donald) Date: Thu, 31 Jan 2013 23:29:57 -0500 Subject: Collectors update In-Reply-To: <510AF214.1030702@oracle.com> References: <51084E8C.2060403@oracle.com> <6712820CB52CFB4D842561213A77C05404C3A88DCE@GSCMAMP09EX.firmwide.corp.gs.com> <5108635F.5030701@cs.oswego.edu> <51086AD8.5090309@cs.oswego.edu> <6712820CB52CFB4D842561213A77C05404C3A88DDA@GSCMAMP09EX.firmwide.corp.gs.com> <51091B49.6040904@cs.oswego.edu> <6712820CB52CFB4D842561213A77C05404C3A88E74@GSCMAMP09EX.firmwide.corp.gs.com> <510AF214.1030702@oracle.com> Message-ID: <6712820CB52CFB4D842561213A77C05404C3A88F64@GSCMAMP09EX.firmwide.corp.gs.com> How about reduceInPlace? Favors the functional form of reduce, and aligns in IDE alongside it in a method list. > > We had that once! MutableReduce, it was. Though mutableReduceUnordered is > a mouthful. From joe.bowbeer at gmail.com Thu Jan 31 20:37:08 2013 From: joe.bowbeer at gmail.com (Joe Bowbeer) Date: Thu, 31 Jan 2013 20:37:08 -0800 Subject: Javadoc conventions in the presence of default methods In-Reply-To: <510B2903.4070000@oracle.com> References: <510B2903.4070000@oracle.com> Message-ID: I don't think a proposal like this would be necessary except for the inheritance aspect. Otherwise, some conventions such as "the default implementation" versus "this implementation" would suffice. I'd like to see more examples, including a few layers of subclasses, where the doc contains both implspec and implnote. The Input/OutputStream classes and Swing Component classes are a good source for material. On Thu, Jan 31, 2013 at 6:31 PM, Brian Goetz wrote: > We've tried this thread a few times without success, so let's try it again. > > There have been a number of cases where its not obvious how to document > default methods. After some analysis, this appears to be just another case > where the complexity was present in Java from day 1, and default methods > simply bring it to the fore because the confusing cases are expected to > come up more often. The following applies equally well to methods in > abstract classes (or concrete classes) as to defaults. > > There are lots of things we might want to document about a method in an > API. Historically we've framed them as either being "specification" (e.g., > necessary postconditions) or "implementation notes" (e.g., hints that give > the user an idea what's going on under the hood.) But really, there are > four boxes (and we've been cramming them into two): > > { API, implementation } x { specification, notes } > > (We sometimes use the terms normative/informative to describe the > difference between specification/notes.) > > As background, here are some example uses for default methods which vary > in their "expected prevalence of overriding". I think the variety of use > cases here have contributed to the confusion on how to document > implementation characteristics. (Note that all of these have analogues in > abstract classes too, one can find examples in Abstract{List,Map,Set}.) > > 1. Optional methods. This is when the default implementation is barely > conformant, such as the following from Iterator: > > public default void remove() { > throw new UnsupportedOperationException(**"remove"); > } > > It adheres to its contract, because the contract is explicitly weak, but > any class that cares about removal will definitely want to override it. > > 2. Methods with *reasonable* defaults but which might well be overridden > by implementations that care enough. For example, again from Iterator: > > default void forEach(Consumer consumer) { > while (hasNext()) > consumer.accept(next()); > } > > This implementation is perfectly fine for most implementations, but some > classes (e.g., ArrayList) might have the chance to do better, if their > maintainers are sufficiently motivated to do so. The new methods on Map > (e.g., putIfAbsent) are also in this bucket. > > 3. Methods where its pretty unlikely anyone will ever override them, such > as this method from Predicate: > > public default Predicate and(Predicate p) { > Objects.requireNonNull(p); > return (T t) -> test(t) && p.test(t); > } > > These are all common enough cases. The primary reason that the Javadoc > needs to provide some information about the implementation, separate from > the API specification, is so that those who would extend these classes or > interfaces can know which methods they need to / want to override. It > should be clear from the doc that anyone who implements Iterator MUST > implement remove() if they want removal to happen, CAN override forEach if > they think it will result in better performance, and almost certainly don't > need to override Predicate.and(). > > > The question is made more complicated by the prevalent use of the > ambiguous phrase "this implementation." We often use "this implementation" > to describe both normative and informative aspects of the implementation, > and readers are left to guess which. (Does "this implementation" mean all > versions of Oracle's JDK forever? The current version in Oracle's JDK? > All versions of all JDKs? The implementation in a specific class? Could > IBM's JDK throw a different exception from UOE from the default of > Iterator.remove()? What happens when the doc is @inheritDoc'ed into a > subclass that overrides the method? Etc. The phrase is too vague to be > useful, and this vagueness has been the subject of many bug report.) > > I think one measure of success of this effort should be "can we replace > all uses of 'this implementation' with something that is more informative > and fits neatly within the model." > > > As said earlier, there are four boxes. Here are some descriptions of what > belongs in each box. > > 1. API specification. This is the one we know and love; a description > that applies equally to all valid implementations of the method, including > preconditions, postconditions, etc. > > 2. API notes. Commentary, rationale, or examples pertaining to the API. > > 3. Implementation specification. This is where we say what it means to > be a valid default implementation (or an overrideable implementation in a > class), such as "throws UOE." Similarly this is where we'd describe what > the default for putIfAbsent does. It is from this box that the > would-be-implementer gets enough information to make a sensible decision as > to whether or not to override. > > 4. Implementation notes. Informative notes about the implementation, > such as performance characteristics that are specific to the implementation > in this class in this JDK in this version, and might change. These things > are allowed to vary across platforms, vendors and versions. > > Once we recognize that these are the four boxes, I think everything gets > simpler. > > > Strawman Proposal > ----------------- > > As a strawman proposal, here's one way to explicitly label the four boxes: > add three new Javadoc tags, @apinote, @implspec, and @implnote. (The > remaining box, API Spec, needs no new tag, since that's how Javadoc is used > already.) @impl{spec,note} can apply equally well to a concrete method in > a class or a default method in an interface. > > (Rule of engagement: bikeshedding the names will be interpreted as a > waiver to ever say anything about the model or the semantics. So you may > bikeshed, but it must be your last word on the topic.) > > /** > * ... API specifications ... > * > * @apinote > * ... API notes ... > * > * @implspec > * ... implementation specification ... > * > * @implnote > * ... implementation notes ... > * > * @param ... > * @return ... > */ > > Applying this to some existing Javadoc, take AbstractMap.putAll: > > Copies all of the mappings from the specified map to this map > (optional operation). The effect of this call is equivalent to > that of calling put(k, v) on this map once for each mapping from > key k to value v in the specified map. The behavior of this > operation is undefined if the specified map is modified while > the operation is in progress. > > This implementation iterates over the specified map's > entrySet() collection, and calls this map's put operation > once for each entry returned by the iteration. > > The first paragraph is API specification and the second is implementation > *specification*, as users expect the implementation in AbstractMap, > regardless of version or vendor, to behave this way. The change here would > be to replace "This implementation" with @implspec, and the ambiguity over > "this implementation" goes away. > > The doc for Iterator.remove could be: > > /** > * Removes from the underlying collection the last element returned by > * this iterator (optional operation). This method can be called only > * once per call to next(). The behavior of an iterator is unspecified > * if the underlying collection is modified while the iteration is in > * progress in any way other than by calling this method. > * > * @implspec > * The default implementation must throw UnsupportedOperationException. > * > * @implnote > * For purposes of efficiency, the same UnsupportedOperationException > * instance is always thrown. [*] > */ > > [*] We don't really intend to implement it this way; this is just an > example of an @implnote. > > > The doc for Map.putIfAbsent could be: > > /** > * If the specified key is not already associated with a value, > associates > * it with the given value. > * > * @implspec > * Th default behaves as if: > *
 {@code
>    * if (!map.containsKey(key))
>    *   return map.put(key, value);
>    * else
>    *   return map.get(key);
>    * } 
> * > * @implnote > * This default implementation is implemented essentially as described > * in the API note. This operation is not atomic. Atomicity, if desired, > * must be provided by a subclass implementation. > */ > > > Secondary: one can build on this to eliminate some common inheritance > anomalies by making these inheritable separately, where @inheritDoc is > interpreted as "inherit the stuff from the corresponding section." This is > backward compatible because these sections do not yet exist in old docs. > SO to inherit API spec and implementation spec, you would do: > > /** > * {@inheritDoc} > * @implspec > * {@inheritDoc} > * ... > */ > From joe.bowbeer at gmail.com Thu Jan 31 20:41:50 2013 From: joe.bowbeer at gmail.com (Joe Bowbeer) Date: Thu, 31 Jan 2013 20:41:50 -0800 Subject: Collectors update In-Reply-To: <6712820CB52CFB4D842561213A77C05404C3A88F64@GSCMAMP09EX.firmwide.corp.gs.com> References: <51084E8C.2060403@oracle.com> <6712820CB52CFB4D842561213A77C05404C3A88DCE@GSCMAMP09EX.firmwide.corp.gs.com> <5108635F.5030701@cs.oswego.edu> <51086AD8.5090309@cs.oswego.edu> <6712820CB52CFB4D842561213A77C05404C3A88DDA@GSCMAMP09EX.firmwide.corp.gs.com> <51091B49.6040904@cs.oswego.edu> <6712820CB52CFB4D842561213A77C05404C3A88E74@GSCMAMP09EX.firmwide.corp.gs.com> <510AF214.1030702@oracle.com> <6712820CB52CFB4D842561213A77C05404C3A88F64@GSCMAMP09EX.firmwide.corp.gs.com> Message-ID: For example, used as follows? stream.reduceInPlace(groupingBy..) stream.reduceInPlaceUnordered(groupingBy..) --Joe On Thu, Jan 31, 2013 at 8:29 PM, Raab, Donald wrote: > How about reduceInPlace? Favors the functional form of reduce, and aligns > in IDE alongside it in a method list. > > > > > We had that once! MutableReduce, it was. Though mutableReduceUnordered > is > > a mouthful. > > From Donald.Raab at gs.com Thu Jan 31 21:41:52 2013 From: Donald.Raab at gs.com (Raab, Donald) Date: Fri, 1 Feb 2013 00:41:52 -0500 Subject: Collectors update In-Reply-To: References: Message-ID: <6712820CB52CFB4D842561213A77C05404C3A88F67@GSCMAMP09EX.firmwide.corp.gs.com> Yes. From: Joe Bowbeer [mailto:joe.bowbeer at gmail.com] Sent: Thursday, January 31, 2013 11:42 PM To: Raab, Donald [Tech] Cc: Brian Goetz; Kevin Bourrillion; lambda-libs-spec-experts at openjdk.java.net Subject: Re: Collectors update For example, used as follows? stream.reduceInPlace(groupingBy..) stream.reduceInPlaceUnordered(groupingBy..) --Joe On Thu, Jan 31, 2013 at 8:29 PM, Raab, Donald > wrote: How about reduceInPlace? Favors the functional form of reduce, and aligns in IDE alongside it in a method list. > > We had that once! MutableReduce, it was. Though mutableReduceUnordered is > a mouthful. From pbenedict at apache.org Tue Jan 22 15:29:18 2013 From: pbenedict at apache.org (Paul Benedict) Date: Tue, 22 Jan 2013 23:29:18 -0000 Subject: hg: lambda/lambda/jdk: - remove Spliterator.iterator(), see Streams.iteratorFrom for equivalent Message-ID: Regarding trySplit/tryAdvance, methods that start with "try" indicate they throw an exception upon failing to split or advance. Brian, but you did indicate at least the first can return null, right? So why not just remove "try" and name them split()/advance()? I think this pattern makes sense: tryABC -> returns element or throws an exception ABC -> returns element or null Paul