Multiple Return Values for Fuctions

1,656 views
Skip to first unread message

Pete

unread,
Aug 2, 2012, 1:28:38 AM8/2/12
to mi...@dartlang.org
I took the tour of Go today and one of the few things that I liked was Go's support for multiple return values for functions.


Any plans to include this in Dart?

Seth Ladd

unread,
Aug 2, 2012, 12:13:38 PM8/2/12
to Pete, mi...@dartlang.org
Hi Pete,

Thanks for the question. I'm not sure if they've come up in the distant past, but I haven't heard about them recently and I don't think they are on the radar. Of course, one workaround is to return a list (aka array) (which isn't too bad thanks to Dart's literal list syntax)

You're always free to suggest bugs and features at http://dartbug.com/new but all language suggestions are weighed heavily against familiarity and implementation concerns.

Curious, are you using Dart for anything in particular?

Thanks,
Seth


--
Consider asking HOWTO questions at Stack Overflow: http://stackoverflow.com/tags/dart
 
 

Bob Nystrom

unread,
Aug 2, 2012, 12:47:14 PM8/2/12
to Seth Ladd, Pete, mi...@dartlang.org
On Thu, Aug 2, 2012 at 9:13 AM, Seth Ladd <seth...@google.com> wrote:
Hi Pete,

Thanks for the question. I'm not sure if they've come up in the distant past, but I haven't heard about them recently and I don't think they are on the radar.

Explicit multiple returns haven't come up much. However, tuples, the more general-purpose feature that covers that and lots of other cases is a perennial topic of discussion. It's frequently ranked high on internal lists of nice-to-haves, but hasn't been a critical priority. We're always trying to balance features with simplicity, and also trying to push off anything to later versions that we can.

Tuples haven't made the cut. I wouldn't be surprised if they appeared in a later version, but I also wouldn't be surprised if they didn't.

Of course, one workaround is to return a list (aka array) (which isn't too bad thanks to Dart's literal list syntax)

Exactly right. Unfortunately, you can't heterogeneously type a list, and it begs for nice destructuring support, but that's a whole other discussion... :)

- bob

Seth Ladd

unread,
Aug 2, 2012, 12:55:59 PM8/2/12
to Bob Nystrom, Pete, mi...@dartlang.org
I couldn't find a bug for tuples, but for the record here's a historical bug: http://code.google.com/p/dart/issues/detail?id=421

Nelson Silva

unread,
Aug 2, 2012, 9:19:53 PM8/2/12
to mi...@dartlang.org
+1 for destructuring support! Really miss it from coffeescript.

Pete

unread,
Aug 3, 2012, 2:57:41 AM8/3/12
to mi...@dartlang.org
Seth, I am not using Dart yet, but I am keeping a close watch on the project.  I am currently developing with GWT/Java.

Bob, "heterogeneously typing a list" sounds awesome!  If you have an issue for it I'll double-star it!  I have already starred 421 ;)

Thanks for your responses!

Nelson Silva

unread,
Aug 3, 2012, 10:35:39 AM8/3/12
to mi...@dartlang.org
Scala has tuples with up to 22 elements if i'm not mistaken so you can have heterogenous typed "lists" with up to 22 elements.
Regarding destructuring is it possible to override operator = to get that sort of behaviour?

num n; string s; [n, s] = [42, "the meaning of life"];

In this case since n and s would be passed by value to the array constructor there would be no way to set them to the proper values right ?

Bob Nystrom

unread,
Aug 6, 2012, 1:34:46 PM8/6/12
to Nelson Silva, mi...@dartlang.org
On Fri, Aug 3, 2012 at 7:35 AM, Nelson Silva <nelson...@gmail.com> wrote:
Scala has tuples with up to 22 elements if i'm not mistaken so you can  have heterogenous typed "lists" with up to 22 elements.
Regarding destructuring is it possible to override operator = to get that sort of behaviour?

No, Dart doesn't allow overriding the assignment operator. Most languages, even ones that support operator overloading/overriding like C#, Smalltalk, etc. still don't allow overloading assignment. C++ is kind of the odd one out here.
 

num n; string s; [n, s] = [42, "the meaning of life"];

In this case since n and s would be passed by value to the array constructor there would be no way to set them to the proper values right ?

Correct. Even if you could overload "=" in Dart, you couldn't make that work because Dart would still need built-in support for having lists like [n, s] be l-values.

- bob

William Hesse

unread,
Aug 15, 2012, 6:43:57 AM8/15/12
to mi...@dartlang.org, Nelson Silva
If your function is asynchronous, or might be asynchronous in the
future, you get multiple return values for free:
myAsynchronousFunc(arg1, arg2, (T1 return1, T2 return2) {
Code to process return values ....
});

Unfortunately, Future<T> only passes a single value T, so this must be
done with a callback, not a Future.

A non-asynchronous workaround with a callback can also be used:
myMultireturnFunctions(arg1, arg2, (x,y){ local1 = x; local2 = y; });
or
myOutVariableFunction(arg1, arg2, (x) { outVar1 = x;}, (x) { outVar2 = x;});

I know these are probably worse than the alternatives, except in the
case where the function already takes a callback.
> --
> Consider asking HOWTO questions at Stack Overflow:
> http://stackoverflow.com/tags/dart
>
>



--
William Hesse

Christopher Wright

unread,
Aug 15, 2012, 3:28:56 PM8/15/12
to mi...@dartlang.org, Nelson Silva
In C#, there is a series of classes named Tuple<T>, Tuple<T1, T2>, all the way up to Tuple<T1, ... T7>. I believe Dart would be sad if you did the same, but you could use Tuple1, Tuple2, and so forth instead.

It's ugly compared to language-supported tuples, but it works.

Rasmus Schultz

unread,
Mar 13, 2014, 6:05:14 PM3/13/14
to mi...@dartlang.org
Seth,

I have been using Go and I am currently using Dart full time - I find this feature is badly missing.

> Of course, one workaround is to return a list (aka array) (which isn't too bad thanks to Dart's literal list syntax)

Sadly, this is just as poor a solution in Dart as in any other language - OK if both values are of the same type, but if not, it's goodbye to type safety. If they are the same type, it's goodbye to semantics, and it's up to the caller to figure out which values are X/Y or R/G/B/A etc... coords[0] and coords[1] is not very legible compared to e.g. "left" and "top".

It's also unnecessary overhead - constructing and populating a list can be too expensive if what you're returning are e.g. X/Y or X/Y/Z coordinates for something that is supposed to be a fast, lightweight computation.

It's one of those features, I think, once you've had it, you will miss it forever - it makes too much sense, and I'm actually surprised it's not in every major language by now... :-)

Alex Tatumizer

unread,
Mar 13, 2014, 7:00:58 PM3/13/14
to mi...@dartlang.org
There's an obvious solution: dart has very lightweight classes.
class PairOfShoes {
  int leftShoe;
  String rightShoe;
  PairOfShoes(this.leftShoe, this.rightShoe);
}
you can return new PairOfShoes(42,"foo") - and it's as typesafe as it gets.
Do you have issues with this pattern? Or what?


Rasmus Schultz

unread,
Mar 13, 2014, 8:35:14 PM3/13/14
to mi...@dartlang.org
For lack of better, that's one solution.

Another solution (unfortunately the one you'll see people resorting to more often) is to accumulate and/or mutate state internally in a class because writing up an class just for the return-value of a function is overkill, when a function really conceptually is a computation of two values.

Sometimes two or more results cannot be (efficiently) computed separately, yet are not conceptually a "pair" - in those cases it's hard to come up with a meaningful name for an arbitrary return type, and implementing an arbitrary return type with no real semantic purpose feels wrong. In those cases, the concrete return type becomes an implementation artifact, or a mere work-around.

We run into those cases in every language, and we come up with work-arounds, whatever is most efficient in that language - in Go I never have to make that trade-off.

It's a "nice to have" feature, of course, not a "must have" - now that I've had it, it just sucks not to have it, that's all :-)



--
For other discussions, see https://groups.google.com/a/dartlang.org/
 
For HOWTO questions, visit http://stackoverflow.com/tags/dart
 
To file a bug report or feature request, go to http://www.dartbug.com/new

Alex Tatumizer

unread,
Mar 13, 2014, 8:48:15 PM3/13/14
to mi...@dartlang.org
 > in those cases it's hard to come up with a meaningful name 
No need to have meaningful name. Most likely, it should be a private class for a library that returns it. Caller looks like:
var pair=getMeAPairOfShoes(); // no type specified

this is not worse than Tuples. 
Supporting multiple return values would cause multiple problems - in go, it affected overall syntax. How multiple values could be expressed in dart, even in principle? Any syntax I can think of is ugly.

 


Rasmus Schultz

unread,
Mar 13, 2014, 9:59:22 PM3/13/14
to mi...@dartlang.org
> Any syntax I can think of is ugly.

Having a whole class declaration is ugly ;-)

