[Proposal] A more liberal placement of defer


(donny wals) #1

Hi,

When we’re using defer we write some code that we want to execute the moment a scope exits.
This leads to code that could read like:

let fibonacci = sequence(state: (0, 1)) { (pair: inout (Int, Int)) -> Int in
    defer { pair = (pair.1, pair.0 + pair.1) }
    return pair.0
}

What I find strange about this is that we have to write the code that we want to execute after the return before the return.

I’d like to propose a change to defer that would allow the above code to be written as:

let fibonacci = sequence(state: (0, 1)) { (pair: inout (Int, Int)) -> Int in
    return pair.0
    defer { pair = (pair.1, pair.0 + pair.1) }
}

This would make the intent of the code more clear (return first, then mutate state). Not all cases can benefit from this change, but anytime you exit a scope using a return I think it might be more clear to define the defer after the return. The code would more closely mirror the intent of the code.

A rule of thumb I’ve come up with for this is that whenever you’re using return to exit a scope, any defer in that same scope should be executed regardless of it’s position in that same scope. This proposal would supplement the way defer currently works.

What do you all think?


(Erica Sadun) #2

This is problematic. You may want to defer code at different points with different reasons. For example, you might not want to trigger defer until after some preconditions have been met.:

guard something
guard something
allocate memory; defer {release memory}

-- E

···

On Jun 6, 2016, at 1:50 PM, donny wals via swift-evolution <swift-evolution@swift.org> wrote:

Hi,

When we’re using defer we write some code that we want to execute the moment a scope exits.
This leads to code that could read like:

let fibonacci = sequence(state: (0, 1)) { (pair: inout (Int, Int)) -> Int in
   defer { pair = (pair.1, pair.0 + pair.1) }
   return pair.0
}

What I find strange about this is that we have to write the code that we want to execute after the return before the return.

I’d like to propose a change to defer that would allow the above code to be written as:

let fibonacci = sequence(state: (0, 1)) { (pair: inout (Int, Int)) -> Int in
   return pair.0
   defer { pair = (pair.1, pair.0 + pair.1) }
}

This would make the intent of the code more clear (return first, then mutate state). Not all cases can benefit from this change, but anytime you exit a scope using a return I think it might be more clear to define the defer after the return. The code would more closely mirror the intent of the code.

A rule of thumb I’ve come up with for this is that whenever you’re using return to exit a scope, any defer in that same scope should be executed regardless of it’s position in that same scope. This proposal would supplement the way defer currently works.

What do you all think?
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Kenny Leung) #3

This proposal runs counter to the spirit of what defer seems to be meant for in the first place. It’s to save you forgetting to do something (like closing a stream) before you exit a block of code. If you push it down in the code to after the return, then you might as well have just put the code in *before* the return without the defer.

public func doMyThing() {
  let theStream = openStream()
  defer {
    closeStream()
  }
  
  …
  …
  …
  <enough code that it’s off the bottom of the screen>
  return
}

In this case, putting closeStream() after the return would defeat the purpose.

-Kenny

···

On Jun 6, 2016, at 12:50 PM, donny wals via swift-evolution <swift-evolution@swift.org> wrote:

Hi,

When we’re using defer we write some code that we want to execute the moment a scope exits.
This leads to code that could read like:

let fibonacci = sequence(state: (0, 1)) { (pair: inout (Int, Int)) -> Int in
   defer { pair = (pair.1, pair.0 + pair.1) }
   return pair.0
}

What I find strange about this is that we have to write the code that we want to execute after the return before the return.

I’d like to propose a change to defer that would allow the above code to be written as:

let fibonacci = sequence(state: (0, 1)) { (pair: inout (Int, Int)) -> Int in
   return pair.0
   defer { pair = (pair.1, pair.0 + pair.1) }
}

This would make the intent of the code more clear (return first, then mutate state). Not all cases can benefit from this change, but anytime you exit a scope using a return I think it might be more clear to define the defer after the return. The code would more closely mirror the intent of the code.

A rule of thumb I’ve come up with for this is that whenever you’re using return to exit a scope, any defer in that same scope should be executed regardless of it’s position in that same scope. This proposal would supplement the way defer currently works.

What do you all think?
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Michael Peternell) #4

Hi,

