[Pitch] make @noescape the default


(Matthew Johnson) #1

@noescape is safer because it does not require thinking about lifetime issues for captured objects. My hunch was that @noescape (or @autoclosure with implies @noescape) is also more common. I had a look through the standard library and this is definitely the case there.

What does everyone think about making @noescape the default and introducing @escaping (or something similar) to annotate function arguments that do escape the call stack?

Matthew


(Dmitri Gribenko) #2

@noescape provides additional guarantees and the optimizer can learn
to rely on it in future. Thus, it effectively becomes ABI, and you
can't remove in future versions of the library without breaking the
ABI. I think adding it should be an explicit decision because of
that.

Dmitri

···

On Sat, Dec 19, 2015 at 12:10 PM, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

@noescape is safer because it does not require thinking about lifetime issues for captured objects. My hunch was that @noescape (or @autoclosure with implies @noescape) is also more common. I had a look through the standard library and this is definitely the case there.

What does everyone think about making @noescape the default and introducing @escaping (or something similar) to annotate function arguments that do escape the call stack?

--
main(i,j){for(i=2;;i++){for(j=2;j<i;j++){if(!(i%j)){j=0;break;}}if
(j){printf("%d\n",i);}}} /*Dmitri Gribenko <gribozavr@gmail.com>*/


(Lily Ballard) #3

-1

The standard library is not representative of how closures are commonly used. Notably, the standard library never executes anything asynchronously (an extremely common use of closures in apps), doesn't ever hold closures that are triggered later by a separate event (e.g. NSNotificationCenter observing, or UIAlertView handlers, or any third-party solution for block-based target/action replacement), no timers, no completion handlers, etc. The only constructs in the standard library that come to mind that would need escaping closures are lazy sequence operations and AnySequence (I haven't actually looked to see if there are any others). But nearly all uses of closures in UIKit require escaping, nearly all uses of closures in libdispatch require escaping, and I wager that nearly all uses of closures in application code require escaping.

-Kevin Ballard

···

On Sat, Dec 19, 2015, at 12:10 PM, Matthew Johnson via swift-evolution wrote:

@noescape is safer because it does not require thinking about lifetime issues for captured objects. My hunch was that @noescape (or @autoclosure with implies @noescape) is also more common. I had a look through the standard library and this is definitely the case there.

What does everyone think about making @noescape the default and introducing @escaping (or something similar) to annotate function arguments that do escape the call stack?

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


(Andrew Bennett) #4

:+1: if it is more common, perhaps even if it isn't. Although I don't think
the standard library is the right place to look. I'm pretty sure if you did
the same check in Dispatch you'd find the opposite case.

I think production app code is probably a better place to look, although
I'm not sure of any good examples of open source swift 2 app code. It would
be nice for a lot of these proposals if there was a common reference list
of libraries and apps which are popular, mature, swift2 and open source so
we could gauge the impact.

···

On Sun, Dec 20, 2015 at 7:10 AM, Matthew Johnson via swift-evolution < swift-evolution@swift.org> wrote:

@noescape is safer because it does not require thinking about lifetime
issues for captured objects. My hunch was that @noescape (or @autoclosure
with implies @noescape) is also more common. I had a look through the
standard library and this is definitely the case there.

What does everyone think about making @noescape the default and
introducing @escaping (or something similar) to annotate function arguments
that do escape the call stack?

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


(Matthew Johnson) #5

@noescape is safer because it does not require thinking about lifetime issues for captured objects. My hunch was that @noescape (or @autoclosure with implies @noescape) is also more common. I had a look through the standard library and this is definitely the case there.

What does everyone think about making @noescape the default and introducing @escaping (or something similar) to annotate function arguments that do escape the call stack?

@noescape provides additional guarantees and the optimizer can learn
to rely on it in future. Thus, it effectively becomes ABI, and you
can't remove in future versions of the library without breaking the
ABI. I think adding it should be an explicit decision because of
that.

That makes sense. Still, it would be a shame to have to add it manually if it is actually the right thing a significant majority of the time (Andrew has a good point about looking at app level code to assess this).