The declaration doesn't have to mess with current return semantics - it could look like this:

void nextPosition(int x, int y) {
    return { int nx = x + 1; int ny = y + 1 }
}

At the call site, for clarity, you could be required to name the return values, along the lines of passing named parameters:

int a;
int b;
nextPosition(x, y, a = nx, b = ny);

Since you can't pass by reference in Dart, that also pretty much takes care of that, in a sense.



Alex Tatumizer

unread,
Mar 13, 2014, 10:20:53 PM3/13/14
to mi...@dartlang.org
function says it returns void. How can it return something if it says it returns nothing?.

Rasmus Schultz

unread,
Mar 13, 2014, 11:30:20 PM3/13/14
to mi...@dartlang.org
Yeah, good point, maybe it's better to think of this is "out params" like in some other languages, rather than multiple return values - it's effectively a means to the same end, so:

void nextPosition(int x, int y, out int nx, out int ny) {
    nx = x + 1;
    ny = y + 1;
}

How it looks is probably less important than how it works, but maybe, if we think of them more as "return values" than "out params", we could actually recycle the return keyword:

void nextPosition(int x, int y; return int nx, int ny) {
    nx = x + 1;
    ny = y + 1;
}

Now of course there's nothing enforcing you actually set the return values, so there should probably be a warning if you exit from a function without setting all of the return values...

Note that this doesn't overlap with return semantics still - you could use both if you wanted:

bool nextPosition(int x, int y, return int nx, return int ny) {
    nx = x + 1;
    ny = y + 1;
    return nx != x || ny != y; // return type indicates whether the object moved
}




On Thu, Mar 13, 2014 at 9:20 PM, Alex Tatumizer <tatu...@gmail.com> wrote:
function says it returns void. How can it return something if it says it returns nothing?.

--

Alex Tatumizer

unread,
Mar 13, 2014, 11:36:20 PM3/13/14
to mi...@dartlang.org
bool nextPosition(int x, int y, return int nx, return int ny) {
    nx = x + 1;
    ny = y + 1;
    return nx != x || ny != y; // return type indicates whether the object moved
}

And how caller would look?

Scott M

unread,
Mar 13, 2014, 11:37:52 PM3/13/14
to mi...@dartlang.org
I certainly hope so. Multiple return values can be fast and clear and type safe and semantically significant (compared to cludgy work arounds).

The syntax doesn't matter, so it's easy to add :)

int a, String b = do_something_for_me(fu,bar); 

or heck

&{int a, String b} = do_something_for_me(fu,bar); 

It doesn't matter. 

Of course, this is another case where the compiler can do relatively simple type inference.

&{a, b} = do_something_for_me(fu,bar).

Keep the feature simple. Don't allow reversion to single return value collection. It's easy to allow elisions to tell the compiler (or reader) that we're not interested in some return value.

&{_, b} = do_something_for_me(fu,bar).

Or whatever. Sorry for the goofy syntax. My point is that some goofy syntax is just fine - we only use the feature when the semantics warrants it. I think it would be a dandy addition to the language (most languages, for that matter!). 

- Scott

cc "maco" young

unread,
Mar 13, 2014, 11:50:33 PM3/13/14
to mi...@dartlang.org
even something as PHP's braindead list() would be helpful:

int a, b;
Array fn() => [ 1, 2 ];
list(a,b) = fn()
print( "$a, $b" ); // prints "1, 2"

a preferred syntax would be something like 

&[a,b] = fn()

even better

&[int a, int b] = fn();

while not as syntactically clean as returning multiple values, it would still make for far cleaner code than packing and unpacking objects to get at multiple values



--
For other discussions, see https://groups.google.com/a/dartlang.org/
 
For HOWTO questions, visit http://stackoverflow.com/tags/dart
 
To file a bug report or feature request, go to http://www.dartbug.com/new

To unsubscribe from this group and stop receiving emails from it, send an email to misc+uns...@dartlang.org.

Greg Lowe

unread,
Mar 14, 2014, 12:13:02 AM3/14/14
to mi...@dartlang.org
Having a whole class declaration is ugly ;-)

You can create a tuple library, rather than creating  specific class each time.

Destructuring remains a bit ugly without language syntax. But maybe there's a workaround that's not too ugly?

class Tup2<A,B> { 
  Tup2(this.a, this.b);
  final A a;
  final B b;
  destructure(f) => f(a, b);
}

// And there would be a Tup3, Tup4 and so forth in the library too.

Example of using this:

Tup2<int, num> foo() => new Tup2(42, 'boom!');

main() {
  int number;
  String blah;
  foo().destructure((a, b) => number = a, blah = b); 
  print('$number, $blah');

Joao Pedrosa

unread,
Mar 14, 2014, 12:24:46 AM3/14/14
to mi...@dartlang.org
Hi,

In Go I think this feature got added simply because they needed it for error handling. In Go this feature should be idiomatic.

In Dart we can come up with naming conventions for single purpose classes that address this. I have some classes that I have prefixed with "Atomic" simply to help to distinguish them from more general purpose classes. Also in Dart folks seem to like their final constants and this problem gives those users some short little classes to play with. In Dart having these classes would be idiomatic.

You could instead of a prefix, add a suffix to your classes for these purposes, like PointTuple or some such.

So long as you don't think about major reuse for those classes, you should be fine. And let's be honest here, people using a lot of finals on their classes tend to think very little about further reuse for those. They are what they are and that's it. Thankfully by prefixing library imports we don't even need to care so much about naming conflicts.

So let's create our Buttons, Windows, Points, Elements and be happy. :-)

Cheers,
Joao





To unsubscribe from this group and stop receiving emails from it, send an email to misc+uns...@dartlang.org.

Scott M

unread,
Mar 14, 2014, 12:40:13 AM3/14/14
to mi...@dartlang.org
I completely separate the notions of tuples (and general destructuring in languages with pattern matching like ML) or "custom classes for return values" and the explicit feature of multiple return values (MRV). 

The point of MRV is efficiency, not just "convenience." 

Plain old MRV allows a function to return multiple values without allocating an object on the heap that needs to be collected. This is important because in MRV, a returned object has no value - it is only its fields that are important. In a tight loop (you know, real computer programs), this matters. 

A compiler can see a function call with MRV and can stack allocate the "slots" to receive the returned values. This is a big efficiency win compared to needlessly returning heap allocated custom classes or even tuples that are simply destructured and then just thrown away. 

Additionally, stack access is often faster than access to other objects on the heap - another potential performance win. IIRC, some whole program compilers for functional languages looked for "useless tuples and structures" (both returned and passed as arguments or for pattern matching expressions, etc.) and substituted stack allocation (again, for whatever case) to increase performance. 

If we're going to use Dart to write big, sometimes compute intensive programs like spread sheets or machine learning systems or lord knows what, it would be nice to look to performance oriented language features and semantics, and not just think of "source mangling and compression" as meaningful "program optimization" - LOL!!!! 

Just my 2 cents.

Joao Pedrosa

unread,
Mar 14, 2014, 1:30:23 AM3/14/14
to mi...@dartlang.org
Hi,

Nice ideals. :-)

I just recalled a technique I used for retrieving multiple values from a function call. We can pass in a function as a latter function argument. Like this:

greet(record, fn(name, location))  {
  var userName = "Mr. ${record.name}", location = "${record.city},  ${record.state}";
  fn(userName, location);
}

Then call it:

main() {
  var record = DB.connect("etc").getRecord(123);
  greet(record, (name, location) {
    print("Hello ${name} in ${location}");
    });
}

I use this in some places. So between the single purpose classes and using closures to get data out of a function we have a lot of power on our hands already.

