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

Hignite, Jamie Jamie.Hignite at kindred.com
Wed Jun 8 09:46:32 CDT 2016


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

Jamie



From: <swift-evolution-bounces at swift.org<mailto:swift-evolution-bounces at swift.org>> on behalf of Haravikk via swift-evolution <swift-evolution at swift.org<mailto:swift-evolution at swift.org>>
Reply-To: Haravikk <swift-evolution at haravikk.me<mailto:swift-evolution at haravikk.me>>
Date: Monday, June 6, 2016 at 6:44 PM
To: donny wals <donnywals at gmail.com<mailto:donnywals at gmail.com>>
Cc: "swift-evolution at swift.org<mailto:swift-evolution at swift.org>" <swift-evolution at swift.org<mailto:swift-evolution at 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 at swift.org<mailto:swift-evolution at 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 at swift.org<mailto:swift-evolution at swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution



More information about the swift-evolution mailing list