One thing to consider - the compiler already detects when function arguments escape the call stack already and provides an error if they are marked @noescape. Because of this it also detects when they do not escape the call stack. I don't think it would be possible to forget @escaping when that is your purpose as you would get an error.

Changing the escaping behavior of a function argument seems like a major change in functionality and thus breaking ABI is probably more acceptable in that case (I'm trying to think of a time when this would even happen - maybe making an synchronous operation asynchronous?).

As the compiler already detects whether the argument escapes or not it seems like the annotation primarily serves a documentation purpose - if you can accept the idea that a change in escaping behavior is significant enough to warrant potentially breaking ABI.

···

Sent from my iPad

On Dec 19, 2015, at 6:38 PM, Dmitri Gribenko <gribozavr@gmail.com> wrote:
On Sat, Dec 19, 2015 at 12:10 PM, Matthew Johnson via swift-evolution > <swift-evolution@swift.org> wrote:

Dmitri

--
main(i,j){for(i=2;;i++){for(j=2;j<i;j++){if(!(i%j)){j=0;break;}}if
(j){printf("%d\n",i);}}} /*Dmitri Gribenko <gribozavr@gmail.com>*/


(Matthew Johnson) #6

-1

The standard library is not representative of how closures are commonly used. Notably, the standard library never executes anything asynchronously (an extremely common use of closures in apps), doesn't ever hold closures that are triggered later by a separate event (e.g. NSNotificationCenter observing, or UIAlertView handlers, or any third-party solution for block-based target/action replacement), no timers, no completion handlers, etc. The only constructs in the standard library that come to mind that would need escaping closures are lazy sequence operations and AnySequence (I haven't actually looked to see if there are any others). But nearly all uses of closures in UIKit require escaping, nearly all uses of closures in libdispatch require escaping, and I wager that nearly all uses of closures in application code require escaping.

Those are fair points. The right place to look is in application code so my skimming of the library was probably not that relevant.

The fact that the compiler would tell you when you need @escaping but doesn’t tell you when you could add @noescape is part of what prompted my pitch. If we don’t change the default maybe we should at least add a warning when you *could* add @noescape but didn’t. A lot of folks are likely to leave it off when they should really be adding it.

It would be interesting to look at function arguments in application code written in different styles (OO vs mixed vs functional-leaning) and see what the split is for escaping vs non-escaping function arguments.

···

On Dec 19, 2015, at 7:51 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

-Kevin Ballard

On Sat, Dec 19, 2015, at 12:10 PM, Matthew Johnson via swift-evolution wrote:

@noescape is safer because it does not require thinking about lifetime issues for captured objects. My hunch was that @noescape (or @autoclosure with implies @noescape) is also more common. I had a look through the standard library and this is definitely the case there.

What does everyone think about making @noescape the default and introducing @escaping (or something similar) to annotate function arguments that do escape the call stack?

Matthew
_______________________________________________
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


(Chris Lattner) #7

I agree. This is a very significant API restriction that should be opted-into intentionally. It affects subtyping relationships in protocol conformances and overrides as well.

A different objection to @noescape-by-default is that it makes closures second citizens by default, which makes teaching and learning higher-order functional programming significantly less awesome.

-Chris

···

On Dec 19, 2015, at 4:38 PM, Dmitri Gribenko via swift-evolution <swift-evolution@swift.org> wrote:

On Sat, Dec 19, 2015 at 12:10 PM, Matthew Johnson via swift-evolution > <swift-evolution@swift.org> wrote:

@noescape is safer because it does not require thinking about lifetime issues for captured objects. My hunch was that @noescape (or @autoclosure with implies @noescape) is also more common. I had a look through the standard library and this is definitely the case there.

What does everyone think about making @noescape the default and introducing @escaping (or something similar) to annotate function arguments that do escape the call stack?

@noescape provides additional guarantees and the optimizer can learn
to rely on it in future. Thus, it effectively becomes ABI, and you
can't remove in future versions of the library without breaking the
ABI. I think adding it should be an explicit decision because of
that.


(ilya) #8

