www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Defer in D

reply Xinok <xinok live.com> writes:
I stumbled upon an example demonstrating defer in Go which I 
thought was interesting. Defer is similar to scope in D except 
they're called at end of function rather than end of scope; you 
can queue multiple defer calls by writing them inside of a loop. 
This implies that it internally builds a stack of delegates which 
are then executed LIFO once the function returns (or panics).

https://tour.golang.org/flowcontrol/13

I took a quick look through Phobos but didn't see anything 
similar so I wrote this as a proof of concept and to elicit 
discussion. This also works should the function throw rather than 
return gracefully.

http://dpaste.dzfl.pl/23c665bdae9e

I think a library solution is elegant enough that it doesn't need 
to be built into the language but that doesn't mean it needs to 
be in Phobos either. Does anybody have a use case for "defer" 
that isn't already adequately covered by scope statements?
Mar 19 2016
next sibling parent reply Nick Treleaven <ntrel-pub mybtinternet.com> writes:
On Saturday, 19 March 2016 at 23:16:40 UTC, Xinok wrote:
 I took a quick look through Phobos but didn't see anything 
 similar so I wrote this as a proof of concept and to elicit 
 discussion. This also works should the function throw rather 
 than return gracefully.

 http://dpaste.dzfl.pl/23c665bdae9e
Nice! This is more flexible than Go's defer as you can have more than one stack of them. I don't quite get this:
 defer((int i) => writeln(i), i);
If it was defer(() => writeln(i)), would that do the same? (Dpaste won't seem to let me edit your example on my tablet).
Mar 20 2016
parent reply Bauss <jj_1337 live.dk> writes:
On Sunday, 20 March 2016 at 14:11:11 UTC, Nick Treleaven wrote:
 On Saturday, 19 March 2016 at 23:16:40 UTC, Xinok wrote:
 I took a quick look through Phobos but didn't see anything 
 similar so I wrote this as a proof of concept and to elicit 
 discussion. This also works should the function throw rather 
 than return gracefully.

 http://dpaste.dzfl.pl/23c665bdae9e
Nice! This is more flexible than Go's defer as you can have more than one stack of them. I don't quite get this:
 defer((int i) => writeln(i), i);
If it was defer(() => writeln(i)), would that do the same? (Dpaste won't seem to let me edit your example on my tablet).
No that's not the same, since the reference to i does not change when doing: defer(() => writeln(i)) However with defer((int i) => writeln(i), i) you pass the value of i to the lambda expression which stores the value in a new reference. The problem with the first one is the value will always be the last value of i.
Mar 20 2016
parent reply Nick Treleaven <ntrel-pub mybtinternet.com> writes:
On 20/03/2016 16:18, Bauss wrote:
 defer((int i) => writeln(i), i);
If it was defer(() => writeln(i)), would that do the same? (Dpaste won't seem to let me edit your example on my tablet).
(or on my laptop)
 No that's not the same, since the reference to i does not change when
 doing:
 defer(() => writeln(i))

 However with defer((int i) => writeln(i), i) you pass the value of i to
 the lambda expression which stores the value in a new reference.

 The problem with the first one is the value will always be the last
 value of i.
Thanks. So we need the lambda to capture args (below) rather than capturing i at the callsite: void opCall(ARGS...)(void delegate(ARGS) call, ARGS args) { stack.put(() => call(args)); } The closure above allocates its copy of args on the heap, instead of the callsite closure which captures i by reference.
Mar 20 2016
parent Nick Treleaven <ntrel-pub mybtinternet.com> writes:
On 20/03/2016 16:57, Nick Treleaven wrote:
      void opCall(ARGS...)(void delegate(ARGS) call, ARGS args)
      {
          stack.put(() => call(args));
      }
Maybe this method would be nice (does the same thing): Deferrer d; ... d.capture!writeln("i = ", i); The name capture makes it clearer the arguments are not taken by reference IMO. Xinok: I suggest for now you put it in a Github repository somewhere.
Mar 21 2016
prev sibling next sibling parent angel <andrey.gelman gmail.com> writes:
I would, actually, like to see it integrated with the core 
language syntax, kinda:
scope(function) ...

While your solution is viable from the technical point of view, 
having a consistent language syntax could also be nice.
Mar 21 2016
prev sibling parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On 20-Mar-2016 02:16, Xinok wrote:
 I stumbled upon an example demonstrating defer in Go which I thought was
 interesting. Defer is similar to scope in D except they're called at end
 of function rather than end of scope; you can queue multiple defer calls
 by writing them inside of a loop. This implies that it internally builds
 a stack of delegates which are then executed LIFO once the function
 returns (or panics).

 https://tour.golang.org/flowcontrol/13
 I think a library solution is elegant enough that it doesn't need to be
 built into the language but that doesn't mean it needs to be in Phobos
 either. Does anybody have a use case for "defer" that isn't already
 adequately covered by scope statements?
The main use case in Go that needs it specifically as a function level primitive is this: files := []File{} for i := paths { files[i], err := os.Open(paths[i]) if err != nil { return errors.Errorf("Failed to open %s", paths[i]) } defer files[i].Close() } ... // lots of code using files So in a nutshell - lack of RAII while operating on collections of resources. -- Dmitry Olshansky
Mar 21 2016
parent Xinok <xinok live.com> writes:
On Monday, 21 March 2016 at 17:46:05 UTC, Dmitry Olshansky wrote:
 ...
 The main use case in Go that needs it specifically as a 
 function level primitive is  this:

 files := []File{}
 for i := paths {
 	files[i], err := os.Open(paths[i])
 	if err != nil {
 		return errors.Errorf("Failed to open %s", paths[i])
 	}
 	defer files[i].Close()
 }
 ... // lots of code using files


 So in a nutshell - lack of RAII while operating on collections 
 of resources.
D and Go have different mechanisms for handling errors/exceptions. I'll refrain from debating "A is better than B" but I haven't seen any use case which isn't already adequately covered by the existing mechanisms in D. However, one thing I do find inferior is the inability of D lambdas to capture by value. Thus, the simple example of "writeln(i)" may produce unexpected results. Nick: https://github.com/Xinok/scrapheap/blob/master/defer.d
Mar 21 2016