you may think of `defer` as a function that pushes a block onto an implicit cleanup stack that is part of every lexical closure. On each scope exit, all blocks from its cleanup stack are popped and executed.

E.g.:

func f(x: Int) {
    defer { print("A"); }
    defer { print("B"); }
    if x == 3 {
        return
    }
    defer { print("C"); }
}

So, f(2) will print "CBA", but f(3) will print "BA" instead. Furthermore, this will change semantics and break the following code:

func g(x: Int) {
    defer { print("A"); }
    let b: Int
    if x == 3 {
        return
    } else {
        b = x
    }
    defer { print("b is \(b)") }
}

In the code above, b is only defined if x is not 3. If x is 3, the last `defer` block cannot be called, and that code would no longer compile.

So I think the current language behavior is more powerful. `defer` is usually used to do cleanup work, and it is called near the place where some resource is initialized. Putting a `defer` block to the end of a function kinda defeats its purpose. And simple functions like fibonacci I would just write without using `defer` at all - it's just confusing to use `defer` and `inout` in this case IMO.

/// Calculates the n'th fibonacci number. (n >= 1)
func fibonacci(n: Int) -> Int {
    var a = 0
    var b = 1
    for _ in 1...n {
        (a,b)=(b, a+b)
    }
    return a
}

Regards,
Michael

···

Am 06.06.2016 um 21:50 schrieb donny wals via swift-evolution <swift-evolution@swift.org>:

Hi,

When we’re using defer we write some code that we want to execute the moment a scope exits.
This leads to code that could read like:

let fibonacci = sequence(state: (0, 1)) { (pair: inout (Int, Int)) -> Int in
   defer { pair = (pair.1, pair.0 + pair.1) }
   return pair.0
}

What I find strange about this is that we have to write the code that we want to execute after the return before the return.

I’d like to propose a change to defer that would allow the above code to be written as:

let fibonacci = sequence(state: (0, 1)) { (pair: inout (Int, Int)) -> Int in
   return pair.0
   defer { pair = (pair.1, pair.0 + pair.1) }
}

This would make the intent of the code more clear (return first, then mutate state). Not all cases can benefit from this change, but anytime you exit a scope using a return I think it might be more clear to define the defer after the return. The code would more closely mirror the intent of the code.

A rule of thumb I’ve come up with for this is that whenever you’re using return to exit a scope, any defer in that same scope should be executed regardless of it’s position in that same scope. This proposal would supplement the way defer currently works.

What do you all think?
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(donny wals) #5

Erica,

Maybe my phrasing was a bit off, but in my proposal it’s really important that the return and the defer are in the same scope. In your example the allocate memory line is in a different scope that the guard/else statements. Therefor, the defer { release memory } shouldn’t be executed if the guards don’t hold.

D

···

On 06 Jun 2016, at 22:09, Erica Sadun <erica@ericasadun.com> wrote:

This is problematic. You may want to defer code at different points with different reasons. For example, you might not want to trigger defer until after some preconditions have been met.:

guard something
guard something
allocate memory; defer {release memory}

-- E

On Jun 6, 2016, at 1:50 PM, donny wals via swift-evolution <swift-evolution@swift.org> wrote:

Hi,

When we’re using defer we write some code that we want to execute the moment a scope exits.
This leads to code that could read like:

let fibonacci = sequence(state: (0, 1)) { (pair: inout (Int, Int)) -> Int in
  defer { pair = (pair.1, pair.0 + pair.1) }
  return pair.0
}

What I find strange about this is that we have to write the code that we want to execute after the return before the return.

I’d like to propose a change to defer that would allow the above code to be written as:

let fibonacci = sequence(state: (0, 1)) { (pair: inout (Int, Int)) -> Int in
  return pair.0
  defer { pair = (pair.1, pair.0 + pair.1) }
}

This would make the intent of the code more clear (return first, then mutate state). Not all cases can benefit from this change, but anytime you exit a scope using a return I think it might be more clear to define the defer after the return. The code would more closely mirror the intent of the code.

A rule of thumb I’ve come up with for this is that whenever you’re using return to exit a scope, any defer in that same scope should be executed regardless of it’s position in that same scope. This proposal would supplement the way defer currently works.

What do you all think?
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Haravikk) #6