In Ruby we get multiple function returns and closures and a specialization of it called blocks. Blocks were apparently an original idea from Smalltalk.

I think Dart is safe regarding application for huge engineering tasks. Its static features give Dart programs an easy packaging for easy deployment which is often the biggest burden found in "dynamic" systems. And even then, some folks overcome those inefficiencies from time to time.

Both Dart and Go have an easy packaging system not always found elsewhere. Performance can be improved over time. If all else fails, demand types be declared everywhere and get us that 5x better performance at some point. :-)

Although I think most users would prefer their "var" thank you very much. :-)

Cheers,
Joao




Rasmus Schultz

unread,
Mar 14, 2014, 1:36:51 AM3/14/14
to mi...@dartlang.org
With a typedef, this approach could be type-safe too - though, again, you're adding a semantically insignificant types as a work-around.

Tuples are no better, in my opinion - as discussed, more objects with no semantic meaning beyond "a set of values", which sounds more like a programming language construct than anything that belongs in a domain.

These are all well-known work-arounds from other languages, but they are just work-arounds.

Gen

unread,
Mar 14, 2014, 3:11:12 AM3/14/14
to
I think the main reason against MRV is dynamic typing.
How to make MRV work (efficiently) at any call site ?

WRT efficiency, the difference is probably how much is allocated and not so much the speed of the allocation itself.
Instead of N objects with MRV there is allocated 1 container object with N members.
Depending on the garbage collector, heap allocation can be very cheap.
Depending on the case, objects can be allocated on the stack; as you have written yourself.
Depending on the case, JIT inlining can happen.

Pablo Guerrero

unread,
Mar 14, 2014, 3:24:03 AM3/14/14
to misc
Hi,

Why not a tuple approach, together with some syntax sugar using
transformers? Given that it's not likely that Dart will have real
support,
I think that's the closest we can get.

You can imagine something like

multifunction() => multi(1,2,3);

@Multi() var a, b, c = multifunction();

that would need to have defined on the library

multi([a, b, c, d, e ...]) { ... }

class Multi {
const Multi([Type t1, Type t2, Type t3, ...]);
}

class Tuple { ... }

Even more, you could provide a single or multiple types to Multi:
@Multi(int) or @Multi(int, String, Foo) ...

The return I think it can be done without transformers, but the
destructure would need to be transformed into something like this:

Tuple t = multifunction(); var a = t._1; var b = t._2; var c = t._3;

or if types are provided

Tuple t = multifunction(); int a = t._1; String b = t._2; Foo c = t._3;

The idea is that the editor wont complain (as is valid Dart syntax),
and everything will still work, but we have static checks on the
compiler if we provide types.

I'm using a similar approach for a different thing and it works perfectly.

Of course, the names and final syntax could be a different one.

I've been thinking a lot about this (for my other problem) and I think
this is as far as we can get without changing the language itself.
For me the syntax is good enough, and being able to implement it as a
library gives you more freedom.

What do you think?
Pablo

Pablo Guerrero

unread,
Mar 14, 2014, 3:31:50 AM3/14/14
to misc
By the way, one problem of my approach is that right now you cannot
debug this on the Editor because you need to run it using pub serve to
have the transformers applied. But I think this will be solved at some
point, or even there is a way of doing it right now that I don't know.

As an aside, transformers are a really nice feature of Dart that
allows to write better libraries in many cases. People should be using
them more !

Cheers,
Pablo

William Hesse

unread,
Mar 14, 2014, 4:28:26 AM3/14/14
to General Dart Discussion
If there was a simple way to declare the setter for a local variable 'a', then it would be easy to give out parameters.  The only problem with

foo(x, y, a_out) => a_out(x + y);

int a;
var a_out = (x) => a = x;
foo( x, y, a_out);
print(a);


is the verbosity of the line defining a_out.  If there was a way to make the definition of a_out look like a function call or constructor, new Out(<a>), then we could just write

foo(x, y, new Out(a))

instead of defining a_out, but we can't.  So the shortest alternative is to create boxed values:

class Out<T> {
  T out;
}

foo(x, y, Out<int> a) => a.out = x + y;

Out a = new Out<int>();
foo(x, y, a);
print(a.out);

This isn't too bad, especially if the types are left out.

William Hesse

Lasse R.H. Nielsen

unread,
Mar 14, 2014, 4:31:51 AM3/14/14
to mi...@dartlang.org
On Thu, Mar 13, 2014 at 11:05 PM, Rasmus Schultz <ras...@mindplay.dk> wrote:
Seth,

I have been using Go and I am currently using Dart full time - I find this feature is badly missing.

I really, really agree. And I haven't even been using Go.

Dart is doubly cursed by having none of:
- multiple return values
- reference parameters
- value types/stack structs.
That means that the only way to return more than one value from a function call, is to return a heap allocated object containing the values.

I know bump allocation of short-lived objects is cheap (but it does cause more frequent GCs), and if the function is inlined, the allocation can go away entirely. It's still hard to make any performance guarantees.

Of the three features above, proper multiple return values is by far the prettiest and simplest to use. I think Go got it right on that.

It does cause some troubles in a dynamic language like Dart, where you don't know the return type of the function you call until you call it. If it turns out it returns three arguments, and you only expect one, should it throw (just as if you passed three arguments to a function expecting one)? [1]

...
 
It's one of those features, I think, once you've had it, you will miss it forever - it makes too much sense, and I'm actually surprised it's not in every major language by now... :-)

Hear, hear.
 
I think the closes feature request we have is: http://dartbug.com/10310

/L
[1] As someone who knows CPS, the answer is obviously: What's the difference between calling and returning? :)
--
Lasse R.H. Nielsen - l...@google.com  
'Faith without judgement merely degrades the spirit divine'
Google Denmark ApS - Frederiksborggade 20B, 1 sal - 1360 København K - Denmark - CVR nr. 28 86 69 84

Alex Tatumizer

unread,
Mar 14, 2014, 8:35:03 AM3/14/14
to mi...@dartlang.org
Dart is doubly cursed by having none of:
- multiple return values
- reference parameters
- value types/stack structs.

Aha! Finally, I found my spiritual brother!
I have to confess: I'm secretly working on a library where you get 2 of the 3, plus long returns and a bunch of other exciting blows and whistles.

Circuit! Brand-new incarnation. Old one is dead. Long live Circuit!

Circuit is going to be unleashed in the near future (some polishing is afoot), but meanwhile, would you care to read and comment on NEW writeup available (for free!) at  

Please note that Circuit especially likes negative comments - he already tucked up his sleeves to beat all potential opponents hands down (I mean, figuratively, in a civilized discussion).
Will you take a challenge?

Rasmus Schultz

unread,
Mar 14, 2014, 10:03:11 AM3/14/14
to mi...@dartlang.org
> - multiple return values
> - reference parameters
> - value types/stack structs.

Multiple return values and reference params effectively accomplish the same thing, so we wouldn't need both, would we?



--

Alex Tatumizer

unread,
Mar 14, 2014, 11:28:01 AM3/14/14
to mi...@dartlang.org
@Lasse: another thing you may like: there's really no difference between call and return in Circuit. Because neither exists.
Everybody is sitting on event bus and listening to events. When they have something new to report, they send it to the bus.

Guys, if someone wants to comment, but don't want to do it publicly, please send email to Circuit to my address (tatumizer).


Bob Nystrom

unread,
Mar 14, 2014, 12:46:05 PM3/14/14
to General Dart Discussion
What this thread really lacks is concrete real-world examples. In the absence of those, there's no way to tell which solution is the best fit.

For example, Go uses multiple return values because it avoids exceptions. Dart has exceptions, so we don't need multiple return values for that use case. (And I'll note that using multiple return values (a product type) to return one of two possible types (a sum type) and then hoping and praying the caller knows to ignore the other zero-valued result is insane.)

In almost all cases where I've wanted to return multiple values in Dart, that aggregate has had a meaningful identity in the context of my application. Creating a tiny class for that (and Dart has more concise than most other languages) invariably makes my code easier to read. I usually end up using that little class in other places over time.

Still, there are a few cases I've run into where I want to return two things that don't have any identity as a unit. In those cases, I just define a Pair class and use that. For example:

