Multiple return (was: JEP 186: Collection Literals)
Alessio Stalla
alessiostalla at gmail.com
Wed Jan 22 08:26:43 PST 2014
FWIW, I know multiple return values from Common Lisp and can share my
experience with them.
First, they are generally implemented using what a Java-like language would
model as value types (in other words, they live on the stack), but this
only an implementation detail: a major attractive is that you only have to
be aware of extra return values where you actually want to use them;
otherwise, you can just pretend there's a single return value (the other
ones are silently discarded and there is no syntactic difference).
Second, they're never used for exception handling (although some of their
uses might be conceivably implemented with exceptions in Java). I've seen
them used in a number of scenarios:
* math functions (e.g. return quotient and rest from integer division);
* to signal a condition of "value absent" when getting values out of maps
and similar data structures, to distinguish between null as a value
associated to some key vs null meaning "there is no value associated to
this key";
* generally, when there is one "strong" return value but one wants to
return auxiliary values that happen to be handy some of the time, without
imposing a holder object.
I believe the first point is important, otherwise there's no clear
advantage in having multiple-valued return over simply having value types.
Cheers,
Alessio
On Wed, Jan 22, 2014 at 5:09 PM, Zhong Yu <zhong.j.yu at gmail.com> wrote:
> I'm not sure how you can do this way with a more complex flow graph.
>
> On Tue, Jan 21, 2014 at 11:22 PM, Sam Pullara <spullara at gmail.com> wrote:
> > I would implement this with a different pattern:
> >
> > search()
> > .left( s -> … )
> > .right( e -> …);
> >
> > Though I think for this case you might name it Try with onSuccess and
> onFailure.
> >
> > Sam
> >
> > On Jan 21, 2014, at 8:13 PM, Zhong Yu <zhong.j.yu at gmail.com> wrote:
> >
> >> There is value in meticulously handling error condition after each
> >> single action. Sometimes I wish Java had a simpler syntax to do just
> >> that (try-catch is too clunky). Go's solution for this kind of error
> >> handling strategy is syntactically beautiful. If someone wants to
> >> emulate that strategy in Java, `Either` won't work as beautifully
> >> (correct me if I'm wrong - but limit the solution to simple C style
> >> code)
> >>
> >> Either<String, ErrorCode> search();
> >>
> >> // call site
> >> Either<String, ErrorCode> result = search();
> >> if(result.isRight()) // if it's right, it's wrong!
> >> handle result.getRight();
> >> else
> >> use result.getLeft();
> >> // what the hell is this code talking about?
> >>
> >>
> >> Zhong Yu
> >>
> >>
> >>
> >> On Tue, Jan 21, 2014 at 6:58 PM, Jed Wesley-Smith <jed at wesleysmith.io>
> wrote:
> >>> Also note that a common use for multiple returns (for instance in the
> Go
> >>> language) is to support error reporting as opposed to throwing
> exceptions.
> >>> This pattern has a number of shortcomings however. Not least is that it
> >>> requires the caller to manually check for the presence of an error,
> and if
> >>> not present then to use the happy path result. This is a failure of
> >>> modelling. Logically, there is not actually a result AND an error, but
> a
> >>> result OR an error – in other words the result isn't a conjunction,
> but a
> >>> disjunction of result and error.
> >>>
> >>> Fortunately, there are disjunctive union types such as Either<A, B>
> that
> >>> can model this kind of relationship in a far superior form to the Go
> >>> approach (who, as a consequence of not having parametric polymorphism
> >>> cannot conveniently use that kind of solution, and are stuck with an
> >>> if-error else proceed check for every call).
> >>>
> >>> Personally, I'd love it if Java could better support ADTs and pattern
> >>> matching, and add higher-kinded-types while you're at it :-)
> >>>
> >>> But of course the number one feature Java needs is Tail Recursion
> >>> Optimisation!
> >>>
> >>> cheers,
> >>> jed
> >>>
> >>>
> >>> On 22 January 2014 11:02, Brian Goetz <brian.goetz at oracle.com> wrote:
> >>>
> >>>> Without getting into syntax....
> >>>>
> >>>> One could argue that Java has multiple return today -- you write a
> Pair
> >>>> class, and return an instance of it.
> >>>>
> >>>> However, this would likely be received as an unsatisfying answer.
> Why?
> >>>> - Performance costs -- having to instantiate a Pair, and possibly
> having
> >>>> to box any primitive components
> >>>> - Having to write the Pair class.
> >>>>
> >>>> I suspect that the first reason is at the heart of most people's
> >>>> objections -- that having to use an object "feels" too heavy (even if
> the
> >>>> VM is able to eliminate the cost via escape analysis.)
> >>>>
> >>>> In any case, value types would certainly address the performance
> issue --
> >>>> returning a 2-tuple would just put the values in registers/on the
> stack.
> >>>>
> >>>> Syntactically, there would certainly be some way to describe a pair
> >>>> literal, to be determined (but not now.)
> >>>>
> >>>> Once you have that, multiple return really isn't needed as a
> standalone
> >>>> feature.
> >>>>
> >>>> On Jan 21, 2014, at 5:59 PM, Roel Spilker wrote:
> >>>>
> >>>>> As a Lombok developer I have quite some experience with value types.
> I
> >>>> do think value types are very important.
> >>>>>
> >>>>> That said, I notice that most of them are used to return two or more
> >>>> values from a method. In concurrent programming this is even more
> >>>> important. The current workaround in AtomicMarkableReference and
> >>>> AtomicStampedReference is to return one value and put the other value
> in
> >>>> the array that was passed as a parameter.
> >>>>>
> >>>>> There is a way to have multiple return values without introducing new
> >>>> types (at least for the java programmer). At the risk of getting into
> a
> >>>> syntax discussion, one possible way would be to only allow the
> results to
> >>>> be assigned to fields, local variables or parameters directly.
> Example:
> >>>>>
> >>>>> public {int, String} foo() {
> >>>>> return {1, "foo"};
> >>>>> }
> >>>>>
> >>>>> void printFoo() {
> >>>>> {int count, String message} = foo();
> >>>>> for (int i = 0; i < count, i++) System.out.println(message);
> >>>>>
> >>>>> // call a method using the multiple results
> >>>>> String repeated = repeat(foo(), false);
> >>>>> }
> >>>>>
> >>>>> String repeat(int count, String message, boolean newLines) {
> >>>>> String result = "";
> >>>>> for (int i = 0; i < count, i++) {
> >>>>> result += message;
> >>>>> if (newLines) result += "\n";
> >>>>> }
> >>>>>
> >>>>> Would having multiple return values be part of the design space?
> >>>>>
> >>>>>
> >>>>> On Tue, Jan 21, 2014 at 8:39 PM, Brian Goetz <brian.goetz at oracle.com
> >
> >>>> wrote:
> >>>>> So, there's been some good discussion regarding the proper scope of
> >>>>> Collection Literals, so let me summarize what I heard. I am sensing
> >>>>> that the center of gravity is roughly behind these points:
> >>>>>
> >>>>> 1. A "collection literals" feature that only worked for the built-in
> >>>>> types (e.g., List, Set, Map), but was not extensible to user-defined
> >>>>> types, would be disappointing.
> >>>>>
> >>>>> 2. Having these literals be mutable would be surprising.
> >>>>>
> >>>>> 3. The real pain is in Maps, not Lists, Sets, or Arrays.
> Library-based
> >>>>> solutions would be mostly acceptable for the latter, but we still
> lack a
> >>>>> reasonable way to describe pair literals, which is what's in the way
> of
> >>>>> a library-based solution for Map (other than a MapBuilder.) Static
> >>>>> methods in interfaces make a library-based solution more practical.
> >>>>> With value types, though, library-based solutions for Map become far
> >>>>> more practical too.
> >>>>>
> >>>>> 4. This feature is less critical that many other features, such as
> >>>>> value types, tuples, or primitive specialization of generics. For
> the
> >>>>> most part, this feature is a nice-to-have, but no one would vote for
> >>>>> delaying value types because of it.
> >>>>>
> >>>>> 5. The underlying mechanics (varargs at the language level; lack of
> >>>>> constant pool support for arrays at the bytecode level; ad-hoc
> support
> >>>>> for caching of (and GC'ing under memory pressure) intermediate
> immutable
> >>>>> results) remain a limiting factor, even with syntactic sugar.
> Working
> >>>>> on these foundational issues might provide more bang-for-buck.
> >>>>>
> >>>>> Of course, in a community of 10M, there will be some range of
> opinion,
> >>>>> but the goal of posting this here was to sanity-check our assumptions
> >>>>> about priorities. Thanks to all who provided feedback!
> >>>>>
> >>>>>
> >>>>
> >>>>
> >>>>
> >>>
> >>
> >
>
>
More information about the lambda-dev
mailing list