While I can kind of see where you’re coming from I’m not sure about the change; the key thing about defer is that it doesn’t just execute in the cases you explicitly define, but can also occur if exceptions are thrown and other exit cases that could be all over the scope.

To compare with other languages, I’ve used several that achieve this with a finally block instead, usually as part of a try/catch like so:

try { doSomethingThatCanThrow(); return 1; }
catch (Exception e) { print(e); return 0; }
finally { doSomeCleanup(); }

Here you have two possible exit points, and in both cases the code in the finally block is executed. But it’s pretty rigid.

This is fine in cases like you suggest where it makes a bit more sense visually, but the cool thing about Swift is that you can declare defer blocks all over the place, build upon them and so-on. It means you can group your cleanup code with the statements that actually require the cleanup, even if there is a ton of extra code that comes afterwards. For example, opening a TCP connection may use a defer block right away to ensure the connection is closed cleanly and any buffers are cleared regardless of how the method ends (normally, IO error etc.), but before that happens there may be a whole load of parsing and other operations before you hit the final return statement.

It’s also pretty clear from the keyword defer; putting it after the return statement actually makes less sense, as there is nothing for it to be deferred in relation to (as there’s nothing else left to do).

I’d say that if you want cleanup to appear visually afterwards you’d be better off promising a finally block, this could be nice to have, especially if it could be applied to most blocks like so:

  do {
    somethingThatCouldThrow()
    if someCondition { return }
  } finally { someCleanup() } // Executed whether the block throws, returns or completes normally

  for eachValue in theValues {
    if doSomethingTo(eachValue) { break }
    if someCondition { return }
    else { throw SomeError() }
  } finally { doSomeCleanup() } // Executes regardless of whether the loop ends normally, breaks, returns or throws

And so-on. I’d say that defer is more flexible overall, but there could be some cause for this visually so you can move simpler deferred code away from the main method body.

···

On 6 Jun 2016, at 20:50, donny wals via swift-evolution <swift-evolution@swift.org> wrote:

Hi,

When we’re using defer we write some code that we want to execute the moment a scope exits.
This leads to code that could read like:

let fibonacci = sequence(state: (0, 1)) { (pair: inout (Int, Int)) -> Int in
   defer { pair = (pair.1, pair.0 + pair.1) }
   return pair.0
}

What I find strange about this is that we have to write the code that we want to execute after the return before the return.

I’d like to propose a change to defer that would allow the above code to be written as:

let fibonacci = sequence(state: (0, 1)) { (pair: inout (Int, Int)) -> Int in
   return pair.0
   defer { pair = (pair.1, pair.0 + pair.1) }
}

This would make the intent of the code more clear (return first, then mutate state). Not all cases can benefit from this change, but anytime you exit a scope using a return I think it might be more clear to define the defer after the return. The code would more closely mirror the intent of the code.

A rule of thumb I’ve come up with for this is that whenever you’re using return to exit a scope, any defer in that same scope should be executed regardless of it’s position in that same scope. This proposal would supplement the way defer currently works.

What do you all think?
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Erica Sadun) #7

Not sure I see how they're in different scopes. Can you explain that to me?

-- E

···

On Jun 6, 2016, at 2:18 PM, donny wals <donnywals@gmail.com> wrote:

Erica,

Maybe my phrasing was a bit off, but in my proposal it’s really important that the return and the defer are in the same scope. In your example the allocate memory line is in a different scope that the guard/else statements. Therefor, the defer { release memory } shouldn’t be executed if the guards don’t hold.

D

On 06 Jun 2016, at 22:09, Erica Sadun <erica@ericasadun.com> wrote:

This is problematic. You may want to defer code at different points with different reasons. For example, you might not want to trigger defer until after some preconditions have been met.:

guard something
guard something
allocate memory; defer {release memory}

-- E


(donny wals) #8

Michael,

How would this proposal break your snippet?

func g(x: Int) {
   defer { print("A"); }
   let b: Int
   if x == 3 {
       return
   } else {
       b = x
   }
   defer { print("b is \(b)") }
}

In this case if x==3 the function should return without executing the final defer. The reason I think it should work like that is that the if statement creates a scope of it’s own. You’re using a return inside of that scope, so only if you’re adding a defer inside of that scope it should be executed even if it’s after the return. The important part here is that the return and the defer should be in the same scope for this proposal to apply.