/// Returns a [EventSink] that pipes all data to [consumer] and a [Future] that
/// will succeed when [EventSink] is closed or fail with any errors that occur
/// while writing.
Pair<EventSink, Future> consumerToSink(StreamConsumer consumer) {
  var controller = new StreamController(sync: true);
  var done = controller.stream.pipe(consumer);
  return new Pair<EventSink, Future>(controller.sink, done);
}

This is a bit verbose, and the lack of destructuring is a pain. What is important, though, is that it composes.

If I have a method like:

void doSomethingWithPair(Pair<EventSink, Future> pair) { ... }

I can call it like:

doSomethingWithPair(consumerToSink(consumer));

Because pair's are objects, this works naturally. Multiple return values, since they are a syntactic feature don't do that.

This places me in the tuple camp, and I would prefer tuples over multiple returns or out params in Dart. However, I'm not convinced that tuples interact well with the rest of the language. They play fine in ML because ML is fully statically-typed and all multiple-parameter function calls are tupled.

That isn't the case in Dart. That means you run into a bunch of really nasty corner cases where it isn't clear to a user when a tuple will be destructured and when it won't. Also, as an API designer, I don't like how tuples confine you to positional fields. That makes API evolution very painful.

I think that Dart is a bit hamstrung in this use case, but I'm not sure there is a solution that doesn't add more problems than it solves.

Cheers,

- bob



--
For other discussions, see https://groups.google.com/a/dartlang.org/
 
For HOWTO questions, visit http://stackoverflow.com/tags/dart
 
To file a bug report or feature request, go to http://www.dartbug.com/new

To unsubscribe from this group and stop receiving emails from it, send an email to misc+uns...@dartlang.org.

Alex Tatumizer

unread,
Mar 14, 2014, 12:57:45 PM3/14/14
to mi...@dartlang.org
I'm aware of just a single example where 2 values SHOULD be returned, and cannot be returned through object or any other device for performance reasons. It's divRem.
Smart compiler can optimize var a=x~/42, r=x%42 so that only one division actually gets executed. But dart doesn't do it. Neither java.

Scott M

unread,
Mar 14, 2014, 1:28:34 PM3/14/14
to mi...@dartlang.org
Yup. The way I think of it in language design is that any program with "return a value" semantics can be translated into a program with continuation passing style semantics. If a language supports multiple arity functions, like Dart does, the requirement that continuations take just one argument is just plain arbitrary. 

Some fine languages like Pascal used "var" arguments (reference arguments) and C permitted pointer arguments to retrieve multiple return values. The obvious cludginess of these designs is that these mechanisms allowed for these "return" arguments to have meaningful function *input* meaning - yuck. (Sorry for any syntax errors, this should be close.)

// C: x is an input and output argument. r is an "output" only argument.
void div2rem (int *x, int* r) { *r = *x | 1;  *x = *x << 1; } // Blech!!!

// Pascal: x is an input and output argument. r is an "output" only argument.
procedure div2rem (var x: int; var r: int);
begin
  r = x | 1; x = x << 1
end

Sanity would separate meaningful multiple input arguments from meaningful output/return results.
 
values(int x, int r) div2rem (int a) { r = a | 1; x = a << 1; }  // maybe like this?
values(int x, int r) div2rem (int a) { return values(a | 1, a << 1); } // or like this?

Again, syntax doesn't really matter, and the "problem" with reference arguments as "multiple return values" is a language semantics problem, not a syntax problem. And coming back to semantics, MRV is useful and a natural outgrowth of permitting arity > 1 continuation functions in a CPS rewritten program in a language that *already* supports arity > 1 functions.

Just my 2 cents. - Scott



On Fri, Mar 14, 2014 at 12:57 PM, Alex Tatumizer <tatu...@gmail.com> wrote:
I'm aware of just a single example where 2 values SHOULD be returned, and cannot be returned through object or any other device for performance reasons. It's divRem.
Smart compiler can optimize var a=x~/42, r=x%42 so that only one division actually gets executed. But dart doesn't do it. Neither java.

Scott M

unread,
Mar 14, 2014, 1:47:02 PM3/14/14
to mi...@dartlang.org
FWIW, there are a ton of functions where MRV make good sense.

Think of a simple list partitioning function.

typedef bool Pred(<T>);

// Partitions a list into two lists based on predicate function
values (List<T> lt, List<T> lf) partition (List<T> il, Pred<T> ) {
  // blah blah - only one pass over the list
  // ....
}

Or this could return counts or sums or averages of <T>'s discriminating on a predicate and so forth and so on. You all get the idea :)

- Scott

Alex Tatumizer

unread,
Mar 14, 2014, 2:54:14 PM3/14/14
to mi...@dartlang.org
Partition into 2 parts naturally generalizes to partition into N parts. Clearly, return value should be a list of lists, otherwise definition won't scale.
Same is true for other trivial examples - they are all functions of N, and so Lists are the answer.
Do you have non-generalizable examples?

Gen

unread,
Mar 14, 2014, 3:32:54 PM3/14/14
to
I think Dart should have tuples where a tuple is an array (or list) with a minimal interface for maximum performance.
A tuple is useful for arguments and return values.
 
Exceptions are no replacement at all because of different use, syntax and implementation.
Error handling is just a possible use for tuples.

Such tuples solve a problem that can not be solved otherwise; either in syntax or in semantics and implementation.

And I fail to see the syntax problems.
I propose:
{A, B, C} ; //As literal. Or some other form that works in Dart.
tuple[0]; //To retrieve the first element.
var A, B, C = tuple; //To assign the first 3 elements in A, B and C respectively. If the tuple is smaller then assign null respectively instead of exception. This could even work for any list.

PS
I guess, tuples should be passed as shallow copy to enable optimizations.

Alex Tatumizer

unread,
Mar 14, 2014, 5:14:13 PM3/14/14
to mi...@dartlang.org
Then {42} would mean one thing, and {42;} - another.
Designing syntax is difficult


Sean Eagan

unread,
Mar 14, 2014, 5:25:24 PM3/14/14
to General Dart Discussion
On Fri, Mar 14, 2014 at 2:16 PM, Gen <gp78...@gmail.com> wrote:
I think Dart should have tuples where a tuple is an array (or list) with a minimal interface for maximum performance.
 
I just sent a pull request to quiver which I think matches that request pretty well:


It basically models tuples as Iterables with typed fields for each item reusing the existing Iterable interface where possible e.g. `first`, `second`, ..., `last`

Would love any feedback.

Cheers,
Sean Eagan



Am Freitag, 14. März 2014 17:46:05 UTC+1 schrieb Bob Nystrom:

Scott M

unread,
Mar 14, 2014, 5:52:34 PM3/14/14
to mi...@dartlang.org
Tuples are not like lists or sets or queues at all. Tuples are the fundamental product type - like a struct or a (kinda like a) class. In theory, all you really need is Pair<A,B>, because you can always nest Pairs to add elements  Pair<String,Pair<int, bool>>. 

Check this out. It's basic, old school CompSci :)


In practice most languages support n-tuples. Each tuple arity is a discrete type. They are not like arrays or list of "variable length". So we might have some convenient syntax to create n-tuples like @(42, "Bar") or @("Fu", 42, 68.9, false). Behind the scenes, these are not "tuple-n" types, but instantiations of the generic types Tuple2<A,B> and Tuple4<A,B,C,D>.  There may even be a "convenience syntax" for writing tuple types, such as @(int, String) instead of Tuple2<int, String>. That's great (and common place). 

This is why the "array index" syntax for accessing tuple elements is a very bad idea - it leads to the misunderstanding that a tuple is something like a list or array - they are not. They are simple stripped down "structs" (field names are a convenience feature - a good one - in structs). You can't "iterate" through fields of a structure or "record".

Syntax in the general case doesn't matter, but tuples and destructuring and pattern matching all go together in many language design like rum and coke :)  You won't generally see too much "field access" of a tuple in SML or languages like that. 

But if you really need to access field-n of a tuple, some syntax like this works better and reinforces the basic semantics of the tuple type.

foo = @(42, "Bar", "Bif");
foo._1
  >>> 42
foo._2
  >>> 42

These are not "indexing" references. They are field references. 

Hope that helps. 

- Scott

Scott M