If we don’t change the default maybe we should at least add a warning

when you *could* add @noescape but didn’t. A lot of folks are likely to
leave it off when they should really be adding it.

I like this idea. We have a warning for variables that can be made into
let, so why not this?

···

On Sun, Dec 20, 2015 at 18:50 Matthew Johnson via swift-evolution < swift-evolution@swift.org> wrote:

> On Dec 19, 2015, at 7:51 PM, Kevin Ballard via swift-evolution < > swift-evolution@swift.org> wrote:
>
> -1
>
> The standard library is not representative of how closures are commonly
used. Notably, the standard library never executes anything asynchronously
(an extremely common use of closures in apps), doesn't ever hold closures
that are triggered later by a separate event (e.g. NSNotificationCenter
observing, or UIAlertView handlers, or any third-party solution for
block-based target/action replacement), no timers, no completion handlers,
etc. The only constructs in the standard library that come to mind that
would need escaping closures are lazy sequence operations and AnySequence
(I haven't actually looked to see if there are any others). But nearly all
uses of closures in UIKit require escaping, nearly all uses of closures in
libdispatch require escaping, and I wager that nearly all uses of closures
in application code require escaping.

Those are fair points. The right place to look is in application code so
my skimming of the library was probably not that relevant.

The fact that the compiler would tell you when you need @escaping but
doesn’t tell you when you could add @noescape is part of what prompted my
pitch. If we don’t change the default maybe we should at least add a
warning when you *could* add @noescape but didn’t. A lot of folks are
likely to leave it off when they should really be adding it.

It would be interesting to look at function arguments in application code
written in different styles (OO vs mixed vs functional-leaning) and see
what the split is for escaping vs non-escaping function arguments.

>
> -Kevin Ballard
>
> On Sat, Dec 19, 2015, at 12:10 PM, Matthew Johnson via swift-evolution > wrote:
>> @noescape is safer because it does not require thinking about lifetime
issues for captured objects. My hunch was that @noescape (or @autoclosure
with implies @noescape) is also more common. I had a look through the
standard library and this is definitely the case there.
>>
>> What does everyone think about making @noescape the default and
introducing @escaping (or something similar) to annotate function arguments
that do escape the call stack?
>>
>> Matthew
>> _______________________________________________
>> 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

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


(Félix Cloutier) #9

@noescape is part of the interface's contract. If you have it for a while (because you had a warning more than because of thoughtful design) then remove it, you're forcing code changes on your clients. I'm not sure it's an appropriate default/warning, at least for public interfaces.

Félix

···

Le 20 déc. 2015 à 11:14:51, ilya via swift-evolution <swift-evolution@swift.org> a écrit :

> If we don’t change the default maybe we should at least add a warning when you *could* add @noescape but didn’t. A lot of folks are likely to leave it off when they should really be adding it.

I like this idea. We have a warning for variables that can be made into let, so why not this?

On Sun, Dec 20, 2015 at 18:50 Matthew Johnson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

> On Dec 19, 2015, at 7:51 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>
> -1
>
> The standard library is not representative of how closures are commonly used. Notably, the standard library never executes anything asynchronously (an extremely common use of closures in apps), doesn't ever hold closures that are triggered later by a separate event (e.g. NSNotificationCenter observing, or UIAlertView handlers, or any third-party solution for block-based target/action replacement), no timers, no completion handlers, etc. The only constructs in the standard library that come to mind that would need escaping closures are lazy sequence operations and AnySequence (I haven't actually looked to see if there are any others). But nearly all uses of closures in UIKit require escaping, nearly all uses of closures in libdispatch require escaping, and I wager that nearly all uses of closures in application code require escaping.

Those are fair points. The right place to look is in application code so my skimming of the library was probably not that relevant.

The fact that the compiler would tell you when you need @escaping but doesn’t tell you when you could add @noescape is part of what prompted my pitch. If we don’t change the default maybe we should at least add a warning when you *could* add @noescape but didn’t. A lot of folks are likely to leave it off when they should really be adding it.

It would be interesting to look at function arguments in application code written in different styles (OO vs mixed vs functional-leaning) and see what the split is for escaping vs non-escaping function arguments.

