future mental model

126 views
Skip to first unread message

Daniel Davidson

unread,
Mar 2, 2015, 11:45:01 AM3/2/15
to

Is there a way to get the effect of RAII using futures that does not rely on memory management schemes. My mental model of how futures work is still developing, so any suggestions on better ways to think about them is appreciated. So when you create a future from scratch you register a function that becomes a list of functions with 1 entry to likely be called and therefore completed at a later time. The then() gives you the ability to chain new functions to be invoked on the end of the list. The whenComplete being like finally allows you to add a function that can be called regardless of success or failure. This sounds promising as a way to manage resources, but the problem is it is triggered immediately after the future it is associated with completes, rather than after all chained thens that may be added. My question is, is there a way to add a function to that list that will always stay at or toward the the end of the list when new then entries are added.

Take this:

import 'dart:async';

p(t) => print(t);

main() {
  {
    new Future
    .sync(() => p('first'))
    .whenComplete(() => p('when complete'))
    .then((_) => p('second'))
    .then((_) => p('third'));
  }
}

The list of functions are:

[
  () => p('first'),
  () => p('when complete'),
  () => p('second'),
  () => p('third'),
]

An example use for this is you open a database or a file, use it and want client code to use it by tacking on some *thens* and then want to close it automatically without having client code be required to call close. Suppose I want the picture to look like this:

[
  () => allocateResource('for first'),
  () => p('first'),
  () => p('when complete'),
  () => allocateResource('for second'),
() => p('second'), () => p('third'), () => deallocateResource('for second'), () => deallocateResource('for first'), ]

The issue is that I want to add the three functions related to first at a single point in time.

    new Future
    .thenBlock(open: allocateResource('for first'),
               close: deallocateResource('for first),
               then: () => p('first'))
    .whenComplete(() => p('when complete'))
    .thenBlock(open: allocateResource('for second'),
               close: deallocateResource('for second'),
               then: () => p('second'))
    .then((_) => p('third'));

Effectively it would be nice to allow thirdfourth, ... infinite to jump the queue and keep pushing the deallocation methods back until they are known not to be needed. The benefit is the chain of functions that require the resources determines the lifespan of the resources.

  • Does this make sense?
  • Is this feasible?
  • Are there already existing ways to achieve this?

Bob Nystrom

unread,
Mar 2, 2015, 12:41:45 PM3/2/15
to General Dart Discussion

On Mon, Mar 2, 2015 at 8:45 AM, Daniel Davidson <phyto...@gmail.com> wrote:
  • Does this make sense?

Yes!

  • Is this feasible?

Yes!
 
  • Are there already existing ways to achieve this?

Yes!

You can't add a "hanging whenComplete" that keeps bumping itself to the end of the future chain. Futures don't really work that way. Instead, you'll use the canonical way to do RAII-like stuff in a language that isn't C++—use a callback.

Ignoring asynchrony for now, let's say you wanted to let the user execute some code in the context of an open file. You also want to ensure the file gets closed when they're done, even if they throw an exception. The classic way to do this is:

void withFile(String path, void callback(RandomAccessFile file)) {
  var file = new File(path).openSync();
  try {
    callback(file);
  } finally {
    file.close();
  }
}

Simple, right? You can translate this exact same idiom directly to futures:

Future withFile(String path, Future callback(RandomAccessFile file)) {
  return new File(path).open().then(callback).whenComplete(() {
    file.close();
  });
}

With async/await, it's:

Future withFile(String path, Future callback(RandomAccessFile file)) async {
  var file = await new File(path).open();
  try {
    await callback(file);
  } finally {
    file.close();
  }
}

Cheers!

- bob

Ahmet A. Akın

unread,
Mar 3, 2015, 10:43:23 AM3/3/15
to mi...@dartlang.org
Hi,
An unrelated question came to my mind. Will there ever be an automatic resource management mechanism in Dart? Like the Java try() or C# using() syntax. Such as for your code:

Instead of this:

var file = await new File(path).open();
  try {
    await callback(file);
  } finally {
    file.close();
  }

This: 

 try(var file = await new File(path).open()) {
    await callback(file);
 }

Thanks.

 
Cheers!

- bob

Bob Nystrom

unread,
Mar 3, 2015, 11:50:07 AM3/3/15
to General Dart Discussion

On Tue, Mar 3, 2015 at 7:43 AM, Ahmet A. Akın <ahm...@gmail.com> wrote:
Will there ever be an automatic resource management mechanism in Dart? Like the Java try() or C# using() syntax.

I can't predict the future, but so far I don't recall there being a request for one. Dart has much better function literal syntax than Java and C#, so cases where you want that behavior are easier to implement just using regular callbacks (with the except of lacking non-local returns).

If it's something that a lot of users started really pushing for, I think it could happen.

Cheers!

- bob

Daniel Davidson

unread,
Mar 4, 2015, 7:06:23 AM3/4/15
to mi...@dartlang.org
Bob,

This is awesome, thanks!

--
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
Reply all
Reply to author
Forward
0 new messages