unread,
Mar 14, 2014, 5:55:49 PM3/14/14
to mi...@dartlang.org
Sorry, my mistake. That pseudo-code should read.

foo = @(42, "Bar", "Bif");
foo._1
  >>> 42
foo._2
  >>> "Bar"

Sorry, my mistake. - Scott

Alex Tatumizer

unread,
Mar 14, 2014, 6:00:59 PM3/14/14
to mi...@dartlang.org
In what respect foo = @(42, "Bar", "Bif") is better than foo=[42, "Bar", "Bif"]?
And foo._1 is better than foo[1] ?

Scott M

unread,
Mar 14, 2014, 6:37:44 PM3/14/14
to mi...@dartlang.org

If I understand your question, the answer is manifest typing and type safety. A product type is a "type" :)

On Mar 14, 2014 6:01 PM, "Alex Tatumizer" <tatu...@gmail.com> wrote:
In what respect foo = @(42, "Bar", "Bif") is better than foo=[42, "Bar", "Bif"]?
And foo._1 is better than foo[1] ?

--

Gen

unread,
Mar 14, 2014, 7:48:32 PM3/14/14
to mi...@dartlang.org
Maybe:
(A, B, C)
[A; B; C]
A, B, C
 
I do not know what works best in Dart.

Gen

unread,
Mar 14, 2014, 7:55:36 PM3/14/14
to mi...@dartlang.org
Dart is special in that it should be usable:
- with runtime type info
- without static typing
- without computer science background
 
And Gilad wants to avoid exceptions.
My idea of a tuple is about maximum comfort and performance.
Your product type and nesting example seems to require a large implementation overhead in a dynamic OO language.

Alex Tatumizer

unread,
Mar 14, 2014, 8:09:10 PM3/14/14
to mi...@dartlang.org
> If I understand your question, the answer is manifest typing and type safety. A product type is a "type" :)
But what is the type of it then?
E.g.
print( @(42, "Bar", "Bif").runtimeType);

What will it print?  

Scott M

unread,
Mar 14, 2014, 8:11:48 PM3/14/14
to mi...@dartlang.org
Tuples, whether or not a potentially useful feature to ad to Dars, are certainly not\ "out of Dart style". 

We already have typed variables, simple local type inference, classes with typed field members. And typed lists too with generics and on and on and on.

Now, Dart also permits latent typing - via the "dynamic" type. It's not all that "special."

So, just by way of concrete pseudo-code example:  a 3-tuple can easily have a dynamic type: 

  Tuple3<int,dynamic,String> is just another type instantiation of Tuple3<A,B,C>.  It's not complicated.

Or again, use some more convenient syntax for declaring n-tuple types if one likes (and convenience constructors for tuples).

It's simply  not a "problem."  As for pedagogical aspects of tuples, if Dart added tuples to the language, it would simply take up 1-2 pages in the various tutorials and "how to" books. No "computer science" required - LOL!!!!

- Scott

p.s. FWIW, I am advocate for adding explicit, efficient multi-value return (MVR) to Dart,  and not for adding Tuples to a Dart style language. But I guess I could be open to persuasion.  I love the algebraic data type and pattern matching style of programming ;)

Alex Tatumizer

unread,
Mar 14, 2014, 8:27:19 PM3/14/14
to mi...@dartlang.org
Scott, sorry you didn't answer my question
print( @(42, "Bar", "Bif").runtimeType);

what will it print? :-)

Gen

unread,
Mar 14, 2014, 9:10:35 PM3/14/14
to
I have written my last reply a little bit too quick as I wanted to go to bed ;)

I still fail to see how tuples are not usable like an array with elements of potentially different types.
From http://en.wikipedia.org/wiki/Tuple: In mathematics, computer science, linguistics,[1] and philosophy[2] a tuple is an ordered list of elements

I think a type like this is fine Tuple<T1, T2, T3> or tuple<T1, T2, T3> whatever keyword is still acceptable.

Gen

unread,
Mar 14, 2014, 9:05:19 PM3/14/14
to mi...@dartlang.org
As I think of it, a tuple with only one element does not make much sense.
So tuple literals might require multiple elements and therefore there is more potential for syntactic differentiation.

Scott M

unread,
Mar 14, 2014, 9:20:18 PM3/14/14
to mi...@dartlang.org
Just by way of comparison, in Poly/ML the type of (42,"Bar", "Bif") is represented as:

  int * string * string 

ML has this special "*" syntax for representing tuple types (there is no 1-tuple in ML). I'm not fond of it, but there it is.

Given the Dart "way of things", minus an annotation to a literal, it would print something like this.

List<int> la = ["four", "five"]; // real Dart - this actually works. It's remarkably retarded.
var  lb = <int>[1,2,3]; // real Dart - this is checked - Yay!

Tuple3<int, String, String> ta =  @(42, "Bar",77); // fake Dart  - retaining retarded part
var tb =  <int,String,String>@(42, "Bar", "Baz");  // fake Dart - should check, Yay!

print(la.runtimeType);
print(lb.runtimeType);
print(ta.runtimeType);
print(tb.runtimeType)

>>> output (real and hypothetica >>>>>

List
List<int>
Tuple3
Tuple3<int,String,String>

There you go. I don't know why you find this so complicated. It  more or less have to "works like the rest of Dart". 

Tuples just aren't that mysterious, certainly less so than Maps and user defined classes and first class functions - wow :) 

Personally, without destructuring I think I would find tuples pretty useless (dunno). With destructuring and pattern matching, though, they give you very, very nifty ways of programming. 







Alternatively, an undeclared literal tuple might have the type

  Tuple3<dynamic, dynamic, dynamic>

Thomas Stephenson

unread,
Mar 14, 2014, 9:22:05 PM3/14/14
to mi...@dartlang.org
I'm squarely in the tuple camp too, but a little syntactic sugar for tuple returns wouldn't go astray. 

eg.
Pair<String, int> foo() {
   return 200, "OK"
}

would just be syntactic sugar for


status, message = foo();

> As I think of it, a tuple with only one element does not make much sense.

A tuple with only one element (or zero elements) make perfect sense, they're just isomorphic to a void return and a normal value return respectively.



Thomas Stephenson

unread,
Mar 14, 2014, 9:31:45 PM3/14/14
to mi...@dartlang.org
Sorry, got distracted

Pair<int,String> foo() => return 200, "OK"

would be syntactic sugar for

Pair<int,String> foo() => return new Pair(200, "OK")

And 
status, message = foo()

would be syntactic sugar for

var pair = foo();
var status = pair[0];
var message = pair[1];

Alex Tatumizer

unread,
Mar 14, 2014, 9:44:21 PM3/14/14
to mi...@dartlang.org
> Pair<int,String> foo() => return 200, "OK"
There's a typo ("return" can't be used in this context) should be Pair<int,String> foo() => 200, "OK"
However, this syntax won't work either - because of precedence rules.
E.g. we can write
var list=[()=>200, "OK"];
this is a list of 2 elements: one is a function returning 200, another - just a string "OK". No way to recognize a tuple here.
Syntax problems are difficult.

Thomas Stephenson

unread,
Mar 14, 2014, 10:27:17 PM3/14/14
to mi...@dartlang.org
It's too early in the morning...

Mandating parenthesis would clear up any ambiguity.
var list=[()=>(200,"OK")]
is a list containing a single element

It also cleans up the assignment (which in my original example contained no types)
(var status, String message) = foo();
But I suppose that looks too much like a function definition... 

 



Alex Tatumizer

unread,
Mar 14, 2014, 10:57:20 PM3/14/14
to mi...@dartlang.org
This again leaves us with no way to express 1-tuple.
E.g. I always write
var x= a==0? 1: 2;
as
var x=(a==0?1:2)
(it's just a bit more readable for me).
With proposed syntax, this would be 1-tuple, which is not what I want.
There's no magic bullet here IMO. Just use lightweight classes (they can be made even more lightweight by implementing https://code.google.com/p/dart/issues/detail?id=10374 -please star)
plus campaign for making "new" optional (can't find issue# for it)
 



Thomas Stephenson

unread,
Mar 14, 2014, 11:27:04 PM3/14/14
to mi...@dartlang.org
The space of 1-tuples is isomorphic to the space of normal values. The space of 0-tuples is isomorphic to Void.

f(Tuple tup) => tup[0]
f_inv(x) = new Tuple(x);

So, var x = (a==0 ? 1 : 2) would be a normal value. 

(var result) = x;

would also assign the normal value of x to result, but allowing 1-Tuple expansions on the LHS of assignment would make it compatible with the proposed syntax for expanding 2- (and in general, n-) tuples.

The syntax
() = x

Would be invalid (empty tuple expansion on left side of assignment) or some similar error.


Scott M

unread,
Mar 14, 2014, 11:29:19 PM3/14/14
to mi...@dartlang.org
Yup. :)

Alex Tatumizer

unread,
Mar 14, 2014, 11:36:47 PM3/14/14
to mi...@dartlang.org
> The space of 1-tuples is isomorphic to the space of normal values. The space of 0-tuples is isomorphic to Void
This definition looks nice indeed. Not sure it helps though.
What is the runtime type of
(42, "foo")
(42, "foo", "bar")
?


Sean Eagan

unread,
Mar 15, 2014, 12:00:17 AM3/15/14
to General Dart Discussion
On Fri, Mar 14, 2014 at 4:52 PM, Scott M <scott...@gmail.com> wrote:
Tuples are not like lists or sets or queues at all.
Tuples are the fundamental product type - like a struct or a (kinda like a) class. In theory, all you really need is Pair<A,B>, because you can always nest Pairs to add elements  Pair<String,Pair<int, bool>>. 

Check this out. It's basic, old school CompSci :)


But an instance of a product type is a tuple, described here:


...as "an ordered list of elements".
 


In practice most languages support n-tuples. Each tuple arity is a discrete type. They are not like arrays or list of "variable length". So we might have some convenient syntax to create n-tuples like @(42, "Bar") or @("Fu", 42, 68.9, false). Behind the scenes, these are not "tuple-n" types, but instantiations of the generic types Tuple2<A,B> and Tuple4<A,B,C,D>.  

If you checkout the pull request that's exactly what it is: Tuple2<T1, T2>, Tuple3<T1, T2, T3> etc. classes.

There may even be a "convenience syntax" for writing tuple types, such as @(int, String)

since dart has first-class types, any tuple *type* syntax would need to be different than the tuple *value* syntax.

instead of Tuple2<int, String>. That's great (and common place). 

This is why the "array index" syntax for accessing tuple elements is a very bad idea - it leads to the misunderstanding that a tuple is something like a list or array - they are not. They are simple stripped down "structs" (field names are a convenience feature - a good one - in structs). You can't "iterate" through fields of a structure or "record".

I agree "structs", having named fields, are not list-like, but "tuples", having ordered fields, IMO are list-like, or at least iterable-like.  Of course, we could apply "composition over inheritance" and provide a "toList()" method instead, but I like being able to reuse the existing Iterable interface to define the tuple fields, namely `first` and `last`, which naturally extends to `second`, `third`, etc.


Syntax in the general case doesn't matter, but tuples and destructuring and pattern matching all go together in many language design like rum and coke :)  You won't generally see too much "field access" of a tuple in SML or languages like that. 

Yes, adding destructuring syntax to the language would be nice in the future.


But if you really need to access field-n of a tuple, some syntax like this works better and reinforces the basic semantics of the tuple type.

foo = @(42, "Bar", "Bif");
foo._1
  >>> 42
foo._2
  >>> 42

These are not "indexing" references. They are field references. 

As mentioned above, the referenced pull request has these same fields, but calls them `first`, `second`, `third`, ..., `last`, to reuse the Iterable interface as much as possible.  Since it implements Iterable, it also has `elementAt(i)` which might be useful for some dynamic use case.

Note that `_1`, `_2` etc. wouldn't work in dart because they would be treated as private fields.
 

Hope that helps. 

Thanks for the feedback!

Alex Tatumizer

unread,
Mar 15, 2014, 12:04:19 AM3/15/14
to mi...@dartlang.org
Sorry. I found an answer to runtimeType question. The types are
print((42, "foo").runtimeType); // (int, String)
Here's why:
There's of course an analogy between tuples and parameter sets of a function.
foo(int a, String b){}'
print(foo.runtimeType); // prints (int, String) => dynamic

Strip out :"=> dynamic" - whatever remains is a type of tuple: (int, String)
Looks consistent, right?

Thomas Stephenson

unread,
Mar 15, 2014, 12:20:29 AM3/15/14
to mi...@dartlang.org
Sorry. I found an answer to runtimeType question. The types are
print((42, "foo").runtimeType); // (int, String)
Here's why:
There's of course an analogy between tuples and parameter sets of a function.
foo(int a, String b){}'
print(foo.runtimeType); // prints (int, String) => dynamic

I was going to say something of the kind, before I loaded your reply into the client. Yes, there is a direct correspondence between parameter lists and tuples (parameter lists are just tuples anyway). You could even include the semantics of optional values and named values in tuples types to make the distinction disappear completely.

The only problem that still remains is when you're running the code in unchecked mode

(int i, String str) = (42, "foo", "bar")

Although obviously that assignment fails in checked mode, what should it do in unchecked mode? The best solution I could come up with is to assign 42 to i and ("foo","bar") to str. It's ugly, but the only valid error you could throw would be a type error and you shouldn't be throwing them in unchecked mode...


--

Gen

unread,
Mar 15, 2014, 5:15:48 AM3/15/14
to
I do not know what special tuple literal syntax works best in Dart.

I might prefer this:
Tuple constructor: TupleKeyword
Type declarations: TupleKeyword<T1>, TupleKeyword<T1, T2>, ...
Tuple constructor calls: TupleKeyword(t1), TupleKeyword(t1, t2), ...

The same tuple keyword or symbol to declare all tuple types and all tuple instances.

I am not fond of Tuple1, Tuple2, Tuple3 as special "hardcoded" classes without redeeming benefit for the 1, 2 or 3 differentiation.
You would not have List1, List2, List3 as types either although I understand that List has only one type parameter.

PS
I do not like pair composition because of dynamic implementation overhead.
I do not like field names because of static limitations and runtime type and reflection implementation overhead.
I think that a tuple as special array is more efficient, flexible, comfortable and intuitive than the other solutions.  

Lasse R.H. Nielsen

unread,
Mar 15, 2014, 7:43:25 AM3/15/14
to mi...@dartlang.org
On Sat, Mar 15, 2014 at 4:27 AM, Thomas Stephenson <ova...@gmail.com> wrote:
The space of 1-tuples is isomorphic to the space of normal values. The space of 0-tuples is isomorphic to Void.

Ob-pedantry: The space of 0-tuples has one element, so it's isomorphic to a unit type like Null.
(In ML, () is the single element of the "unit" type, and (x) is equivalent to x).


/L
--
Lasse R.H. Nielsen - l...@google.com  
'Faith without judgement merely degrades the spirit divine'
Google Denmark ApS - Frederiksborggade 20B, 1 sal - 1360 København K - Denmark - CVR nr. 28 86 69 84

Alex Tatumizer

unread,
Mar 15, 2014, 8:14:13 AM3/15/14
to mi...@dartlang.org
>The only problem that still remains is when you're running the code in unchecked mode
> (int i, String str) = (42, "foo", "bar")

What happens here is the same as while calling "foo" with incorrect number of arguments. It throws, even in unchecked mode.
The correspondense between tuples and parameter lists opens other possibilities like 
bar(int i, String b) {...};
foo(int a, String b) {
   var args=argements(); // intrinsic, returns tuple of actual arguments passed, similar to js,
   bar.apply(args); 
   etc. 
)
Apparently, we just "rediscovered" some concept from other languages. Beautiful concept indeed, and obvious in retrospect.
Rediscovering concepts is good for pedagogical purposes :-)

I badly need someone who "rediscovers" Circuit concept. Since it's not implemented elsewhere, people just ignore it (for psychological reasons mostly, like:
if it's so good, why isn't it anywhere else?). In fact, all hardware is built out of circuits, so it's not even that new.
Thomas, maybe you are interested? Please critique!


.

Pablo Guerrero

unread,
Mar 15, 2014, 10:45:25 AM3/15/14
to misc
Hi Alex,

I sympathize with your idea of circuit (in fact, I'm building
something similar), but I think you should frame it in terms other
people can relate to, and use in practice.

As far as I can see on your repo, it's closely related to functional
reactive programming and dataflow programming. Maybe if you explain it
in these terms, and show where your circuit is different, people can
relate to it and see that it is a useful concept already used in many
places.

It's also important to see a useful use case. It's not exactly the
same, but for me, this concept is useful to extend the concept of
simple data binding provided by polymer/angular to the whole system.
You can construct "circuits" that connect different components/objects
in your system and the UI, and everything is updated automatically.
That's pretty cool, and it can show the potential to a web centric
audience like this one.

Still, the syntax you use is pretty scary if you are not use to it, so
I understand that many people wont just get it on a quick look.

Cheers,
Pablo

Alex Tatumizer

unread,
Mar 15, 2014, 11:45:55 AM3/15/14
to mi...@dartlang.org
Pablo,
Any syntax is scary if you never used it before. Actually, if you look at any language beyond direct C descendants, you will find it scary (tried to code in LISP? Prolog?)
The scariest thing for me, for example, is programming with raw Futures. Programs with raw futures are very difficult to debug, maintain, understand, analyze performance etc. Error processing is scary (some errors are silently dropped). There's no clear mental picture of what's going on (at least for me, but can I be unique? Highly unlikely. Note that I do have experience), and bugs love this state of mind. However, if for somebody it's simple, Circuit in no way prevents you from following that route.

The main advantages of circuit come from: 1) concept itself 2) infrastructure (error processing, debugging, performance analysis etc). 3) parallel programming (e.g. you can invoke functions in isolate with no hassle, while preserving all useful properties of the concept).

One of the main obstacles for Circuit is that the author has no clout to influence people other than through logical argumentation.
But one has to have clout BEFORE people start argumenting against you (other than just pronounce you a lunatic). That's the problem.

I will go through you comments once again to see how I can improve documentation.
Thanks a lot. Maybe others can join in, with more techincal questions?

Alex



Alan Knight

unread,
Mar 15, 2014, 11:48:52 AM3/15/14
to mi...@dartlang.org
Leveraging closures, as suggested in another post, also composes reasonably.

consumerToSink(StreamConsumer consumer, TypedefLeftAsAnExercise whatToDo) {
  ... do the work...
  whatToDo(sink, future);

void doSomethingWith(EventSink foo, Future bar) {} 

consumerToSink(consumer, doSomethingWith);

Although using that pattern would suggest different names.

Thomas Stephenson

unread,
Mar 16, 2014, 8:55:05 AM3/16/14
to mi...@dartlang.org
On Sat, Mar 15, 2014 at 4:27 AM, Thomas Stephenson <ova...@gmail.com> wrote:
The space of 1-tuples is isomorphic to the space of normal values. The space of 0-tuples is isomorphic to Void.

Ob-pedantry: The space of 0-tuples has one element, so it's isomorphic to a unit type like Null.
(In ML, () is the single element of the "unit" type, and (x) is equivalent to x).

More pedantry:
In dart, `void` is a singleton containing the value `null` -- there are no empty sets in a language with null :)

void foo() { }

void main() {
   var x = foo();
   //prints `null`.
   print (x);
}

ML is different, it has a bottom type (which *is* an empty set), so it needs a unit type `()` in order to express the concept of a singleton. 

@Alex
Could you link me to where you've explained the circuit idea? I'm familiar with circuits (actually did a thesis on approximating of NP-complete problems via iterative elimination of invalid solutions in a boolean envelope), but I sincerely doubt those are the kind of circuits you're talking about...


--

Alex Tatumizer

unread,
Mar 16, 2014, 9:11:09 AM3/16/14
to mi...@dartlang.org
Could you link me to where you've explained the circuit idea? I'm familiar with circuits (actually did a thesis on approximating of NP-complete problems via iterative elimination of invalid solutions in a boolean envelope), but I sincerely doubt those are the kind of circuits you're talking about...

These circuits are similar to yours :-)

Lasse R.H. Nielsen

unread,
Mar 16, 2014, 12:26:42 PM3/16/14
to mi...@dartlang.org
On Sun, Mar 16, 2014 at 1:55 PM, Thomas Stephenson <ova...@gmail.com> wrote:
On Sat, Mar 15, 2014 at 4:27 AM, Thomas Stephenson <ova...@gmail.com> wrote:
The space of 1-tuples is isomorphic to the space of normal values. The space of 0-tuples is isomorphic to Void.

Ob-pedantry: The space of 0-tuples has one element, so it's isomorphic to a unit type like Null.
(In ML, () is the single element of the "unit" type, and (x) is equivalent to x).

More pedantry:
In dart, `void` is a singleton containing the value `null` -- there are no empty sets in a language with null :)

The null value is not in all types. The `int` type does not contain null ("null is int" -> false). The void type is indeed empty.

It's just that type annotations in Dart allow both the annotated type and null. That is, null is *assignable* to a variable of any type (and returnable by a function with any return type), even if when does not have that type.
Or, in other words, the type annotation `foo` is really checking for the union of foo and Null.

/L

Thomas Stephenson

unread,
Mar 16, 2014, 11:09:31 PM3/16/14
to mi...@dartlang.org
> The null value is not in all types. The `int` type does not contain null ("null is int" -> false). The void type is indeed empty.

Woah, wait what? Well slap me and call me Sally. I've considered `is`
to check for `Type \ {null}` and assignments to check for membership
of the type. Since null is an object, I can see it would be simpler if
null were considered a member of it's own type.

var x = null;
//Hint -- checks for `null` should be done with `== null`
if (x is Null) {
print("The `is` operator is not always null-safe");
} else {
print("The `is` operator is always null-safe");
}

I still don't understand why `void` can't be used as a variable type
or generic though... Future<void> would come in useful sometimes and
is consistent, since assignment to the value which completes the
future allows assignment to `null`.

Lasse R.H. Nielsen

unread,
Mar 17, 2014, 3:05:57 AM3/17/14
to mi...@dartlang.org
On Mon, Mar 17, 2014 at 4:09 AM, Thomas Stephenson <ova...@gmail.com> wrote:
> The null value is not in all types. The `int` type does not contain null ("null is int" -> false). The void type is indeed empty.

Woah, wait what? Well slap me and call me Sally. I've considered `is`
to check for `Type \ {null}` and assignments to check for membership
of the type. Since null is an object, I can see it would be simpler if
null were considered a member of it's own type.

var x = null;
//Hint -- checks for `null` should be done with `== null`
if (x is Null) {
   print("The `is` operator is not always null-safe");
} else {
  print("The `is` operator is always null-safe");
}

Yes, null is an object. If you check "null is Object" or "null is dynamic", you also get true. 
 
I still don't understand why `void` can't be used as a variable type
or generic though... Future<void> would come in useful sometimes and
is consistent, since assignment to the value which completes the
future allows assignment to `null`.

That would actually be nice. It would mean the same as using "Future<Null>", but would better document the intention, and let Future values match function return values in expression.

The Dart syntax was intended to be familiar to programmers of Java and similar languages, so I guess "void" was only allowed as a function return type because that's all it's being used for in other languages.
I'll immediately make a feature request for allowing it everywhere in dart - even though I know it won't be allowed any time soon anyway :)