>
> -Kevin Ballard
>
> On Sat, Dec 19, 2015, at 12:10 PM, Matthew Johnson via swift-evolution wrote:
>> @noescape is safer because it does not require thinking about lifetime issues for captured objects. My hunch was that @noescape (or @autoclosure with implies @noescape) is also more common. I had a look through the standard library and this is definitely the case there.
>>
>> What does everyone think about making @noescape the default and introducing @escaping (or something similar) to annotate function arguments that do escape the call stack?
>>
>> Matthew
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-evolution
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto: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


(Matthew Johnson) #10

@noescape is part of the interface's contract. If you have it for a while (because you had a warning more than because of thoughtful design) then remove it, you're forcing code changes on your clients. I'm not sure it's an appropriate default/warning, at least for public interfaces.

I'm trying to think of examples where you would change this in scenarios that don't already impact clients. A change from synchronous to asynchronous or vice versa is the main example I can think of and that definitely impacts clients (at least when going from sync to async).

Do you have any examples of where you would reasonably change the escaping behavior without an impact to client code other than the escaping change?

···

Sent from my iPad

On Dec 20, 2015, at 10:21 AM, Félix Cloutier <felixcca@yahoo.ca> wrote:

Félix

Le 20 déc. 2015 à 11:14:51, ilya via swift-evolution <swift-evolution@swift.org> a écrit :

> If we don’t change the default maybe we should at least add a warning when you *could* add @noescape but didn’t. A lot of folks are likely to leave it off when they should really be adding it.

I like this idea. We have a warning for variables that can be made into let, so why not this?

On Sun, Dec 20, 2015 at 18:50 Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

> On Dec 19, 2015, at 7:51 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:
>
> -1
>
> The standard library is not representative of how closures are commonly used. Notably, the standard library never executes anything asynchronously (an extremely common use of closures in apps), doesn't ever hold closures that are triggered later by a separate event (e.g. NSNotificationCenter observing, or UIAlertView handlers, or any third-party solution for block-based target/action replacement), no timers, no completion handlers, etc. The only constructs in the standard library that come to mind that would need escaping closures are lazy sequence operations and AnySequence (I haven't actually looked to see if there are any others). But nearly all uses of closures in UIKit require escaping, nearly all uses of closures in libdispatch require escaping, and I wager that nearly all uses of closures in application code require escaping.

Those are fair points. The right place to look is in application code so my skimming of the library was probably not that relevant.

The fact that the compiler would tell you when you need @escaping but doesn’t tell you when you could add @noescape is part of what prompted my pitch. If we don’t change the default maybe we should at least add a warning when you *could* add @noescape but didn’t. A lot of folks are likely to leave it off when they should really be adding it.

It would be interesting to look at function arguments in application code written in different styles (OO vs mixed vs functional-leaning) and see what the split is for escaping vs non-escaping function arguments.

>
> -Kevin Ballard
>
> On Sat, Dec 19, 2015, at 12:10 PM, Matthew Johnson via swift-evolution wrote:
>> @noescape is safer because it does not require thinking about lifetime issues for captured objects. My hunch was that @noescape (or @autoclosure with implies @noescape) is also more common. I had a look through the standard library and this is definitely the case there.
>>
>> What does everyone think about making @noescape the default and introducing @escaping (or something similar) to annotate function arguments that do escape the call stack?
>>
>> Matthew
>> _______________________________________________
>> 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

_______________________________________________
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


(Chris Lattner) #11

An opt-in -Woptimization sort of warning (or static analyzer like feature) would be great for suggesting performance hints like this.

-Chris

···

On Dec 20, 2015, at 8:14 AM, ilya via swift-evolution <swift-evolution@swift.org> wrote:

> If we don’t change the default maybe we should at least add a warning when you *could* add @noescape but didn’t. A lot of folks are likely to leave it off when they should really be adding it.

I like this idea. We have a warning for variables that can be made into let, so why not this?


(Félix Cloutier) #12

