Required Callback


(James Campbell) #1

It would be handy if a callback could be marked as required with an
optional descriptive message i.e

class BackgroundTask {
func run(end: @required("You must call end otherwise iOS will penalise
your app for being a bad citizen") () -> Void)
}

That was the developer can comunicate the bad things that can happen if
this callback isn't called such as iOS peanlizing them for not ending a
background task or perhaps memory leaks caused by clean up code unable to
be triggered.

···

*___________________________________*

*James*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com <http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *


(Xiaodi Wu) #2

I can see the use case, but it'd be annoying (or, impossible) to work
around if I intend to call `end` by passing it to a helper function in
another (let's say, precompiled) module. There's no way for the compiler to
inspect that `end` is always called by that other module, and if calling
`end` twice causes bad things to happen, I'm totally out of luck. You'd
need a companion annotation to pass along the requirement to the callee, or
some sort of force-unrequire, but the latter can't have teeth (i.e. can't
enforce at runtime) if the closure is escaping.

···

On Tue, Aug 16, 2016 at 08:39 James Campbell via swift-evolution < swift-evolution@swift.org> wrote:

It would be handy if a callback could be marked as required with an
optional descriptive message i.e

class BackgroundTask {
func run(end: @required("You must call end otherwise iOS will penalise
your app for being a bad citizen") () -> Void)
}

That was the developer can comunicate the bad things that can happen if
this callback isn't called such as iOS peanlizing them for not ending a
background task or perhaps memory leaks caused by clean up code unable to
be triggered.

*___________________________________*

*James*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com
<http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(James Campbell) #3

I think as long as the end callback is referenced in some way that would
still be better than what we have now, if you pass it into your own code
but then continue on to not call it then I think it would be fair for the
compiler to let you shoot yourself in the foot in that case.

Calling end more than once is another case which isn't covered by this
proposal.

so this:

run() { end in

}

would have an error:

but

run() end in {
MyModule(end)
}

would not since we have at least referenced it.

···

*___________________________________*

*James⎥Lead Hustler*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com <http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *

On 16 August 2016 at 15:49, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

I can see the use case, but it'd be annoying (or, impossible) to work
around if I intend to call `end` by passing it to a helper function in
another (let's say, precompiled) module. There's no way for the compiler to
inspect that `end` is always called by that other module, and if calling
`end` twice causes bad things to happen, I'm totally out of luck. You'd
need a companion annotation to pass along the requirement to the callee, or
some sort of force-unrequire, but the latter can't have teeth (i.e. can't
enforce at runtime) if the closure is escaping.
On Tue, Aug 16, 2016 at 08:39 James Campbell via swift-evolution < > swift-evolution@swift.org> wrote:

It would be handy if a callback could be marked as required with an
optional descriptive message i.e

class BackgroundTask {
func run(end: @required("You must call end otherwise iOS will penalise
your app for being a bad citizen") () -> Void)
}

That was the developer can comunicate the bad things that can happen if
this callback isn't called such as iOS peanlizing them for not ending a
background task or perhaps memory leaks caused by clean up code unable to
be triggered.

*___________________________________*

*James*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com
<http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Haravikk) #4

Could this not just behave in the same way as @noescape, in which case you can pass the closure on to other functions so long as they also have the @noescape attribute? In this case passing it as a parameter to another method with the @required attribute would be equivalent to calling it directly (since you know the other method must eventually call it).

···

On 16 Aug 2016, at 15:49, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

I can see the use case, but it'd be annoying (or, impossible) to work around if I intend to call `end` by passing it to a helper function in another (let's say, precompiled) module. There's no way for the compiler to inspect that `end` is always called by that other module, and if calling `end` twice causes bad things to happen, I'm totally out of luck. You'd need a companion annotation to pass along the requirement to the callee, or some sort of force-unrequire, but the latter can't have teeth (i.e. can't enforce at runtime) if the closure is escaping.
On Tue, Aug 16, 2016 at 08:39 James Campbell via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
It would be handy if a callback could be marked as required with an optional descriptive message i.e

class BackgroundTask {
func run(end: @required("You must call end otherwise iOS will penalise your app for being a bad citizen") () -> Void)
}

That was the developer can comunicate the bad things that can happen if this callback isn't called such as iOS peanlizing them for not ending a background task or perhaps memory leaks caused by clean up code unable to be triggered.


(Xiaodi Wu) #5

I wonder if we can be a little more elegant:

- no need for an optional message: if the compiler is going to force you
the implementor to call something, it really doesn't matter why--you just
call it; any additional explanation can be given in doc comments

- at the implementation site, you the implementor would have to parrot
@required in the declaration; that in itself wins some clarity (the
implementor has to know it's required)

- instead of allowing any assignment or reference of the callback to
satisfy the requirement, either require actual invocation in the body, or
allow the callback to be called/assigned/whatever with an extra attribute
@invoked (or whatever the preferred bikeshed)

MyModule(@invoked end)
// or live dangerously:
// _ = @invoked end

···

On Tue, Aug 16, 2016 at 09:54 James Campbell <james@supmenow.com> wrote:

I think as long as the end callback is referenced in some way that would
still be better than what we have now, if you pass it into your own code
but then continue on to not call it then I think it would be fair for the
compiler to let you shoot yourself in the foot in that case.

Calling end more than once is another case which isn't covered by this
proposal.

so this:

run() { end in

}

would have an error:

but

run() end in {
MyModule(end)
}

would not since we have at least referenced it.

*___________________________________*

*James⎥Lead Hustler*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com
<http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *

On 16 August 2016 at 15:49, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

I can see the use case, but it'd be annoying (or, impossible) to work
around if I intend to call `end` by passing it to a helper function in
another (let's say, precompiled) module. There's no way for the compiler to
inspect that `end` is always called by that other module, and if calling
`end` twice causes bad things to happen, I'm totally out of luck. You'd
need a companion annotation to pass along the requirement to the callee, or
some sort of force-unrequire, but the latter can't have teeth (i.e. can't
enforce at runtime) if the closure is escaping.
On Tue, Aug 16, 2016 at 08:39 James Campbell via swift-evolution < >> swift-evolution@swift.org> wrote:

It would be handy if a callback could be marked as required with an
optional descriptive message i.e

class BackgroundTask {
func run(end: @required("You must call end otherwise iOS will penalise
your app for being a bad citizen") () -> Void)
}

That was the developer can comunicate the bad things that can happen if
this callback isn't called such as iOS peanlizing them for not ending a
background task or perhaps memory leaks caused by clean up code unable to
be triggered.

*___________________________________*

*James*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com
<http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Xiaodi Wu) #6

Nice! Still need an escape hatch for when that other function isn't
annotated though, for those situations when you *know* it's always going to
be called and that you should never call it twice.

···

On Tue, Aug 16, 2016 at 11:50 Haravikk <swift-evolution@haravikk.me> wrote:

On 16 Aug 2016, at 15:49, Xiaodi Wu via swift-evolution < > swift-evolution@swift.org> wrote:

I can see the use case, but it'd be annoying (or, impossible) to work
around if I intend to call `end` by passing it to a helper function in
another (let's say, precompiled) module. There's no way for the compiler to
inspect that `end` is always called by that other module, and if calling
`end` twice causes bad things to happen, I'm totally out of luck. You'd
need a companion annotation to pass along the requirement to the callee, or
some sort of force-unrequire, but the latter can't have teeth (i.e. can't
enforce at runtime) if the closure is escaping.
On Tue, Aug 16, 2016 at 08:39 James Campbell via swift-evolution < > swift-evolution@swift.org> wrote:

It would be handy if a callback could be marked as required with an
optional descriptive message i.e

class BackgroundTask {
func run(end: @required("You must call end otherwise iOS will penalise
your app for being a bad citizen") () -> Void)
}

That was the developer can comunicate the bad things that can happen if
this callback isn't called such as iOS peanlizing them for not ending a
background task or perhaps memory leaks caused by clean up code unable to
be triggered.

Could this not just behave in the same way as @noescape, in which case you
can pass the closure on to other functions so long as they also have the
@noescape attribute? In this case passing it as a parameter to another
method with the @required attribute would be equivalent to calling it
directly (since you know the other method must eventually call it).


(James Campbell) #7

I guess that would make sense and you could wrap the callback up in a
anon-closure if the module hadn't adpated the @required property so you get
both compatibility, safety and clarity.

···

*___________________________________*

*James⎥Lead Hustler*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com <http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *

On 16 August 2016 at 17:50, Haravikk <swift-evolution@haravikk.me> wrote:

On 16 Aug 2016, at 15:49, Xiaodi Wu via swift-evolution < > swift-evolution@swift.org> wrote:

I can see the use case, but it'd be annoying (or, impossible) to work
around if I intend to call `end` by passing it to a helper function in
another (let's say, precompiled) module. There's no way for the compiler to
inspect that `end` is always called by that other module, and if calling
`end` twice causes bad things to happen, I'm totally out of luck. You'd
need a companion annotation to pass along the requirement to the callee, or
some sort of force-unrequire, but the latter can't have teeth (i.e. can't
enforce at runtime) if the closure is escaping.
On Tue, Aug 16, 2016 at 08:39 James Campbell via swift-evolution < > swift-evolution@swift.org> wrote:

It would be handy if a callback could be marked as required with an
optional descriptive message i.e

class BackgroundTask {
func run(end: @required("You must call end otherwise iOS will penalise
your app for being a bad citizen") () -> Void)
}

That was the developer can comunicate the bad things that can happen if
this callback isn't called such as iOS peanlizing them for not ending a
background task or perhaps memory leaks caused by clean up code unable to
be triggered.

Could this not just behave in the same way as @noescape, in which case you
can pass the closure on to other functions so long as they also have the
@noescape attribute? In this case passing it as a parameter to another
method with the @required attribute would be equivalent to calling it
directly (since you know the other method must eventually call it).


(Xiaodi Wu) #8

Nicer still!

···

On Tue, Aug 16, 2016 at 11:53 James Campbell <james@supmenow.com> wrote:

I guess that would make sense and you could wrap the callback up in a
anon-closure if the module hadn't adpated the @required property so you get
both compatibility, safety and clarity.

*___________________________________*

*James⎥Lead Hustler*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com
<http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *

On 16 August 2016 at 17:50, Haravikk <swift-evolution@haravikk.me> wrote:

On 16 Aug 2016, at 15:49, Xiaodi Wu via swift-evolution < >> swift-evolution@swift.org> wrote:

I can see the use case, but it'd be annoying (or, impossible) to work
around if I intend to call `end` by passing it to a helper function in
another (let's say, precompiled) module. There's no way for the compiler to
inspect that `end` is always called by that other module, and if calling
`end` twice causes bad things to happen, I'm totally out of luck. You'd
need a companion annotation to pass along the requirement to the callee, or
some sort of force-unrequire, but the latter can't have teeth (i.e. can't
enforce at runtime) if the closure is escaping.
On Tue, Aug 16, 2016 at 08:39 James Campbell via swift-evolution < >> swift-evolution@swift.org> wrote:

It would be handy if a callback could be marked as required with an
optional descriptive message i.e

class BackgroundTask {
func run(end: @required("You must call end otherwise iOS will penalise
your app for being a bad citizen") () -> Void)
}

That was the developer can comunicate the bad things that can happen if
this callback isn't called such as iOS peanlizing them for not ending a
background task or perhaps memory leaks caused by clean up code unable to
be triggered.

Could this not just behave in the same way as @noescape, in which case
you can pass the closure on to other functions so long as they also have
the @noescape attribute? In this case passing it as a parameter to another
method with the @required attribute would be equivalent to calling it
directly (since you know the other method must eventually call it).


(James Campbell) #9

I think that tidies it up a lot, that would make it stick out a lot for me
especially when dealing with iOS's app delegate callbacks.

···

*___________________________________*

*James⎥Lead Hustler*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com <http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *

On 16 August 2016 at 17:29, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

I wonder if we can be a little more elegant:

- no need for an optional message: if the compiler is going to force you
the implementor to call something, it really doesn't matter why--you just
call it; any additional explanation can be given in doc comments

- at the implementation site, you the implementor would have to parrot
@required in the declaration; that in itself wins some clarity (the
implementor has to know it's required)

- instead of allowing any assignment or reference of the callback to
satisfy the requirement, either require actual invocation in the body, or
allow the callback to be called/assigned/whatever with an extra attribute
@invoked (or whatever the preferred bikeshed)

MyModule(@invoked end)
// or live dangerously:
// _ = @invoked end

On Tue, Aug 16, 2016 at 09:54 James Campbell <james@supmenow.com> wrote:

I think as long as the end callback is referenced in some way that would
still be better than what we have now, if you pass it into your own code
but then continue on to not call it then I think it would be fair for the
compiler to let you shoot yourself in the foot in that case.

Calling end more than once is another case which isn't covered by this
proposal.

so this:

run() { end in

}

would have an error:

but

run() end in {
MyModule(end)
}

would not since we have at least referenced it.

*___________________________________*

*James⎥Lead Hustler*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com
<http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *

On 16 August 2016 at 15:49, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

I can see the use case, but it'd be annoying (or, impossible) to work
around if I intend to call `end` by passing it to a helper function in
another (let's say, precompiled) module. There's no way for the compiler to
inspect that `end` is always called by that other module, and if calling
`end` twice causes bad things to happen, I'm totally out of luck. You'd
need a companion annotation to pass along the requirement to the callee, or
some sort of force-unrequire, but the latter can't have teeth (i.e. can't
enforce at runtime) if the closure is escaping.
On Tue, Aug 16, 2016 at 08:39 James Campbell via swift-evolution < >>> swift-evolution@swift.org> wrote:

It would be handy if a callback could be marked as required with an
optional descriptive message i.e

class BackgroundTask {
func run(end: @required("You must call end otherwise iOS will penalise
your app for being a bad citizen") () -> Void)
}

That was the developer can comunicate the bad things that can happen if
this callback isn't called such as iOS peanlizing them for not ending a
background task or perhaps memory leaks caused by clean up code unable to
be triggered.

*___________________________________*

*James*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com
<http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(James Campbell) #10

I'll write up a draft proposal :slight_smile: I think we have something nice :slight_smile:

···

*___________________________________*

*James⎥Lead Hustler*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com <http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *

On 16 August 2016 at 17:54, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Nicer still!

On Tue, Aug 16, 2016 at 11:53 James Campbell <james@supmenow.com> wrote:

I guess that would make sense and you could wrap the callback up in a
anon-closure if the module hadn't adpated the @required property so you get
both compatibility, safety and clarity.

*___________________________________*

*James⎥Lead Hustler*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com
<http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *

On 16 August 2016 at 17:50, Haravikk <swift-evolution@haravikk.me> wrote:

On 16 Aug 2016, at 15:49, Xiaodi Wu via swift-evolution < >>> swift-evolution@swift.org> wrote:

I can see the use case, but it'd be annoying (or, impossible) to work
around if I intend to call `end` by passing it to a helper function in
another (let's say, precompiled) module. There's no way for the compiler to
inspect that `end` is always called by that other module, and if calling
`end` twice causes bad things to happen, I'm totally out of luck. You'd
need a companion annotation to pass along the requirement to the callee, or
some sort of force-unrequire, but the latter can't have teeth (i.e. can't
enforce at runtime) if the closure is escaping.
On Tue, Aug 16, 2016 at 08:39 James Campbell via swift-evolution < >>> swift-evolution@swift.org> wrote:

It would be handy if a callback could be marked as required with an
optional descriptive message i.e

class BackgroundTask {
func run(end: @required("You must call end otherwise iOS will penalise
your app for being a bad citizen") () -> Void)
}

That was the developer can comunicate the bad things that can happen if
this callback isn't called such as iOS peanlizing them for not ending a
background task or perhaps memory leaks caused by clean up code unable to
be triggered.

Could this not just behave in the same way as @noescape, in which case
you can pass the closure on to other functions so long as they also have
the @noescape attribute? In this case passing it as a parameter to another
method with the @required attribute would be equivalent to calling it
directly (since you know the other method must eventually call it).


(Xiaodi Wu) #11

Wait, doesn't work. Your anonymous closure would then be dinged for not
satisfying the requirement. Turtles all the way down. Still need an escape
hatch.

···

On Tue, Aug 16, 2016 at 11:54 James Campbell <james@supmenow.com> wrote:

I'll write up a draft proposal :slight_smile: I think we have something nice :slight_smile:

*___________________________________*

*James⎥Lead Hustler*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com
<http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *

On 16 August 2016 at 17:54, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Nicer still!

On Tue, Aug 16, 2016 at 11:53 James Campbell <james@supmenow.com> wrote:

I guess that would make sense and you could wrap the callback up in a
anon-closure if the module hadn't adpated the @required property so you get
both compatibility, safety and clarity.

*___________________________________*

*James⎥Lead Hustler*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com
<http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *

On 16 August 2016 at 17:50, Haravikk <swift-evolution@haravikk.me> >>> wrote:

On 16 Aug 2016, at 15:49, Xiaodi Wu via swift-evolution < >>>> swift-evolution@swift.org> wrote:

I can see the use case, but it'd be annoying (or, impossible) to work
around if I intend to call `end` by passing it to a helper function in
another (let's say, precompiled) module. There's no way for the compiler to
inspect that `end` is always called by that other module, and if calling
`end` twice causes bad things to happen, I'm totally out of luck. You'd
need a companion annotation to pass along the requirement to the callee, or
some sort of force-unrequire, but the latter can't have teeth (i.e. can't
enforce at runtime) if the closure is escaping.
On Tue, Aug 16, 2016 at 08:39 James Campbell via swift-evolution < >>>> swift-evolution@swift.org> wrote:

It would be handy if a callback could be marked as required with an
optional descriptive message i.e

class BackgroundTask {
func run(end: @required("You must call end otherwise iOS will
penalise your app for being a bad citizen") () -> Void)
}

That was the developer can comunicate the bad things that can happen
if this callback isn't called such as iOS peanlizing them for not ending a
background task or perhaps memory leaks caused by clean up code unable to
be triggered.

Could this not just behave in the same way as @noescape, in which case
you can pass the closure on to other functions so long as they also have
the @noescape attribute? In this case passing it as a parameter to another
method with the @required attribute would be equivalent to calling it
directly (since you know the other method must eventually call it).


(James Campbell) #12

It would though, look :slight_smile: :

Backend.run() { requiredCallback in

NonAnotatedModuled({

//We send a anon closure to the module and we call the callback with a
required specifier to indicate to the compiler it will be called :slight_smile:
@required requiredCallback()
})
}

This would be the same as this:

Backend.run() { requiredCallback in

AnotatedModuled(requiredCallback)
}

And this:

Backend.run() { requiredCallback in

@required requiredCallback()
}

···

*___________________________________*

*James⎥Lead Hustler*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com <http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *

On 16 August 2016 at 17:55, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Wait, doesn't work. Your anonymous closure would then be dinged for not
satisfying the requirement. Turtles all the way down. Still need an escape
hatch.

On Tue, Aug 16, 2016 at 11:54 James Campbell <james@supmenow.com> wrote:

I'll write up a draft proposal :slight_smile: I think we have something nice :slight_smile:

*___________________________________*

*James⎥Lead Hustler*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com
<http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *

On 16 August 2016 at 17:54, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Nicer still!

On Tue, Aug 16, 2016 at 11:53 James Campbell <james@supmenow.com> wrote:

I guess that would make sense and you could wrap the callback up in a
anon-closure if the module hadn't adpated the @required property so you get
both compatibility, safety and clarity.

*___________________________________*

*James⎥Lead Hustler*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com
<http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *

On 16 August 2016 at 17:50, Haravikk <swift-evolution@haravikk.me> >>>> wrote:

On 16 Aug 2016, at 15:49, Xiaodi Wu via swift-evolution < >>>>> swift-evolution@swift.org> wrote:

I can see the use case, but it'd be annoying (or, impossible) to work
around if I intend to call `end` by passing it to a helper function in
another (let's say, precompiled) module. There's no way for the compiler to
inspect that `end` is always called by that other module, and if calling
`end` twice causes bad things to happen, I'm totally out of luck. You'd
need a companion annotation to pass along the requirement to the callee, or
some sort of force-unrequire, but the latter can't have teeth (i.e. can't
enforce at runtime) if the closure is escaping.
On Tue, Aug 16, 2016 at 08:39 James Campbell via swift-evolution < >>>>> swift-evolution@swift.org> wrote:

It would be handy if a callback could be marked as required with an
optional descriptive message i.e

class BackgroundTask {
func run(end: @required("You must call end otherwise iOS will
penalise your app for being a bad citizen") () -> Void)
}

That was the developer can comunicate the bad things that can happen
if this callback isn't called such as iOS peanlizing them for not ending a
background task or perhaps memory leaks caused by clean up code unable to
be triggered.

Could this not just behave in the same way as @noescape, in which case
you can pass the closure on to other functions so long as they also have
the @noescape attribute? In this case passing it as a parameter to another
method with the @required attribute would be equivalent to calling it
directly (since you know the other method must eventually call it).


(Xiaodi Wu) #13

Well, the callsite @required would be what I call your escape hatch :slight_smile:

Hmm, lemme think on this...

···

On Tue, Aug 16, 2016 at 11:59 James Campbell <james@supmenow.com> wrote:

It would though, look :slight_smile: :

Backend.run() { requiredCallback in

NonAnotatedModuled({

//We send a anon closure to the module and we call the callback with a
required specifier to indicate to the compiler it will be called :slight_smile:
@required requiredCallback()
})
}

This would be the same as this:

Backend.run() { requiredCallback in

AnotatedModuled(requiredCallback)
}

And this:

Backend.run() { requiredCallback in

@required requiredCallback()
}

*___________________________________*

*James⎥Lead Hustler*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com
<http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *

On 16 August 2016 at 17:55, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Wait, doesn't work. Your anonymous closure would then be dinged for not
satisfying the requirement. Turtles all the way down. Still need an escape
hatch.

On Tue, Aug 16, 2016 at 11:54 James Campbell <james@supmenow.com> wrote:

I'll write up a draft proposal :slight_smile: I think we have something nice :slight_smile:

*___________________________________*

*James⎥Lead Hustler*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com
<http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *

On 16 August 2016 at 17:54, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Nicer still!

On Tue, Aug 16, 2016 at 11:53 James Campbell <james@supmenow.com> >>>> wrote:

I guess that would make sense and you could wrap the callback up in a
anon-closure if the module hadn't adpated the @required property so you get
both compatibility, safety and clarity.

*___________________________________*

*James⎥Lead Hustler*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com
<http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *

On 16 August 2016 at 17:50, Haravikk <swift-evolution@haravikk.me> >>>>> wrote:

On 16 Aug 2016, at 15:49, Xiaodi Wu via swift-evolution < >>>>>> swift-evolution@swift.org> wrote:

I can see the use case, but it'd be annoying (or, impossible) to work
around if I intend to call `end` by passing it to a helper function in
another (let's say, precompiled) module. There's no way for the compiler to
inspect that `end` is always called by that other module, and if calling
`end` twice causes bad things to happen, I'm totally out of luck. You'd
need a companion annotation to pass along the requirement to the callee, or
some sort of force-unrequire, but the latter can't have teeth (i.e. can't
enforce at runtime) if the closure is escaping.
On Tue, Aug 16, 2016 at 08:39 James Campbell via swift-evolution < >>>>>> swift-evolution@swift.org> wrote:

It would be handy if a callback could be marked as required with an
optional descriptive message i.e

class BackgroundTask {
func run(end: @required("You must call end otherwise iOS will
penalise your app for being a bad citizen") () -> Void)
}

That was the developer can comunicate the bad things that can happen
if this callback isn't called such as iOS peanlizing them for not ending a
background task or perhaps memory leaks caused by clean up code unable to
be triggered.

Could this not just behave in the same way as @noescape, in which
case you can pass the closure on to other functions so long as they also
have the @noescape attribute? In this case passing it as a parameter to
another method with the @required attribute would be equivalent to calling
it directly (since you know the other method must eventually call it).


(Xiaodi Wu) #14

One alternative that comes to mind:

The @escaping stuff came with a helper function, withoutActuallyEscaping,
to deal with attribute mismatches. Perhaps we could have the same here, for
consistency, something like withActuallyInvoking?

···

On Tue, Aug 16, 2016 at 12:05 Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Well, the callsite @required would be what I call your escape hatch :slight_smile:

Hmm, lemme think on this...
On Tue, Aug 16, 2016 at 11:59 James Campbell <james@supmenow.com> wrote:

It would though, look :slight_smile: :

Backend.run() { requiredCallback in

NonAnotatedModuled({

//We send a anon closure to the module and we call the callback with a
required specifier to indicate to the compiler it will be called :slight_smile:
@required requiredCallback()
})
}

This would be the same as this:

Backend.run() { requiredCallback in

AnotatedModuled(requiredCallback)
}

And this:

Backend.run() { requiredCallback in

@required requiredCallback()
}

*___________________________________*

*James⎥Lead Hustler*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com
<http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *

On 16 August 2016 at 17:55, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Wait, doesn't work. Your anonymous closure would then be dinged for not
satisfying the requirement. Turtles all the way down. Still need an escape
hatch.

On Tue, Aug 16, 2016 at 11:54 James Campbell <james@supmenow.com> wrote:

I'll write up a draft proposal :slight_smile: I think we have something nice :slight_smile:

*___________________________________*

*James⎥Lead Hustler*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com
<http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *

On 16 August 2016 at 17:54, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Nicer still!

On Tue, Aug 16, 2016 at 11:53 James Campbell <james@supmenow.com> >>>>> wrote:

I guess that would make sense and you could wrap the callback up in a
anon-closure if the module hadn't adpated the @required property so you get
both compatibility, safety and clarity.

*___________________________________*

*James⎥Lead Hustler*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com
<http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *

On 16 August 2016 at 17:50, Haravikk <swift-evolution@haravikk.me> >>>>>> wrote:

On 16 Aug 2016, at 15:49, Xiaodi Wu via swift-evolution < >>>>>>> swift-evolution@swift.org> wrote:

I can see the use case, but it'd be annoying (or, impossible) to
work around if I intend to call `end` by passing it to a helper function in
another (let's say, precompiled) module. There's no way for the compiler to
inspect that `end` is always called by that other module, and if calling
`end` twice causes bad things to happen, I'm totally out of luck. You'd
need a companion annotation to pass along the requirement to the callee, or
some sort of force-unrequire, but the latter can't have teeth (i.e. can't
enforce at runtime) if the closure is escaping.
On Tue, Aug 16, 2016 at 08:39 James Campbell via swift-evolution < >>>>>>> swift-evolution@swift.org> wrote:

It would be handy if a callback could be marked as required with an
optional descriptive message i.e

class BackgroundTask {
func run(end: @required("You must call end otherwise iOS will
penalise your app for being a bad citizen") () -> Void)
}

That was the developer can comunicate the bad things that can
happen if this callback isn't called such as iOS peanlizing them for not
ending a background task or perhaps memory leaks caused by clean up code
unable to be triggered.

Could this not just behave in the same way as @noescape, in which
case you can pass the closure on to other functions so long as they also
have the @noescape attribute? In this case passing it as a parameter to
another method with the @required attribute would be equivalent to calling
it directly (since you know the other method must eventually call it).


(James Campbell) #15

That sounds fair, the closure that function returns could trigger a runtime
warning when it detects the closure has be deallocated without being
triggered (or even mutliple times but ofc not the focus for this proposal)

···

*___________________________________*

*James⎥Lead Hustler*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com <http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *

On 16 August 2016 at 18:39, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

One alternative that comes to mind:

The @escaping stuff came with a helper function, withoutActuallyEscaping,
to deal with attribute mismatches. Perhaps we could have the same here, for
consistency, something like withActuallyInvoking?

On Tue, Aug 16, 2016 at 12:05 Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Well, the callsite @required would be what I call your escape hatch :slight_smile:

Hmm, lemme think on this...
On Tue, Aug 16, 2016 at 11:59 James Campbell <james@supmenow.com> wrote:

It would though, look :slight_smile: :

Backend.run() { requiredCallback in

NonAnotatedModuled({

//We send a anon closure to the module and we call the callback with a
required specifier to indicate to the compiler it will be called :slight_smile:
@required requiredCallback()
})
}

This would be the same as this:

Backend.run() { requiredCallback in

AnotatedModuled(requiredCallback)
}

And this:

Backend.run() { requiredCallback in

@required requiredCallback()
}

*___________________________________*

*James⎥Lead Hustler*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com
<http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *

On 16 August 2016 at 17:55, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Wait, doesn't work. Your anonymous closure would then be dinged for not
satisfying the requirement. Turtles all the way down. Still need an escape
hatch.

On Tue, Aug 16, 2016 at 11:54 James Campbell <james@supmenow.com> >>>> wrote:

I'll write up a draft proposal :slight_smile: I think we have something nice :slight_smile:

*___________________________________*

*James⎥Lead Hustler*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com
<http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *

On 16 August 2016 at 17:54, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Nicer still!

On Tue, Aug 16, 2016 at 11:53 James Campbell <james@supmenow.com> >>>>>> wrote:

I guess that would make sense and you could wrap the callback up in
a anon-closure if the module hadn't adpated the @required property so you
get both compatibility, safety and clarity.

*___________________________________*

*James⎥Lead Hustler*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com
<http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *

On 16 August 2016 at 17:50, Haravikk <swift-evolution@haravikk.me> >>>>>>> wrote:

On 16 Aug 2016, at 15:49, Xiaodi Wu via swift-evolution < >>>>>>>> swift-evolution@swift.org> wrote:

I can see the use case, but it'd be annoying (or, impossible) to
work around if I intend to call `end` by passing it to a helper function in
another (let's say, precompiled) module. There's no way for the compiler to
inspect that `end` is always called by that other module, and if calling
`end` twice causes bad things to happen, I'm totally out of luck. You'd
need a companion annotation to pass along the requirement to the callee, or
some sort of force-unrequire, but the latter can't have teeth (i.e. can't
enforce at runtime) if the closure is escaping.
On Tue, Aug 16, 2016 at 08:39 James Campbell via swift-evolution < >>>>>>>> swift-evolution@swift.org> wrote:

It would be handy if a callback could be marked as required with
an optional descriptive message i.e

class BackgroundTask {
func run(end: @required("You must call end otherwise iOS will
penalise your app for being a bad citizen") () -> Void)
}

That was the developer can comunicate the bad things that can
happen if this callback isn't called such as iOS peanlizing them for not
ending a background task or perhaps memory leaks caused by clean up code
unable to be triggered.

Could this not just behave in the same way as @noescape, in which
case you can pass the closure on to other functions so long as they also
have the @noescape attribute? In this case passing it as a parameter to
another method with the @required attribute would be equivalent to calling
it directly (since you know the other method must eventually call it).


(James Campbell) #16

Proposed ! https://github.com/apple/swift-evolution/pull/502

···

*___________________________________*

*James⎥Lead Hustler*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com <http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *

On 16 August 2016 at 20:44, James Campbell <james@supmenow.com> wrote:

That sounds fair, the closure that function returns could trigger a
runtime warning when it detects the closure has be deallocated without
being triggered (or even mutliple times but ofc not the focus for this
proposal)

*___________________________________*

*James⎥Lead Hustler*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com
<http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *

On 16 August 2016 at 18:39, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

One alternative that comes to mind:

The @escaping stuff came with a helper function, withoutActuallyEscaping,
to deal with attribute mismatches. Perhaps we could have the same here, for
consistency, something like withActuallyInvoking?

On Tue, Aug 16, 2016 at 12:05 Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Well, the callsite @required would be what I call your escape hatch :slight_smile:

Hmm, lemme think on this...
On Tue, Aug 16, 2016 at 11:59 James Campbell <james@supmenow.com> wrote:

It would though, look :slight_smile: :

Backend.run() { requiredCallback in

NonAnotatedModuled({

//We send a anon closure to the module and we call the callback with a
required specifier to indicate to the compiler it will be called :slight_smile:
@required requiredCallback()
})
}

This would be the same as this:

Backend.run() { requiredCallback in

AnotatedModuled(requiredCallback)
}

And this:

Backend.run() { requiredCallback in

@required requiredCallback()
}

*___________________________________*

*James⎥Lead Hustler*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com
<http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *

On 16 August 2016 at 17:55, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Wait, doesn't work. Your anonymous closure would then be dinged for
not satisfying the requirement. Turtles all the way down. Still need an
escape hatch.

On Tue, Aug 16, 2016 at 11:54 James Campbell <james@supmenow.com> >>>>> wrote:

I'll write up a draft proposal :slight_smile: I think we have something nice :slight_smile:

*___________________________________*

*James⎥Lead Hustler*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com
<http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *

On 16 August 2016 at 17:54, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Nicer still!

On Tue, Aug 16, 2016 at 11:53 James Campbell <james@supmenow.com> >>>>>>> wrote:

I guess that would make sense and you could wrap the callback up in
a anon-closure if the module hadn't adpated the @required property so you
get both compatibility, safety and clarity.

*___________________________________*

*James⎥Lead Hustler*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com
<http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *

On 16 August 2016 at 17:50, Haravikk <swift-evolution@haravikk.me> >>>>>>>> wrote:

On 16 Aug 2016, at 15:49, Xiaodi Wu via swift-evolution < >>>>>>>>> swift-evolution@swift.org> wrote:

I can see the use case, but it'd be annoying (or, impossible) to
work around if I intend to call `end` by passing it to a helper function in
another (let's say, precompiled) module. There's no way for the compiler to
inspect that `end` is always called by that other module, and if calling
`end` twice causes bad things to happen, I'm totally out of luck. You'd
need a companion annotation to pass along the requirement to the callee, or
some sort of force-unrequire, but the latter can't have teeth (i.e. can't
enforce at runtime) if the closure is escaping.
On Tue, Aug 16, 2016 at 08:39 James Campbell via swift-evolution < >>>>>>>>> swift-evolution@swift.org> wrote:

It would be handy if a callback could be marked as required with
an optional descriptive message i.e

class BackgroundTask {
func run(end: @required("You must call end otherwise iOS will
penalise your app for being a bad citizen") () -> Void)
}

That was the developer can comunicate the bad things that can
happen if this callback isn't called such as iOS peanlizing them for not
ending a background task or perhaps memory leaks caused by clean up code
unable to be triggered.

Could this not just behave in the same way as @noescape, in which
case you can pass the closure on to other functions so long as they also
have the @noescape attribute? In this case passing it as a parameter to
another method with the @required attribute would be equivalent to calling
it directly (since you know the other method must eventually call it).


(Brent Royal-Gordon) #17

You know, I'm thinking about that "multiple times" thing...

We know we want a @once (formerly @noescape(once)) for closures which are guaranteed to be called exactly once. This would help us with initializing variables, and especially properties, from a closure. Perhaps we should extend that to support @escaping @once. Would that handle these use cases? Are there use cases for @required where you expect more than one call? I think all of the use cases we've discussed here—ending handlers and such—should only be called once.

···

On Aug 16, 2016, at 12:44 PM, James Campbell via swift-evolution <swift-evolution@swift.org> wrote:

That sounds fair, the closure that function returns could trigger a runtime warning when it detects the closure has be deallocated without being triggered (or even mutliple times but ofc not the focus for this proposal)

--
Brent Royal-Gordon
Architechies


(Ben Rimmington) #18

What are the differences between
<https://github.com/apple/swift-evolution/pull/502> and
<https://github.com/apple/swift-evolution/blob/master/proposals/0073-noescape-once.md>
?

application(_:handleEventsForBackgroundURLSession:completionHandler:)
has an @escaping `completionHandler`, so can @required be enforced?

<https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160125/008478.html>

-- Ben


(James Campbell) #19

Most of the cases I can think of for @required are for clean up mechanisms
triggered by the closure and as such would only need to be called once.

So in theory @once may be able to cover this case, in a sense I guess you
could argue @once is just a subset of @required it would check it is
exactly once. But @required is about making sure it is called at all.

···

*___________________________________*

*James⎥Lead Hustler*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com <http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *

On 17 August 2016 at 13:43, Brent Royal-Gordon <brent@architechies.com> wrote:

> On Aug 16, 2016, at 12:44 PM, James Campbell via swift-evolution < > swift-evolution@swift.org> wrote:
>
> That sounds fair, the closure that function returns could trigger a
runtime warning when it detects the closure has be deallocated without
being triggered (or even mutliple times but ofc not the focus for this
proposal)

You know, I'm thinking about that "multiple times" thing...

We know we want a @once (formerly @noescape(once)) for closures which are
guaranteed to be called exactly once. This would help us with initializing
variables, and especially properties, from a closure. Perhaps we should
extend that to support @escaping @once. Would that handle these use cases?
Are there use cases for @required where you expect more than one call? I
think all of the use cases we've discussed here—ending handlers and
such—should only be called once.

--
Brent Royal-Gordon
Architechies


(James Campbell) #20

@noescape is a hint that the callback will only be called once and allows
compiler to optimise, we may or may not be able to combine @required
functionality into that but it looks like it was rejected for now.

@required is a compiler hint / sanity check to enforce that the callback is
actually called once by the developer. There is no focus on any compiler
enhancements other than that.