Skip to content
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

un-defer, re-defer, defer.is_active #473

Closed
PavelVozenilek opened this issue Sep 13, 2017 · 4 comments
Closed

un-defer, re-defer, defer.is_active #473

PavelVozenilek opened this issue Sep 13, 2017 · 4 comments
Labels
breaking Implementing this issue could cause existing code to no longer compile or have different behavior. enhancement Solving this issue will likely involve adding new logic or components to the codebase. proposal This issue suggests modifications. If it also has the "accepted" label then it is planned.
Milestone

Comments

@PavelVozenilek
Copy link

defer feature was initially invented in year 2000 by Andrei Alexandrescu and Petru Marginean, under name scope guard. ( http://www.drdobbs.com/cpp/generic-change-the-way-you-write-excepti/184403758 )

Alexandrescu later introduced it into D language, in three forms, no less. Language Go brought up keyword defer.


Say we have a connection, want to close it at the end of lexical block, but it can be closed only once. Solution is simple, use a helper flag:

{
  connection = open_connection();
  bool do_not_do_this = false;
  defer if (!do_not_do_this ) close_connection(connection);
  ...
  if (error_occured_and_connection_selfclosed) do_not_do_this = true;
  ...
}

Though simple it has some problems:

  1. defer is high level functionality, fiddling with a flag is the lowest possible level.
  2. The intention (to cancel defer) is hidden.
  3. If the function is complicated and already has lot of local variables adding a flag makes things worse.

My suggestion is to provide the inversion to defer as language feature:

{
  connection = open_connection();
  defer close_connection(connection);
  ...
  if (...) un-defer;
  ...
}

or named alternative:

{
  connection = open_connection();
  const closer = defer close_connection(connection);
  ...
  if (...) un-defer closer;
  ...
}

No easy to misuse helper flags anymore!

Notes:

  1. The name un-defer contains minus. Why not? It is more readable.
  2. Multiple named defers are possible.
  3. Anonymous un-defer could be used only in case when there's just single unnamed defer, otherwise name is required.

It is possible to go even further and add re-defer functionality.

{
  transaction = begin_transaction();
  defer commit(transaction);
  ...
  if (...) re-defer rollback(transaction);
  ...
}

or similarly named variant.

Notes:

  1. Notes for un-defer are also valid here.
  2. It is possible to combine un-defer and re-defer in any way.

As things grew complicated support for assert could be added:

{
  connection = open_connection();
  defer close_connection(connection);
  ...
  if (...) {
    un-defer;
    assert(defer.is_active == false);
  } else {
    assert(defer.is_active == true);
  }
}

Named variant:

{
  connection = open_connection();
  const closer = defer close_connection(connection);
  ...
  if (...) {
    un-defer closer;
    assert(closer.is_active == false);
  } else {
    assert(closer.is_active == true);
  }
}

Notes:

  1. This feature should be allowed only inside assert. Making it general would open Hell's gate.

To support re-defer there could be some way to print current defer action as a string (string with code inside) in debugger and/or IDE.

@andrewrk andrewrk added breaking Implementing this issue could cause existing code to no longer compile or have different behavior. enhancement Solving this issue will likely involve adding new logic or components to the codebase. labels Sep 13, 2017
@andrewrk andrewrk added this to the 0.2.0 milestone Sep 13, 2017
@kyle-github
Copy link

I think I understand the idea here. Let me see if I can restate it correctly:

If you have an action that is not idempotent and you want to make sure it only happens once, then defer can be a problem because it can possible do an action that has already been done. The example is closing a connection.

Is that a correct restatement?

This is precisely the case where I start to think about something like reference counting if I really need that much complexity. I think that if you have so many edge cases in your code that you need all this extra logic that you might want to refactor it a bit.

I think I can see an argument for aborting a pending defer. The idea of making the result of defer a value is nice! That seems like a simple thing that can be leveraged in a lot of ways.

@PavelVozenilek
Copy link
Author

@kyle-github

Yes.

For very complicated situations it would go out of hand no matter what. Possibly the language can restrict number of defer's per function, forcing people to redesign the logic.

@raulgrell
Copy link
Contributor

There is some discussion about expanding defer in #340.

@tiehuis tiehuis added the proposal This issue suggests modifications. If it also has the "accepted" label then it is planned. label Sep 15, 2017
@thejoshwolfe thejoshwolfe changed the title defer enhanced un-defer, re-defer, defer.is_active Sep 19, 2017
@pluto439
Copy link

Huh, didn't knew defer originated from D.

https://dlang.org/exception-safe.html

https://dlang.org/spec/statement.html#ScopeGuardStatement

{
    scope(exit) write("1");
    scope(success) write("2");
    scope(exit) write("3");
    scope(success) write("4");
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking Implementing this issue could cause existing code to no longer compile or have different behavior. enhancement Solving this issue will likely involve adding new logic or components to the codebase. proposal This issue suggests modifications. If it also has the "accepted" label then it is planned.
Projects
None yet
Development

No branches or pull requests

6 participants