In general, any case where you go from immediate evaluation to lazy/delayed evaluation. That change might not be the most frequent, but in my opinion, public interfaces should be as forward compatible as possible.

Assuming that the closure escapes is always safe and the only difference that I know of is that you need to prefix member accesses with self. I'm not part of it, but there's also a non-negligible group of people who apparently always use self and who would not find any benefit, but still get the forward compatibility downsides, if closures were @noescape by default.

Félix

···

Le 20 déc. 2015 à 13:05:40, Matthew Johnson <matthew@anandabits.com> a écrit :

Sent from my iPad

On Dec 20, 2015, at 10:21 AM, Félix Cloutier <felixcca@yahoo.ca <mailto:felixcca@yahoo.ca>> wrote:

@noescape is part of the interface's contract. If you have it for a while (because you had a warning more than because of thoughtful design) then remove it, you're forcing code changes on your clients. I'm not sure it's an appropriate default/warning, at least for public interfaces.

I'm trying to think of examples where you would change this in scenarios that don't already impact clients. A change from synchronous to asynchronous or vice versa is the main example I can think of and that definitely impacts clients (at least when going from sync to async).

Do you have any examples of where you would reasonably change the escaping behavior without an impact to client code other than the escaping change?

Félix

Le 20 déc. 2015 à 11:14:51, ilya via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> a écrit :

> If we don’t change the default maybe we should at least add a warning when you *could* add @noescape but didn’t. A lot of folks are likely to leave it off when they should really be adding it.

I like this idea. We have a warning for variables that can be made into let, so why not this?

On Sun, Dec 20, 2015 at 18:50 Matthew Johnson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

> On Dec 19, 2015, at 7:51 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>
> -1
>
> The standard library is not representative of how closures are commonly used. Notably, the standard library never executes anything asynchronously (an extremely common use of closures in apps), doesn't ever hold closures that are triggered later by a separate event (e.g. NSNotificationCenter observing, or UIAlertView handlers, or any third-party solution for block-based target/action replacement), no timers, no completion handlers, etc. The only constructs in the standard library that come to mind that would need escaping closures are lazy sequence operations and AnySequence (I haven't actually looked to see if there are any others). But nearly all uses of closures in UIKit require escaping, nearly all uses of closures in libdispatch require escaping, and I wager that nearly all uses of closures in application code require escaping.

Those are fair points. The right place to look is in application code so my skimming of the library was probably not that relevant.

The fact that the compiler would tell you when you need @escaping but doesn’t tell you when you could add @noescape is part of what prompted my pitch. If we don’t change the default maybe we should at least add a warning when you *could* add @noescape but didn’t. A lot of folks are likely to leave it off when they should really be adding it.

It would be interesting to look at function arguments in application code written in different styles (OO vs mixed vs functional-leaning) and see what the split is for escaping vs non-escaping function arguments.

>
> -Kevin Ballard
>
> On Sat, Dec 19, 2015, at 12:10 PM, Matthew Johnson via swift-evolution wrote:
>> @noescape is safer because it does not require thinking about lifetime issues for captured objects. My hunch was that @noescape (or @autoclosure with implies @noescape) is also more common. I had a look through the standard library and this is definitely the case there.
>>
>> What does everyone think about making @noescape the default and introducing @escaping (or something similar) to annotate function arguments that do escape the call stack?
>>
>> Matthew
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-evolution
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution

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


(Matthew Johnson) #13

If we don’t change the default maybe we should at least add a warning when you *could* add @noescape but didn’t. A lot of folks are likely to leave it off when they should really be adding it.

I like this idea. We have a warning for variables that can be made into let, so why not this?

An opt-in -Woptimization sort of warning (or static analyzer like feature) would be great for suggesting performance hints like this.

So this turned out to be a bad idea, but at least it turned up a good one during discussion! :slight_smile: I would love -Woptimization.

···

Sent from my iPad

On Dec 21, 2015, at 6:44 PM, Chris Lattner <clattner@apple.com> wrote:

On Dec 20, 2015, at 8:14 AM, ilya via swift-evolution <swift-evolution@swift.org> wrote:

-Chris