[swift-evolution] [Proposal] A more liberal placement of defer

donny wals donnywals at gmail.com
Mon Jun 6 15:33:27 CDT 2016


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 at 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 at 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 at 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 at 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 at swift.org
>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>> 
>> 
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
> 



More information about the swift-evolution mailing list