(Now, if we had strict type assertions that didn't allow null, we could write "void! foo()" for a function that never returns :).

/L 

Ross Smith

unread,
Mar 17, 2014, 3:43:42 AM3/17/14
to mi...@dartlang.org
> Future<void> would come in useful sometimes and is consistent

There was a request for `Void` opened and closed here:

> even though I know it won't be allowed any time soon anyway :)

Perhaps you could add a type alias to `dart:core` without requiring any language change, since the goal is to provide better documentation for streams and futures that are 'signals' and actually return `null` ?  I just wrote the following in the editor (1.3.0.dev_03_02) and there were no analyzer warnings:

```
class Void = Null;
class Foo {  
  Future<Void> bar() => new Future.value();
}
```

cheers

Lasse R.H. Nielsen

unread,
Mar 17, 2014, 4:03:41 AM3/17/14
to mi...@dartlang.org
On Mon, Mar 17, 2014 at 8:43 AM, Ross Smith <d...@futureperfect.info> wrote:
> Future<void> would come in useful sometimes and is consistent

There was a request for `Void` opened and closed here:

Ah, yes. Didn't expect it to be approved.

It could still work with "void" being a an empty type ("x is void" always false, "void x" only allowing null).
 
> even though I know it won't be allowed any time soon anyway :)

Perhaps you could add a type alias to `dart:core` without requiring any language change, since the goal is to provide better documentation for streams and futures that are 'signals' and actually return `null` ?  I just wrote the following in the editor (1.3.0.dev_03_02) and there were no analyzer warnings:

```
class Void = Null;

Sorry to say it, but that's a bug in the analyzer. You can't alias a class, only a mixin application. The "with ..." is mandatory.

/L

Ross Smith

unread,
Mar 17, 2014, 4:29:23 AM3/17/14
to mi...@dartlang.org
> Sorry to say it, but that's a bug in the analyzer. You can't alias a class, only a mixin application. The "with ..." is mandatory.  Bug filed: http://dartbug.com/17519

Okay :/  But maybe this then?

```
class _Void {}
class Void = Null with _Void;
```

Or perhaps this is another analyzer bug :)

cheers,

Lasse R.H. Nielsen

unread,
Mar 17, 2014, 4:46:48 AM3/17/14
to mi...@dartlang.org
On Mon, Mar 17, 2014 at 9:29 AM, Ross Smith <d...@futureperfect.info> wrote:
> Sorry to say it, but that's a bug in the analyzer. You can't alias a class, only a mixin application. The "with ..." is mandatory.  Bug filed: http://dartbug.com/17519

Okay :/  But maybe this then?

```
class _Void {}
class Void = Null with _Void;
```

Or perhaps this is another analyzer bug :)

Yep, sorry. You are not allowed to extend Null.


(and thanks for all the great bug reports!)

It turned out that when we made the Null class visible, a lot of tests needed extra cases that weren't possible before. The analyzer seems to have missed this one :)

/L
-- 

Peter Ahé

unread,
Mar 17, 2014, 4:53:40 AM3/17/14
to mi...@dartlang.org
The syntax you use is for creating new classes. It has nothing to do with type aliases.

What you have created is basically:

class Void extends Null {}

This is not a type alias, just like int isn't an alias for num. 

Cheers,
Peter

--

Ross Smith

unread,
Mar 17, 2014, 5:06:28 AM3/17/14
to mi...@dartlang.org
@Lasse

> Yep, sorry. You are not allowed to extend Null.

Thanks, that is what I thought but it was worth a try :)

@Peter

> The syntax you use is for creating new classes. It has nothing to do with type aliases.

Thanks, I understand the difference and apologize for using the incorrect terminology.  I think that at one point the spec did in fact declare use of `typedef` for mixin application and there may be areas of the spec that have ambiguous language about this?  For example if I search for `type alias` I find text such as:

"via a type alias (15.3.1) for a mixin application (9.1)"

Anyways, I'll stick to my `Future<Null>`s 

cheers

Peter Ahé

unread,
Mar 17, 2014, 6:39:11 AM3/17/14
to General Dart Discussion
On Mon, Mar 17, 2014 at 10:06 AM, Ross Smith <d...@futureperfect.info> wrote:
> @Lasse
>
>> Yep, sorry. You are not allowed to extend Null.
>
> Thanks, that is what I thought but it was worth a try :)
>
> @Peter
>
>> The syntax you use is for creating new classes. It has nothing to do with
>> type aliases.
>
> Thanks, I understand the difference and apologize for using the incorrect
> terminology. I think that at one point the spec did in fact declare use of
> `typedef` for mixin application and there may be areas of the spec that have
> ambiguous language about this? For example if I search for `type alias` I
> find text such as:
>
> "via a type alias (15.3.1) for a mixin application (9.1)"

The syntax is actually for creating a new class class and giving it a
name. This name is not an alias, it is the name of the class. This can
be observed with the mirror system. In the below example, the class A1
is created as a mixin application of the mixin M to the superclass S.
Similarly, the superclass of C is a mixin application of the mixin M
to the superclass S. That is, we have two different classes from "S
with M":

library mixins;

import 'dart:mirrors';

class S {}

class M {}

class A1 = S with M;

class C extends S with M {}

main() {
ClassMirror a1 = reflectClass(A1);
ClassMirror a2 = reflectClass(C).superclass.originalDeclaration;
print('a1.simpleName: ${a1.simpleName}');
print('a2.simpleName: ${a2.simpleName}');
print('a1.mixin.simpleName: ${a1.mixin.simpleName}');
print('a1.superclass.simpleName: ${a1.superclass.simpleName}');
print('a2.mixin.simpleName: ${a2.mixin.simpleName}');
print('a2.superclass.simpleName: ${a2.superclass.simpleName}');
}

This prints:

a1.simpleName: Symbol("A1")
a2.simpleName: Symbol("mixins.S with mixins.M")
a1.mixin.simpleName: Symbol("M")
a1.superclass.simpleName: Symbol("S")
a2.mixin.simpleName: Symbol("M")
a2.superclass.simpleName: Symbol("S")

Cheers,
Peter

Alex Tatumizer

unread,
Mar 20, 2014, 10:06:53 PM3/20/14
to mi...@dartlang.org
Turns out, in dart, (x) and x sometimes have completely different meaning so isomorphism (x) <-> x would have some holes.
Exercise to reader: find examples where it breaks :-) 

Bob Nystrom

unread,
Mar 21, 2014, 12:44:45 PM3/21/14
to General Dart Discussion

On Thu, Mar 20, 2014 at 7:06 PM, Alex Tatumizer <tatu...@gmail.com> wrote:
Exercise to reader: find examples where it breaks :-) 

class Foo {
  noSuchMethod(Invocation invocation) {
    print(invocation.isGetter);
    return (arg) => print("called result with $arg");
  }

  test() {
    bar("arg");
    (bar)("arg");
  }
}

main() {
  new Foo().test();
}

Prints:

false
true
called result with arg

:)

- bob

Alex Tatumizer

unread,
Mar 21, 2014, 1:58:40 PM3/21/14
to mi...@dartlang.org
There's much simpler example, and  closer to the surface of the language.
main() {
 
   print((String).runtimeType);
   print(String.runtimeType);
}
prints:
_Type
Breaking on exception: object of type NoSuchMethodError

Alex Tatumizer

unread,
Mar 21, 2014, 2:06:37 PM3/21/14
to mi...@dartlang.org
@Bob, doesn't printout in your example look completely random? How is (bar)("arg") a getter? Why?
I think it's a bug, and it's fixable. (String) example is probably not.

Lasse R.H. Nielsen

unread,
Mar 21, 2014, 2:11:47 PM3/21/14
to mi...@dartlang.org
On Fri, Mar 21, 2014 at 7:06 PM, Alex Tatumizer <tatu...@gmail.com> wrote:
@Bob, doesn't printout in your example look completely random? How is (bar)("arg") a getter? Why?
I think it's a bug, and it's fixable. (String) example is probably not.

Not random at all.
When you do  o.foo()  , on an object with no foo method, it hits the noSuchMethod method with an invocation that is a method call. When you do  (o.foo) , it is a getter call.

In this case "bar" is a variable that is not in scope, so it means "this.bar", and therefore "bar()" is a function call (that returns a function) and "(bar)()" is a getter call that returns a function, that is then called.

All according to spec.

/L

 


On Fri, Mar 21, 2014 at 1:58 PM, Alex Tatumizer <tatu...@gmail.com> wrote:
There's much simpler example, and  closer to the surface of the language.
main() {
 
   print((String).runtimeType);
   print(String.runtimeType);
}
prints:
_Type
Breaking on exception: object of type NoSuchMethodError


--
For other discussions, see https://groups.google.com/a/dartlang.org/
 
For HOWTO questions, visit http://stackoverflow.com/tags/dart
 
To file a bug report or feature request, go to http://www.dartbug.com/new

To unsubscribe from this group and stop receiving emails from it, send an email to misc+uns...@dartlang.org.

Alex Tatumizer

unread,
Mar 21, 2014, 2:17:12 PM3/21/14
to mi...@dartlang.org
> In this case "bar" is a variable that is not in scope
I missed that, sorry.
Reply all
Reply to author
Forward
0 new messages