···

On 06 Jun 2016, at 22:07, Michael Peternell <michael.peternell@gmx.at> wrote:

Hi,

you may think of `defer` as a function that pushes a block onto an implicit cleanup stack that is part of every lexical closure. On each scope exit, all blocks from its cleanup stack are popped and executed.

E.g.:

func f(x: Int) {
   defer { print("A"); }
   defer { print("B"); }
   if x == 3 {
       return
   }
   defer { print("C"); }
}

So, f(2) will print "CBA", but f(3) will print "BA" instead. Furthermore, this will change semantics and break the following code:

func g(x: Int) {
   defer { print("A"); }
   let b: Int
   if x == 3 {
       return
   } else {
       b = x
   }
   defer { print("b is \(b)") }
}

In the code above, b is only defined if x is not 3. If x is 3, the last `defer` block cannot be called, and that code would no longer compile.

So I think the current language behavior is more powerful. `defer` is usually used to do cleanup work, and it is called near the place where some resource is initialized. Putting a `defer` block to the end of a function kinda defeats its purpose. And simple functions like fibonacci I would just write without using `defer` at all - it's just confusing to use `defer` and `inout` in this case IMO.

/// Calculates the n'th fibonacci number. (n >= 1)
func fibonacci(n: Int) -> Int {
   var a = 0
   var b = 1
   for _ in 1...n {
       (a,b)=(b, a+b)
   }
   return a
}

Regards,
Michael

Am 06.06.2016 um 21:50 schrieb donny wals via swift-evolution <swift-evolution@swift.org>:

Hi,

When we’re using defer we write some code that we want to execute the moment a scope exits.
This leads to code that could read like:

let fibonacci = sequence(state: (0, 1)) { (pair: inout (Int, Int)) -> Int in
  defer { pair = (pair.1, pair.0 + pair.1) }
  return pair.0
}

What I find strange about this is that we have to write the code that we want to execute after the return before the return.

I’d like to propose a change to defer that would allow the above code to be written as:

let fibonacci = sequence(state: (0, 1)) { (pair: inout (Int, Int)) -> Int in
  return pair.0
  defer { pair = (pair.1, pair.0 + pair.1) }
}

This would make the intent of the code more clear (return first, then mutate state). Not all cases can benefit from this change, but anytime you exit a scope using a return I think it might be more clear to define the defer after the return. The code would more closely mirror the intent of the code.

A rule of thumb I’ve come up with for this is that whenever you’re using return to exit a scope, any defer in that same scope should be executed regardless of it’s position in that same scope. This proposal would supplement the way defer currently works.

What do you all think?
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Tim Vermeulen) #9

And simple functions like fibonacci I would just write without using `defer` at all - it's just confusing to use `defer` and `inout` in this case IMO.

/// Calculates the n'th fibonacci number. (n>= 1)
func fibonacci(n: Int) ->Int {
var a = 0
var b = 1
for _ in 1...n {
(a,b)=(b, a+b)
}
return a
}

This is simply a function to calculate the n-th Fibonacci number, though. The original code constructed a sequence.

···

Hi,

you may think of `defer` as a function that pushes a block onto an implicit cleanup stack that is part of every lexical closure. On each scope exit, all blocks from its cleanup stack are popped and executed.

E.g.:

func f(x: Int) {
defer { print("A"); }
defer { print("B"); }
if x == 3 {
return
}
defer { print("C"); }
}

So, f(2) will print "CBA", but f(3) will print "BA" instead. Furthermore, this will change semantics and break the following code:

func g(x: Int) {
defer { print("A"); }
let b: Int
if x == 3 {
return
} else {
b = x
}
defer { print("b is \(b)") }
}

In the code above, b is only defined if x is not 3. If x is 3, the last `defer` block cannot be called, and that code would no longer compile.

So I think the current language behavior is more powerful. `defer` is usually used to do cleanup work, and it is called near the place where some resource is initialized. Putting a `defer` block to the end of a function kinda defeats its purpose. And simple functions like fibonacci I would just write without using `defer` at all - it's just confusing to use `defer` and `inout` in this case IMO.

/// Calculates the n'th fibonacci number. (n>= 1)
func fibonacci(n: Int) ->Int {
var a = 0
var b = 1
for _ in 1...n {
(a,b)=(b, a+b)
}
return a
}

Regards,
Michael

> Am 06.06.2016 um 21:50 schrieb donny wals via swift-evolution<swift-evolution@swift.org>:
>
> Hi,
>
> When we’re using defer we write some code that we want to execute the moment a scope exits.
> This leads to code that could read like:
>
> let fibonacci = sequence(state: (0, 1)) { (pair: inout (Int, Int)) ->Int in
> defer { pair = (pair.1, pair.0 + pair.1) }
> return pair.0
> }
>
> What I find strange about this is that we have to write the code that we want to execute after the return before the return.
>
> I’d like to propose a change to defer that would allow the above code to be written as:
>
> let fibonacci = sequence(state: (0, 1)) { (pair: inout (Int, Int)) ->Int in
> return pair.0
> defer { pair = (pair.1, pair.0 + pair.1) }
> }
>
> This would make the intent of the code more clear (return first, then mutate state). Not all cases can benefit from this change, but anytime you exit a scope using a return I think it might be more clear to define the defer after the return. The code would more closely mirror the intent of the code.
>
> A rule of thumb I’ve come up with for this is that whenever you’re using return to exit a scope, any defer in that same scope should be executed regardless of it’s position in that same scope. This proposal would supplement the way defer currently works.
>
> What do you all think?
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution


(Hignite, Jamie) #10

I like the idea of having a finally and a defer.

Jamie

···

From: <swift-evolution-bounces@swift.org<mailto:swift-evolution-bounces@swift.org>> on behalf of Haravikk via swift-evolution <swift-evolution@swift.org<mailto:swift-evolution@swift.org>>
Reply-To: Haravikk <swift-evolution@haravikk.me<mailto:swift-evolution@haravikk.me>>
Date: Monday, June 6, 2016 at 6:44 PM
To: donny wals <donnywals@gmail.com<mailto:donnywals@gmail.com>>
Cc: "swift-evolution@swift.org<mailto:swift-evolution@swift.org>" <swift-evolution@swift.org<mailto:swift-evolution@swift.org>>
Subject: [EXTERNAL] Re: [swift-evolution] [Proposal] A more liberal placement of defer

While I can kind of see where you’re coming from I’m not sure about the change; the key thing about defer is that it doesn’t just execute in the cases you explicitly define, but can also occur if exceptions are thrown and other exit cases that could be all over the scope.

To compare with other languages, I’ve used several that achieve this with a finally block instead, usually as part of a try/catch like so:

try { doSomethingThatCanThrow(); return 1; }
catch (Exception e) { print(e); return 0; }
finally { doSomeCleanup(); }

Here you have two possible exit points, and in both cases the code in the finally block is executed. But it’s pretty rigid.

This is fine in cases like you suggest where it makes a bit more sense visually, but the cool thing about Swift is that you can declare defer blocks all over the place, build upon them and so-on. It means you can group your cleanup code with the statements that actually require the cleanup, even if there is a ton of extra code that comes afterwards. For example, opening a TCP connection may use a defer block right away to ensure the connection is closed cleanly and any buffers are cleared regardless of how the method ends (normally, IO error etc.), but before that happens there may be a whole load of parsing and other operations before you hit the final return statement.

It’s also pretty clear from the keyword defer; putting it after the return statement actually makes less sense, as there is nothing for it to be deferred in relation to (as there’s nothing else left to do).

I’d say that if you want cleanup to appear visually afterwards you’d be better off promising a finally block, this could be nice to have, especially if it could be applied to most blocks like so:

do {
somethingThatCouldThrow()
if someCondition { return }
} finally { someCleanup() } // Executed whether the block throws, returns or completes normally

for eachValue in theValues {
if doSomethingTo(eachValue) { break }
if someCondition { return }
else { throw SomeError() }
} finally { doSomeCleanup() } // Executes regardless of whether the loop ends normally, breaks, returns or throws

And so-on. I’d say that defer is more flexible overall, but there could be some cause for this visually so you can move simpler deferred code away from the main method body.

On 6 Jun 2016, at 20:50, donny wals via swift-evolution <swift-evolution@swift.org<mailto:swift-evolution@swift.org>> wrote:

Hi,

When we’re using defer we write some code that we want to execute the moment a scope exits.
This leads to code that could read like:

let fibonacci = sequence(state: (0, 1)) { (pair: inout (Int, Int)) -> Int in
   defer { pair = (pair.1, pair.0 + pair.1) }
   return pair.0
}

What I find strange about this is that we have to write the code that we want to execute after the return before the return.

I’d like to propose a change to defer that would allow the above code to be written as:

let fibonacci = sequence(state: (0, 1)) { (pair: inout (Int, Int)) -> Int in
   return pair.0
   defer { pair = (pair.1, pair.0 + pair.1) }
}

This would make the intent of the code more clear (return first, then mutate state). Not all cases can benefit from this change, but anytime you exit a scope using a return I think it might be more clear to define the defer after the return. The code would more closely mirror the intent of the code.

A rule of thumb I’ve come up with for this is that whenever you’re using return to exit a scope, any defer in that same scope should be executed regardless of it’s position in that same scope. This proposal would supplement the way defer currently works.

What do you all think?
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org<mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Michael Peternell) #11

Hi,

I think that's an awful idea. I can already tell you that this will never be accepted. It just complicates the design and the semantics of the `defer` statement, and I don't see any advantage. Sorry.

-Michael

···

Am 06.06.2016 um 22:14 schrieb donny wals via swift-evolution <swift-evolution@swift.org>:

Michael,

How would this proposal break your snippet?

func g(x: Int) {
  defer { print("A"); }
  let b: Int
  if x == 3 {
      return
  } else {
      b = x
  }
  defer { print("b is \(b)") }
}

In this case if x==3 the function should return without executing the final defer. The reason I think it should work like that is that the if statement creates a scope of it’s own. You’re using a return inside of that scope, so only if you’re adding a defer inside of that scope it should be executed even if it’s after the return. The important part here is that the return and the defer should be in the same scope for this proposal to apply.

On 06 Jun 2016, at 22:07, Michael Peternell <michael.peternell@gmx.at> wrote:

Hi,

you may think of `defer` as a function that pushes a block onto an implicit cleanup stack that is part of every lexical closure. On each scope exit, all blocks from its cleanup stack are popped and executed.

E.g.:

func f(x: Int) {
  defer { print("A"); }
  defer { print("B"); }
  if x == 3 {
      return
  }
  defer { print("C"); }
}

So, f(2) will print "CBA", but f(3) will print "BA" instead. Furthermore, this will change semantics and break the following code:

func g(x: Int) {
  defer { print("A"); }
  let b: Int
  if x == 3 {
      return
  } else {
      b = x
  }
  defer { print("b is \(b)") }
}

In the code above, b is only defined if x is not 3. If x is 3, the last `defer` block cannot be called, and that code would no longer compile.

So I think the current language behavior is more powerful. `defer` is usually used to do cleanup work, and it is called near the place where some resource is initialized. Putting a `defer` block to the end of a function kinda defeats its purpose. And simple functions like fibonacci I would just write without using `defer` at all - it's just confusing to use `defer` and `inout` in this case IMO.

/// Calculates the n'th fibonacci number. (n >= 1)
func fibonacci(n: Int) -> Int {
  var a = 0
  var b = 1
  for _ in 1...n {
      (a,b)=(b, a+b)
  }
  return a
}

Regards,
Michael

Am 06.06.2016 um 21:50 schrieb donny wals via swift-evolution <swift-evolution@swift.org>:

Hi,

When we’re using defer we write some code that we want to execute the moment a scope exits.
This leads to code that could read like:

let fibonacci = sequence(state: (0, 1)) { (pair: inout (Int, Int)) -> Int in
defer { pair = (pair.1, pair.0 + pair.1) }
return pair.0
}

What I find strange about this is that we have to write the code that we want to execute after the return before the return.

I’d like to propose a change to defer that would allow the above code to be written as:

let fibonacci = sequence(state: (0, 1)) { (pair: inout (Int, Int)) -> Int in
return pair.0
defer { pair = (pair.1, pair.0 + pair.1) }
}

This would make the intent of the code more clear (return first, then mutate state). Not all cases can benefit from this change, but anytime you exit a scope using a return I think it might be more clear to define the defer after the return. The code would more closely mirror the intent of the code.

A rule of thumb I’ve come up with for this is that whenever you’re using return to exit a scope, any defer in that same scope should be executed regardless of it’s position in that same scope. This proposal would supplement the way defer currently works.

What do you all think?
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(donny wals) #12

Hi,

That’s fair enough. IMO structuring code so it reads in the same order it executes is an opportunity to simplify code rather than complicate it. However, I do agree that the fibonacci example I mentioned earlier might not be the best use case and it’s probably the best use case for this proposal. Since in terms of using defer as cleanup code the order doesn’t matter as much. But still.. it feels a bit weird to be forced to write the “after return” code before the actual return. On the other hand, defer has a pretty clear meaning already and if you and others think that this proposal would break semantics, it’s probably not the best idea

D

···

On 06 Jun 2016, at 22:25, michael.peternell@gmx.at wrote:

Hi,

I think that's an awful idea. I can already tell you that this will never be accepted. It just complicates the design and the semantics of the `defer` statement, and I don't see any advantage. Sorry.

-Michael

Am 06.06.2016 um 22:14 schrieb donny wals via swift-evolution <swift-evolution@swift.org>:

Michael,

How would this proposal break your snippet?

func g(x: Int) {
defer { print("A"); }
let b: Int
if x == 3 {
     return
} else {
     b = x
}
defer { print("b is \(b)") }
}

In this case if x==3 the function should return without executing the final defer. The reason I think it should work like that is that the if statement creates a scope of it’s own. You’re using a return inside of that scope, so only if you’re adding a defer inside of that scope it should be executed even if it’s after the return. The important part here is that the return and the defer should be in the same scope for this proposal to apply.

On 06 Jun 2016, at 22:07, Michael Peternell <michael.peternell@gmx.at> wrote:

Hi,

you may think of `defer` as a function that pushes a block onto an implicit cleanup stack that is part of every lexical closure. On each scope exit, all blocks from its cleanup stack are popped and executed.

E.g.:

func f(x: Int) {
defer { print("A"); }
defer { print("B"); }
if x == 3 {
     return
}
defer { print("C"); }
}

So, f(2) will print "CBA", but f(3) will print "BA" instead. Furthermore, this will change semantics and break the following code:

func g(x: Int) {
defer { print("A"); }
let b: Int
if x == 3 {
     return
} else {
     b = x
}
defer { print("b is \(b)") }
}

In the code above, b is only defined if x is not 3. If x is 3, the last `defer` block cannot be called, and that code would no longer compile.

So I think the current language behavior is more powerful. `defer` is usually used to do cleanup work, and it is called near the place where some resource is initialized. Putting a `defer` block to the end of a function kinda defeats its purpose. And simple functions like fibonacci I would just write without using `defer` at all - it's just confusing to use `defer` and `inout` in this case IMO.

/// Calculates the n'th fibonacci number. (n >= 1)
func fibonacci(n: Int) -> Int {
var a = 0
var b = 1
for _ in 1...n {
     (a,b)=(b, a+b)
}
return a
}

Regards,
Michael

Am 06.06.2016 um 21:50 schrieb donny wals via swift-evolution <swift-evolution@swift.org>:

Hi,

When we’re using defer we write some code that we want to execute the moment a scope exits.
This leads to code that could read like:

let fibonacci = sequence(state: (0, 1)) { (pair: inout (Int, Int)) -> Int in
defer { pair = (pair.1, pair.0 + pair.1) }
return pair.0
}

What I find strange about this is that we have to write the code that we want to execute after the return before the return.

I’d like to propose a change to defer that would allow the above code to be written as:

let fibonacci = sequence(state: (0, 1)) { (pair: inout (Int, Int)) -> Int in
return pair.0
defer { pair = (pair.1, pair.0 + pair.1) }
}

This would make the intent of the code more clear (return first, then mutate state). Not all cases can benefit from this change, but anytime you exit a scope using a return I think it might be more clear to define the defer after the return. The code would more closely mirror the intent of the code.

A rule of thumb I’ve come up with for this is that whenever you’re using return to exit a scope, any defer in that same scope should be executed regardless of it’s position in that same scope. This proposal would supplement the way defer currently works.

What do you all think?
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution