-
Notifications
You must be signed in to change notification settings - Fork 205
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Multiple return values #68
Comments
This feature is related to parallel assignments, which has been raised a couple of times before. A problem with over-encouraging the use of multiple returns (with multiple types) is that they're not very self-documenting. In Python, it's easy to creep into patterns like:
This function returns the corners of a rectangle, its color, and its depth buffer value. At this point, it would be better off explicitly stored in an object. Golang enforces some rules on multiple returns to avoid this behavior (among other reasons).
Additionally, Golang doesn't allow you to unpack tuples. This avoids some confusing patterns such as:
which is valid Python. Adopting a stricter system similar to Golang's may be a reasonable tradeoff:
|
It would be great if Dart had builtin support for a typedef LatLng = Tuple(double, double);
class MapService {
Future<LatLng> geoCode(String name) {
// ...
}
}
var mapService = MapService();
// This is valid:
var latng = await mapService.geoCode('Aarhus');
var lat, lng = latlng;
// This is also valid:
var lat, lng = await mapService.geoCode('Aarhus'); This way we could avoid the complexity of multiple returns. The only thing we need now is to enable the syntactic sugar for destructuring tuples. We could also use fixed-length lists instead of tuples here, but I believe tuples provide a better type primitive. |
Another way might be going the Standard ML way and just define product types like this: edit: missing column. |
Yes! Destructuring! I think leaving the typesystem alone for this is fine. Array seems like a good enough default, (if packing results in an array) and recast to a tupleN type on assignment (as mentioned above). I would just like to mention the addition of some nicer destructuring patterns (well better than JavaScript's anyway) and more inline with those found in clojure. (https://gist.github.com/john2x/e1dca953548bfdfb9844) specifically the 'rest' like behavior. |
Kotlin uses the data class (value class) to implement the destructing and multiple return values (https://kotlinlang.org/docs/reference/data-classes.html#data-classes-and-destructuring-declarations). This is not perfect but works in most of the cases. Would be great if dart has built in support for value types. |
See also #207 |
I think we really need multiple return values in Dart. How can we say that Dart is type checked if I'm forced to return List and then like in the stone age, do a list lookup one by one and assign it to proper named variables, casting to the right object including all the danger that comes with list array access and casting.
|
I've never been forced to do this - nothing has prevented me from writing a class that contains correctly typed fields for each value I want to return. |
@natebosch crazy to make a class just to return something |
@Meai1 he probably means a Couple<T, Q> class which can be reused. It's not pretty but it's nothing crazy. |
Yeah, if we're doing types I think @GregorySech pointed out the common solution for the scenario (tupleN generics) but destructuring is more about shapes (but I guess have to be realised as types at some point, explicitly or otherwise) |
@natebosch a class is probably sufficient for a certain percentage of use-cases. The issue is that you would be allocating an extra object on the heap just to return something. I'm hoping that the request here is to return everything via the stack. |
@yjbanov - yup - I think this feature could make things a lot nicer and potentially more efficient, I'd really like to see it happen! The current situation isn't doom and gloom 😄 |
I actually think that it would be very nice to get named return values for this as well, so that we dont have to guess what an int, int might be. This would also help with auto generating in the IDE where you could auto create the lefthand side of a function call with parameters that have the right kind of names already. Suggested syntax:
Slightly related:
|
Destructuring syntax would be a great addition to Dart! Coming to Dart from Kotlin I wish destructuring declarations were available in Dart, they make coding easier. |
Destructuring is more than enough to solve that issue. (take a look on how kotlin do that in conjunction with Data Classes. They are awesome!) |
FWIW, I believe Fuchsia would take advantage of this feature. |
Tell me more about that 👀 |
I tend to view Tupples as stack-allocated static structures. We're soon getting NNBD, which introduces the type modifier '?', such as Point? is a nullable reference to an instance of Point. Tupples would inherit declarations and composability of classes. return Point(0,0) The way we do it now Just an abstract idea, I'm sure we can shoot holes in it :P |
In my view, "Multiple Return Values" are exactly that. |
AFAIK all values are objects in Dart so is memory allocation even involved in this issue at all? As everyone I'd like multiple return values so my code is more coincise and human readable but in the end it's something I can live without. The problem IMO is that the "tuple-like" class work-around will start biting everyone in the ass if over-used by (somewhat successful) third party packages APIs. We will start having X implementations of Tuple<...> coming from external dependencies. So we will start doing adapters but it's more code which is still error-prone both in the making (code generation could help in this) and in the usage (GL HF developers that abuse If the language provides a standard solution to the problem this future's chances of existing are very low + we might get some sweet syntax in the process. ps: sorry for the previous send, it was a mistake. |
|
Not just 2, multiple return values make lot of sense. Once this feature is available, first thing I want to do is to write a simple dart implementation equivalent of https://golang.org/src/errors/ such that I can avoid using exception framework ( try-catch rethrow .. ) unless it is required for a specific purpose. This will be help to write relatively elegant code like :
|
Not specifically, but we understand that records/tuples would address this issue too. Language features don't always map perfectly to user requests. :) |
I believe GingerBill has an interesting take on returning multiple return values using his creation, the Odin language: |
Ada provides in out parameter arguments as a better interface than C pointers. The out parameters can be thought of as additional return values. |
That's OK too. |
I couldn't agree more with gingerBill. |
I have to say I'm inclined to vote against this. How often do I need to return multiple values and I don't have a class handy? The answer is not that often. This week I had one occurrence where it would have been convenient. The down side is that we make the language more complex for something that only gets used occasionally and for which there are perfectly adequate alternatives. |
As a counter: the answer for me is really rather often! Most recently I had a method that had a "success" return value and a couple of "expected" error cases that needed to be returned. The options are:
In C# I found the option to define named inline tuples very convenient. Having a way to extract them at the call site all the better, though I guess that's more of a marginal gain. Being able to say |
@ElteHupkes I'm also concerned that they are going to promote bad practices and fragment the package eco system. Fragmented package eco system. Poor coding practices. Good practice is that a function does one thing and returns one thing. Returning error codes. Error returns were a blight. When exceptions arrived it made a massive difference to code quality. Programmers could no longer ignore failed method calls. Exceptions to errors are what types are to variables. Encouraging the use of error returns is a dis-service to the Dart community. Over a beer one day I will tell you the story of 150 hours spent finding a bug in the asterisk source code caused by a dev failing to check the return on a call to lock(). |
You have a good point but I believe there is room for both. In Ada, exceptions are used for exceptional errors and out parameters akin to multiple return for status messages. |
@kevlar700 There was an pre-internet standard for exchanging invoices etc. Any new feature we add to the language needs to answer two questions: Does this feature make using the language less complex. I think this proposal fails on both. I measure complexity here as the trade off between users having to learn a new language feature vs the reduction in complexity of arriving at a solution. The existing solutions are simply to implement and simple to understand. One of the concerns is that the addition of this feature will encourage users to be 'creative' with the feature in ways that just make code harder to understand. My belief is that the reason Ruby failed was that it was too flexible. It encourage users to essentially write DSLs so you had to learn the DSL of every ruby program. I've had first hand experience of the type of nonsense that Ruby encourages and its fun to implement, impossible to maintain. Building a language that is somewhat dogmatic in style actually makes for a better package eco system. The short story is don't introduce features:
|
Hello and thank you for Dart! I just wanted to chime in out of nowhere and add my comments here: If you look at some of the top programming languages used these days for both front and backend development, you'll see that they support returning tuples:
JavaScript surprisingly apparently doesn't allow tuples 🤔 or am I mistaken? So just to wrap it up, I think it's a matter of time before people expect tuple support from Dart given how popular it has become now! |
I think you should know the Records proposal. Some examples: (int, String, bool) foo() => (1, 'yes', true);
(int, String, bool) triple = foo();
var namedRecord = (1, 2, a: 3, b: 4);
var a = (1, 2);
var b = (1, 2);
print(a == b); // true.
(num, Object) pair = (1, 2.3);
print(pair is (int, double)); // "true".
|
@jodinathan, when will records be available? |
When they're ready! Records (and pattern matching) are being actively worked on by the Dart language team, and are getting close to where the designs are ready for implementation. They'll then be implemented, and released when ready. Basically, as soon as possible, but no sooner. Unless something happens, and we decide to postpone or drop the feature. We probably won't, but there are no promises until the feature has actually launched. But they are next on our list of non-trivial features, so as to whether records will be ready before "any other feature", the answer is "likely yes". |
For those that want to experiment with unreleased experimental features, note that you can enable the records feature on the master branch of flutter with: void main(List<String> arguments) async {
print(returnValue(1).$0);
final newValue = (1, 2, a: 1);
print(newValue);
print(newValue.a);
print(swap((1, 2)));
}
(int, int) returnValue(int value){
print('hi');
print((a: 1, b: 2));
return (value, value);
}
(int, int) swap((int, int) value){
return (value.$1, value.$0);
} =>
Note that I do not think AOT support or JS support is working yet. Just realize that the feature could be canceled or changed at any time while in the experimental phase. Thank you dart team for making this happen!! You all are the best! |
@TimWhiting I was wondering if we could access the values of the record in a array way. Like, instead of |
@shinayser No. This was discussed on the language repo, and you can see the reasoning process here: #2388 |
Any chance we could have Python-like syntax for creating local variables from record elements? Example:
I can mostly achieve what records seem to be aiming at using the tuple package, but it doesn't provide that concise syntax Python has for unpacking multiple elements in a single line. Example using tuple package, which requires three lines:
From what I have read, records would be similar:
If records could also support Python-like unpacking as a language feature as in the first example, that would be a real value add over the tuple package. |
Yes. The currently proposed syntax is |
For more details, see the draft spec: |
Is |
@munificent @mit-mit - should we tag this with the records label: https://github.com/dart-lang/language/issues?q=is%3Aopen+is%3Aissue+label%3Arecords |
Are records going to be more performant than custom classes with final fields (and == and hashCode)?
Is this optimization planned for Dart 3 or just left open as a possible optimization later? Also if one were to write a package like vector_math only using records for the vectors and matrices where the operations are extensions for example on |
In general, the language doesn't specify which optimizations are done. We just try to design features such that the semantics don't prevent optimizations. For Dart, we have two compilers to JavaScript, a JIT that targets several CPU architectures, as well as an ahead-of-time compiler that also supports several CPU architectures. So the answer "is this optimization" done can be complex. It may be optimized on some targets under some contexts but not others. In general, the most accurate way to reason about performance of your program is to profile it. |
Closing this because records and patterns are now enabled by default on the main branch of the Dart SDK! 🎉 |
A partner team has requested support for multiple return values for functions, for example:
The text was updated successfully, but these errors were encountered: