Closure delegation


(Pierre Monod-Broca) #1

A groovy closure can have a delegate which replaces `this` as the default receiver. The issue in groovy is that it is not compatible with static compilation, and there is no way to know from the code what is the type of the delegate.

It works great for DSL. It would work great the Swift Package Manager manifest, among other things.

It could look like this in swift

protocol Bar {
    var someConfig: String { get set }
}

func prepareSomething(setup: @delegate Bar -> () -> ()) {
    let bar = BarImplementation()
    // code before
    setup(bar)()
    // code after
}

prepareSomething { () -> () in
    someConfig = "Hello world"
}

Where `someConfig` would refer to bar.

Pierre


(James Campbell) #2

Could you explain a little more its a bit confusing ?

···

On Fri, Dec 11, 2015 at 10:32 AM, Pierre Monod-Broca via swift-evolution < swift-evolution@swift.org> wrote:

A groovy closure can have a delegate which replaces `this` as the default
receiver. The issue in groovy is that it is not compatible with static
compilation, and there is no way to know from the code what is the type of
the delegate.

It works great for DSL. It would work great the Swift Package Manager
manifest, among other things.

It could look like this in swift

protocol Bar {
    var someConfig: String { get set }
}

func prepareSomething(setup: @delegate Bar -> () -> ()) {
    let bar = BarImplementation()
    // code before
    setup(bar)()
    // code after
}

prepareSomething { () -> () in
    someConfig = "Hello world"
}

Where `someConfig` would refer to bar.

Pierre

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

--
 Wizard
james@supmenow.com
+44 7523 279 698


(Pierre Monod-Broca) #3

Could you explain a little more its a bit confusing ?

A groovy closure can have a delegate which replaces `this` as the default receiver. The issue in groovy is that it is not compatible with static compilation, and there is no way to know from the code what is the type of the delegate.

It works great for DSL. It would work great the Swift Package Manager manifest, among other things.

It could look like this in swift

protocol Bar {
    var someConfig: String { get set }
}

func prepareSomething(setup: @delegate Bar -> () -> ()) {

Here we define a function `prepareSomething(_:)` which receives one parameter: a closure that takes a delegate conforming to `Bar`, and otherwise take no parameter and returns nothing

    let bar = BarImplementation()
    // code before
    setup(bar)()

Here we pass a delegate to the closure, then call the closure

    // code after
}

prepareSomething { () -> () in

Here we call the function `prepareSomething(_:)` with a closure which we define a the same time

    someConfig = "Hello world"

Here `someConfig` is a property of the closure’s delegate

}

Where `someConfig` would refer to bar.

I’m not sure about the syntax, we could also declare the delegate that way, maybe :
func prepareSomething(doSomething: @delegate(Bar) () -> ()) {
    /**/
}

···

Le 11 déc. 2015 à 11:54, James Campbell <james@supmenow.com> a écrit :
On Fri, Dec 11, 2015 at 10:32 AM, Pierre Monod-Broca via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Pierre

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

--
 Wizard
james@supmenow.com <mailto:james@supmenow.com>
+44 7523 279 698


(James Campbell) #4

So I don't like this way of declaring it as it is not very clear what it
is doing but I actually prefer the javascript way of binding, so in your
example the code would be this:

func prepareSomething(setup:() -> ()) {
    let bar = BarImplementation()
    // code before
    setup().bind(bar)
    // code after
}

The bind method lets Swift know it should bind this to `bar`, so now this
is the same as writing `bar`. So in your example:

without delegation
prepareSomething { delegate in
    delegate.someConfig = "Hello World"
}

with binding
prepareSomething {
    someConfig = "Hello World"
}

These would work the same :slight_smile:

without delegation
prepareSomething { delegate in
    delegate.someConfig = "Hello World"
}

with delegation
prepareSomething {
    someConfig = "Hello World"
}

The exemple is not very compelling, but in a manifest file it would
reduce boiler plate.

Pierre

Whats the advantage over this ?

protocol Bar {

    var someConfig: String { get set }
}

func prepareSomething(setup: (delegate: Bar) -> ()) {

Could you explain a little more its a bit confusing ?

A groovy closure can have a delegate which replaces `this` as the
default receiver. The issue in groovy is that it is not compatible with
static compilation, and there is no way to know from the code what is the
type of the delegate.

It works great for DSL. It would work great the Swift Package Manager
manifest, among other things.

It could look like this in swift

protocol Bar {
    var someConfig: String { get set }
}

func prepareSomething(setup: @delegate Bar -> () -> ()) {

Here we define a function `prepareSomething(_:)` which receives one
parameter: a closure that takes a delegate conforming to `Bar`, and
otherwise take no parameter and returns nothing

    let bar = BarImplementation()

    // code before
    setup(bar)()

Here we pass a delegate to the closure, then call the closure

    // code after

}

prepareSomething { () -> () in

Here we call the function `prepareSomething(_:)` with a closure which we
define a the same time

    someConfig = "Hello world"

Here `someConfig` is a property of the closure’s delegate

}

Where `someConfig` would refer to bar.

I’m not sure about the syntax, we could also declare the delegate that
way, maybe :
func prepareSomething(doSomething: @delegate(Bar) () -> ()) {
    /**/
}

Pierre

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

--
 Wizard
james@supmenow.com
+44 7523 279 698

--
 Wizard
james@supmenow.com
+44 7523 279 698

--
 Wizard
james@supmenow.com
+44 7523 279 698

···

On Fri, Dec 11, 2015 at 12:14 PM, James Campbell <james@supmenow.com> wrote:

On Fri, Dec 11, 2015 at 11:49 AM, Pierre Monod-Broca < > pierremonodbroca@gmail.com> wrote:

Le 11 déc. 2015 à 12:29, James Campbell <james@supmenow.com> a écrit :
On Fri, Dec 11, 2015 at 11:27 AM, Pierre Monod-Broca < >> pierremonodbroca@gmail.com> wrote:

Le 11 déc. 2015 à 11:54, James Campbell <james@supmenow.com> a écrit :
On Fri, Dec 11, 2015 at 10:32 AM, Pierre Monod-Broca via swift-evolution >>> <swift-evolution@swift.org> wrote:

--
 Wizard
james@supmenow.com
+44 7523 279 698


(Marc Knaup) #5

How would capture semantics work?

I think this could easily lead to reference cycles as the parameter is
captured strongly without any hint at the call-site.
The developer should be warned and have a way to state the capture
semantics explicitly through the capture list.

If the delegate parameter "Bar" becomes "self" in the closure then using
"self" everywhere should be required just as in normal closures.
"@noescape" would avoid this but the closure reference must be stored then.
If the delegate parameter "Bar" really becomes "self" there's also the
problem that the original "self" variable suddenly becomes hidden.

···

On Fri, Dec 11, 2015 at 1:15 PM, James Campbell via swift-evolution < swift-evolution@swift.org> wrote:

On Fri, Dec 11, 2015 at 12:14 PM, James Campbell <james@supmenow.com> > wrote:

So I don't like this way of declaring it as it is not very clear what it
is doing but I actually prefer the javascript way of binding, so in your
example the code would be this:

func prepareSomething(setup:() -> ()) {
    let bar = BarImplementation()
    // code before
    setup().bind(bar)
    // code after
}

The bind method lets Swift know it should bind this to `bar`, so now this
is the same as writing `bar`. So in your example:

without delegation
prepareSomething { delegate in
    delegate.someConfig = "Hello World"
}

with binding
prepareSomething {
    someConfig = "Hello World"
}

These would work the same :slight_smile:

On Fri, Dec 11, 2015 at 11:49 AM, Pierre Monod-Broca < >> pierremonodbroca@gmail.com> wrote:

without delegation
prepareSomething { delegate in
    delegate.someConfig = "Hello World"
}

with delegation
prepareSomething {
    someConfig = "Hello World"
}

The exemple is not very compelling, but in a manifest file it would
reduce boiler plate.

Pierre

Le 11 déc. 2015 à 12:29, James Campbell <james@supmenow.com> a écrit :

Whats the advantage over this ?

protocol Bar {

    var someConfig: String { get set }
}

func prepareSomething(setup: (delegate: Bar) -> ()) {

On Fri, Dec 11, 2015 at 11:27 AM, Pierre Monod-Broca < >>> pierremonodbroca@gmail.com> wrote:

Le 11 déc. 2015 à 11:54, James Campbell <james@supmenow.com> a écrit :

Could you explain a little more its a bit confusing ?

On Fri, Dec 11, 2015 at 10:32 AM, Pierre Monod-Broca via >>>> swift-evolution <swift-evolution@swift.org> wrote:

A groovy closure can have a delegate which replaces `this` as the
default receiver. The issue in groovy is that it is not compatible with
static compilation, and there is no way to know from the code what is the
type of the delegate.

It works great for DSL. It would work great the Swift Package Manager
manifest, among other things.

It could look like this in swift

protocol Bar {
    var someConfig: String { get set }
}

func prepareSomething(setup: @delegate Bar -> () -> ()) {

Here we define a function `prepareSomething(_:)` which receives one
parameter: a closure that takes a delegate conforming to `Bar`, and
otherwise take no parameter and returns nothing

    let bar = BarImplementation()

    // code before
    setup(bar)()

Here we pass a delegate to the closure, then call the closure

    // code after

}

prepareSomething { () -> () in

Here we call the function `prepareSomething(_:)` with a closure which
we define a the same time

    someConfig = "Hello world"

Here `someConfig` is a property of the closure’s delegate

}

Where `someConfig` would refer to bar.

I’m not sure about the syntax, we could also declare the delegate that
way, maybe :
func prepareSomething(doSomething: @delegate(Bar) () -> ()) {
    /**/
}

Pierre

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

--
 Wizard
james@supmenow.com
+44 7523 279 698

--
 Wizard
james@supmenow.com
+44 7523 279 698

--
 Wizard
james@supmenow.com
+44 7523 279 698

--
 Wizard
james@supmenow.com
+44 7523 279 698

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


(Pierre Monod-Broca) #6

To answer James:
Binding is probably a better word than delegation :), it seems more consistent.

I’m ok with the javascript syntax of binding, but the curried-like one has the advantage of being consistent with methods eg:
let f = BarImplementation.doStuff // BarImplementation -> () -> ()
f(bar)()

To answer Marc:
The bound value is not captured from the closure definition site, it’s explicitly bound, usually just before calling the closure. I think a strong reference is enough, like when calling a method. Besides the binding wouldn’t mutate the closure, but create a new one, usually short-lived.

I think `self` should keep it’s value, and we should find another word for the bound value, especially to avoid `self` being shadowed. I would suggest `this` if it wasn’t heavily used in other language to mean self.

Pierre

···

Le 11 déc. 2015 à 13:36, Marc Knaup <marc@knaup.koeln> a écrit :

How would capture semantics work?

I think this could easily lead to reference cycles as the parameter is captured strongly without any hint at the call-site.
The developer should be warned and have a way to state the capture semantics explicitly through the capture list.

If the delegate parameter "Bar" becomes "self" in the closure then using "self" everywhere should be required just as in normal closures. "@noescape" would avoid this but the closure reference must be stored then.
If the delegate parameter "Bar" really becomes "self" there's also the problem that the original "self" variable suddenly becomes hidden.

On Fri, Dec 11, 2015 at 1:15 PM, James Campbell via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Fri, Dec 11, 2015 at 12:14 PM, James Campbell <james@supmenow.com <mailto:james@supmenow.com>> wrote:
So I don't like this way of declaring it as it is not very clear what it is doing but I actually prefer the javascript way of binding, so in your example the code would be this:

func prepareSomething(setup:() -> ()) {
    let bar = BarImplementation()
    // code before
    setup().bind(bar)
    // code after
}

The bind method lets Swift know it should bind this to `bar`, so now this is the same as writing `bar`. So in your example:

without delegation
prepareSomething { delegate in
    delegate.someConfig = "Hello World"
}

with binding
prepareSomething {
    someConfig = "Hello World"
}

These would work the same :slight_smile:

On Fri, Dec 11, 2015 at 11:49 AM, Pierre Monod-Broca <pierremonodbroca@gmail.com <mailto:pierremonodbroca@gmail.com>> wrote:
without delegation
prepareSomething { delegate in
    delegate.someConfig = "Hello World"
}

with delegation
prepareSomething {
    someConfig = "Hello World"
}

The exemple is not very compelling, but in a manifest file it would reduce boiler plate.

Pierre

Le 11 déc. 2015 à 12:29, James Campbell <james@supmenow.com <mailto:james@supmenow.com>> a écrit :

Whats the advantage over this ?

protocol Bar {
    var someConfig: String { get set }
}

func prepareSomething(setup: (delegate: Bar) -> ()) {

On Fri, Dec 11, 2015 at 11:27 AM, Pierre Monod-Broca <pierremonodbroca@gmail.com <mailto:pierremonodbroca@gmail.com>> wrote:

Le 11 déc. 2015 à 11:54, James Campbell <james@supmenow.com <mailto:james@supmenow.com>> a écrit :

Could you explain a little more its a bit confusing ?

On Fri, Dec 11, 2015 at 10:32 AM, Pierre Monod-Broca via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
A groovy closure can have a delegate which replaces `this` as the default receiver. The issue in groovy is that it is not compatible with static compilation, and there is no way to know from the code what is the type of the delegate.

It works great for DSL. It would work great the Swift Package Manager manifest, among other things.

It could look like this in swift

protocol Bar {
    var someConfig: String { get set }
}

func prepareSomething(setup: @delegate Bar -> () -> ()) {

Here we define a function `prepareSomething(_:)` which receives one parameter: a closure that takes a delegate conforming to `Bar`, and otherwise take no parameter and returns nothing

    let bar = BarImplementation()
    // code before
    setup(bar)()

Here we pass a delegate to the closure, then call the closure

    // code after
}

prepareSomething { () -> () in

Here we call the function `prepareSomething(_:)` with a closure which we define a the same time

    someConfig = "Hello world"

Here `someConfig` is a property of the closure’s delegate

}

Where `someConfig` would refer to bar.

I’m not sure about the syntax, we could also declare the delegate that way, maybe :
func prepareSomething(doSomething: @delegate(Bar) () -> ()) {
    /**/
}

Pierre

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

--
 Wizard
james@supmenow.com <mailto:james@supmenow.com>
+44 7523 279 698 <tel:%2B44%207523%20279%20698>

--
 Wizard
james@supmenow.com <mailto:james@supmenow.com>
+44 7523 279 698 <tel:%2B44%207523%20279%20698>

--
 Wizard
james@supmenow.com <mailto:james@supmenow.com>
+44 7523 279 698 <tel:%2B44%207523%20279%20698>

--
 Wizard
james@supmenow.com <mailto:james@supmenow.com>
+44 7523 279 698 <tel:%2B44%207523%20279%20698>
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Matthew Johnson) #7

What if the delegate has a member with the same name as a captured value? This would create ambiguity when referring to that name in the closure. Would this just not be allowed and callers required to rename the captured value in the capture list?

Btw, this is effectively a variation on Erica Sadun's Method Cascade proposal which was filed as a bug report at bugs.swift.org. You might want to look into that if you're not already aware of it.

···

Sent from my iPad

On Dec 11, 2015, at 6:46 AM, Pierre Monod-Broca via swift-evolution <swift-evolution@swift.org> wrote:

To answer James:
Binding is probably a better word than delegation :), it seems more consistent.

I’m ok with the javascript syntax of binding, but the curried-like one has the advantage of being consistent with methods eg:
let f = BarImplementation.doStuff // BarImplementation -> () -> ()
f(bar)()

To answer Marc:
The bound value is not captured from the closure definition site, it’s explicitly bound, usually just before calling the closure. I think a strong reference is enough, like when calling a method. Besides the binding wouldn’t mutate the closure, but create a new one, usually short-lived.

I think `self` should keep it’s value, and we should find another word for the bound value, especially to avoid `self` being shadowed. I would suggest `this` if it wasn’t heavily used in other language to mean self.

Pierre

Le 11 déc. 2015 à 13:36, Marc Knaup <marc@knaup.koeln> a écrit :

How would capture semantics work?

I think this could easily lead to reference cycles as the parameter is captured strongly without any hint at the call-site.
The developer should be warned and have a way to state the capture semantics explicitly through the capture list.

If the delegate parameter "Bar" becomes "self" in the closure then using "self" everywhere should be required just as in normal closures. "@noescape" would avoid this but the closure reference must be stored then.
If the delegate parameter "Bar" really becomes "self" there's also the problem that the original "self" variable suddenly becomes hidden.

On Fri, Dec 11, 2015 at 1:15 PM, James Campbell via swift-evolution <swift-evolution@swift.org> wrote:

On Fri, Dec 11, 2015 at 12:14 PM, James Campbell <james@supmenow.com> wrote:
So I don't like this way of declaring it as it is not very clear what it is doing but I actually prefer the javascript way of binding, so in your example the code would be this:

func prepareSomething(setup:() -> ()) {
    let bar = BarImplementation()
    // code before
    setup().bind(bar)
    // code after
}

The bind method lets Swift know it should bind this to `bar`, so now this is the same as writing `bar`. So in your example:

without delegation
prepareSomething { delegate in
    delegate.someConfig = "Hello World"
}

with binding
prepareSomething {
    someConfig = "Hello World"
}

These would work the same :slight_smile:

On Fri, Dec 11, 2015 at 11:49 AM, Pierre Monod-Broca <pierremonodbroca@gmail.com> wrote:
without delegation
prepareSomething { delegate in
    delegate.someConfig = "Hello World"
}

with delegation
prepareSomething {
    someConfig = "Hello World"
}

The exemple is not very compelling, but in a manifest file it would reduce boiler plate.

Pierre

Le 11 déc. 2015 à 12:29, James Campbell <james@supmenow.com> a écrit :

Whats the advantage over this ?

protocol Bar {
    var someConfig: String { get set }
}

func prepareSomething(setup: (delegate: Bar) -> ()) {

On Fri, Dec 11, 2015 at 11:27 AM, Pierre Monod-Broca <pierremonodbroca@gmail.com> wrote:

Le 11 déc. 2015 à 11:54, James Campbell <james@supmenow.com> a écrit :

Could you explain a little more its a bit confusing ?

On Fri, Dec 11, 2015 at 10:32 AM, Pierre Monod-Broca via swift-evolution <swift-evolution@swift.org> wrote:

A groovy closure can have a delegate which replaces `this` as the default receiver. The issue in groovy is that it is not compatible with static compilation, and there is no way to know from the code what is the type of the delegate.

It works great for DSL. It would work great the Swift Package Manager manifest, among other things.

It could look like this in swift

protocol Bar {
    var someConfig: String { get set }
}

func prepareSomething(setup: @delegate Bar -> () -> ()) {

Here we define a function `prepareSomething(_:)` which receives one parameter: a closure that takes a delegate conforming to `Bar`, and otherwise take no parameter and returns nothing

    let bar = BarImplementation()
    // code before
    setup(bar)()

Here we pass a delegate to the closure, then call the closure

    // code after
}

prepareSomething { () -> () in

Here we call the function `prepareSomething(_:)` with a closure which we define a the same time

    someConfig = "Hello world"

Here `someConfig` is a property of the closure’s delegate

}

Where `someConfig` would refer to bar.

I’m not sure about the syntax, we could also declare the delegate that way, maybe :
func prepareSomething(doSomething: @delegate(Bar) () -> ()) {
    /**/
}

Pierre

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

--
 Wizard
james@supmenow.com
+44 7523 279 698

--
 Wizard
james@supmenow.com
+44 7523 279 698

--
 Wizard
james@supmenow.com
+44 7523 279 698

--
 Wizard
james@supmenow.com
+44 7523 279 698

_______________________________________________
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


(James Campbell) #8

I think in those cases the compiler would step in and force you to rename
or explicitly reference the bound object i.e "boundObject.boundValue"

···

On Fri, Dec 11, 2015 at 2:05 PM, Matthew Johnson via swift-evolution < swift-evolution@swift.org> wrote:

What if the delegate has a member with the same name as a captured value?
This would create ambiguity when referring to that name in the closure.
Would this just not be allowed and callers required to rename the captured
value in the capture list?

Btw, this is effectively a variation on Erica Sadun's Method Cascade
proposal which was filed as a bug report at bugs.swift.org. You might
want to look into that if you're not already aware of it.

Sent from my iPad

On Dec 11, 2015, at 6:46 AM, Pierre Monod-Broca via swift-evolution < > swift-evolution@swift.org> wrote:

To answer James:
Binding is probably a better word than delegation :), it seems more
consistent.

I’m ok with the javascript syntax of binding, but the curried-like one has
the advantage of being consistent with methods eg:
let f = BarImplementation.doStuff // BarImplementation -> () -> ()
f(bar)()

To answer Marc:
The bound value is not captured from the closure definition site, it’s
explicitly bound, usually just before calling the closure. I think a strong
reference is enough, like when calling a method. Besides the binding
wouldn’t mutate the closure, but create a new one, usually short-lived.

I think `self` should keep it’s value, and we should find another word for
the bound value, especially to avoid `self` being shadowed. I would suggest
`this` if it wasn’t heavily used in other language to mean self.

Pierre

Le 11 déc. 2015 à 13:36, Marc Knaup <marc@knaup.koeln> a écrit :

How would capture semantics work?

I think this could easily lead to reference cycles as the parameter is
captured strongly without any hint at the call-site.
The developer should be warned and have a way to state the capture
semantics explicitly through the capture list.

If the delegate parameter "Bar" becomes "self" in the closure then using
"self" everywhere should be required just as in normal closures.
"@noescape" would avoid this but the closure reference must be stored then.
If the delegate parameter "Bar" really becomes "self" there's also the
problem that the original "self" variable suddenly becomes hidden.

On Fri, Dec 11, 2015 at 1:15 PM, James Campbell via swift-evolution < > swift-evolution@swift.org> wrote:

On Fri, Dec 11, 2015 at 12:14 PM, James Campbell <james@supmenow.com> >> wrote:

So I don't like this way of declaring it as it is not very clear what it
is doing but I actually prefer the javascript way of binding, so in your
example the code would be this:

func prepareSomething(setup:() -> ()) {
    let bar = BarImplementation()
    // code before
    setup().bind(bar)
    // code after
}

The bind method lets Swift know it should bind this to `bar`, so now
this is the same as writing `bar`. So in your example:

without delegation
prepareSomething { delegate in
    delegate.someConfig = "Hello World"
}

with binding
prepareSomething {
    someConfig = "Hello World"
}

These would work the same :slight_smile:

On Fri, Dec 11, 2015 at 11:49 AM, Pierre Monod-Broca < >>> pierremonodbroca@gmail.com> wrote:

without delegation
prepareSomething { delegate in
    delegate.someConfig = "Hello World"
}

with delegation
prepareSomething {
    someConfig = "Hello World"
}

The exemple is not very compelling, but in a manifest file it would
reduce boiler plate.

Pierre

Le 11 déc. 2015 à 12:29, James Campbell <james@supmenow.com> a écrit :

Whats the advantage over this ?

protocol Bar {

    var someConfig: String { get set }
}

func prepareSomething(setup: (delegate: Bar) -> ()) {

On Fri, Dec 11, 2015 at 11:27 AM, Pierre Monod-Broca < >>>> pierremonodbroca@gmail.com> wrote:

Le 11 déc. 2015 à 11:54, James Campbell <james@supmenow.com> a écrit :

Could you explain a little more its a bit confusing ?

On Fri, Dec 11, 2015 at 10:32 AM, Pierre Monod-Broca via >>>>> swift-evolution <swift-evolution@swift.org> wrote:

A groovy closure can have a delegate which replaces `this` as the
default receiver. The issue in groovy is that it is not compatible with
static compilation, and there is no way to know from the code what is the
type of the delegate.

It works great for DSL. It would work great the Swift Package Manager
manifest, among other things.

It could look like this in swift

protocol Bar {
    var someConfig: String { get set }
}

func prepareSomething(setup: @delegate Bar -> () -> ()) {

Here we define a function `prepareSomething(_:)` which receives one
parameter: a closure that takes a delegate conforming to `Bar`, and
otherwise take no parameter and returns nothing

    let bar = BarImplementation()

    // code before
    setup(bar)()

Here we pass a delegate to the closure, then call the closure

    // code after

}

prepareSomething { () -> () in

Here we call the function `prepareSomething(_:)` with a closure which
we define a the same time

    someConfig = "Hello world"

Here `someConfig` is a property of the closure’s delegate

}

Where `someConfig` would refer to bar.

I’m not sure about the syntax, we could also declare the delegate that
way, maybe :
func prepareSomething(doSomething: @delegate(Bar) () -> ()) {
    /**/
}

Pierre

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

--
 Wizard
james@supmenow.com
+44 7523 279 698

--
 Wizard
james@supmenow.com
+44 7523 279 698

--
 Wizard
james@supmenow.com
+44 7523 279 698

--
 Wizard
james@supmenow.com
+44 7523 279 698

_______________________________________________
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

--
 Wizard
james@supmenow.com
+44 7523 279 698


(ilya) #9

I'm still not sure I understand the capture semantics in this proposal.
Let's say we want to do stuff inside NSOperation:

class C:NSOperation {

  func doStuff() {

    // We want to set the name of new operation to be equal the old name +
some suffix that is currently bound to 'name'.
    var name = "suffix"

    // How we do it right now, assume prepareSomething creates a new
NSOperation that we want to configure.

    prepareSomething { operation in
        operation.name = self.name + name
    }

   // How do we do that in the proposal?

    prepareSomething {
        name = ?
    }
}

···

On Fri, Dec 11, 2015 at 3:46 PM, Pierre Monod-Broca via swift-evolution < swift-evolution@swift.org> wrote:

To answer James:
Binding is probably a better word than delegation :), it seems more
consistent.

I’m ok with the javascript syntax of binding, but the curried-like one has
the advantage of being consistent with methods eg:
let f = BarImplementation.doStuff // BarImplementation -> () -> ()
f(bar)()

To answer Marc:
The bound value is not captured from the closure definition site, it’s
explicitly bound, usually just before calling the closure. I think a strong
reference is enough, like when calling a method. Besides the binding
wouldn’t mutate the closure, but create a new one, usually short-lived.

I think `self` should keep it’s value, and we should find another word for
the bound value, especially to avoid `self` being shadowed. I would suggest
`this` if it wasn’t heavily used in other language to mean self.

Pierre

Le 11 déc. 2015 à 13:36, Marc Knaup <marc@knaup.koeln> a écrit :

How would capture semantics work?

I think this could easily lead to reference cycles as the parameter is
captured strongly without any hint at the call-site.
The developer should be warned and have a way to state the capture
semantics explicitly through the capture list.

If the delegate parameter "Bar" becomes "self" in the closure then using
"self" everywhere should be required just as in normal closures.
"@noescape" would avoid this but the closure reference must be stored then.
If the delegate parameter "Bar" really becomes "self" there's also the
problem that the original "self" variable suddenly becomes hidden.

On Fri, Dec 11, 2015 at 1:15 PM, James Campbell via swift-evolution < > swift-evolution@swift.org> wrote:

On Fri, Dec 11, 2015 at 12:14 PM, James Campbell <james@supmenow.com> >> wrote:

So I don't like this way of declaring it as it is not very clear what it
is doing but I actually prefer the javascript way of binding, so in your
example the code would be this:

func prepareSomething(setup:() -> ()) {
    let bar = BarImplementation()
    // code before
    setup().bind(bar)
    // code after
}

The bind method lets Swift know it should bind this to `bar`, so now
this is the same as writing `bar`. So in your example:

without delegation
prepareSomething { delegate in
    delegate.someConfig = "Hello World"
}

with binding
prepareSomething {
    someConfig = "Hello World"
}

These would work the same :slight_smile:

On Fri, Dec 11, 2015 at 11:49 AM, Pierre Monod-Broca < >>> pierremonodbroca@gmail.com> wrote:

without delegation
prepareSomething { delegate in
    delegate.someConfig = "Hello World"
}

with delegation
prepareSomething {
    someConfig = "Hello World"
}

The exemple is not very compelling, but in a manifest file it would
reduce boiler plate.

Pierre

Le 11 déc. 2015 à 12:29, James Campbell <james@supmenow.com> a écrit :

Whats the advantage over this ?

protocol Bar {

    var someConfig: String { get set }
}

func prepareSomething(setup: (delegate: Bar) -> ()) {

On Fri, Dec 11, 2015 at 11:27 AM, Pierre Monod-Broca < >>>> pierremonodbroca@gmail.com> wrote:

Le 11 déc. 2015 à 11:54, James Campbell <james@supmenow.com> a écrit :

Could you explain a little more its a bit confusing ?

On Fri, Dec 11, 2015 at 10:32 AM, Pierre Monod-Broca via >>>>> swift-evolution <swift-evolution@swift.org> wrote:

A groovy closure can have a delegate which replaces `this` as the
default receiver. The issue in groovy is that it is not compatible with
static compilation, and there is no way to know from the code what is the
type of the delegate.

It works great for DSL. It would work great the Swift Package Manager
manifest, among other things.

It could look like this in swift

protocol Bar {
    var someConfig: String { get set }
}

func prepareSomething(setup: @delegate Bar -> () -> ()) {

Here we define a function `prepareSomething(_:)` which receives one
parameter: a closure that takes a delegate conforming to `Bar`, and
otherwise take no parameter and returns nothing

    let bar = BarImplementation()

    // code before
    setup(bar)()

Here we pass a delegate to the closure, then call the closure

    // code after

}

prepareSomething { () -> () in

Here we call the function `prepareSomething(_:)` with a closure which
we define a the same time

    someConfig = "Hello world"

Here `someConfig` is a property of the closure’s delegate

}

Where `someConfig` would refer to bar.

I’m not sure about the syntax, we could also declare the delegate that
way, maybe :
func prepareSomething(doSomething: @delegate(Bar) () -> ()) {
    /**/
}

Pierre

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

--
 Wizard
james@supmenow.com
+44 7523 279 698

--
 Wizard
james@supmenow.com
+44 7523 279 698

--
 Wizard
james@supmenow.com
+44 7523 279 698

--
 Wizard
james@supmenow.com
+44 7523 279 698

_______________________________________________
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


(Pierre Monod-Broca) #10

What if the delegate has a member with the same name as a captured value? This would create ambiguity when referring to that name in the closure. Would this just not be allowed and callers required to rename the captured value in the capture list?

I would stay consistent with current @noescape blocks, where the captured value has precedence. Then you would rename with an explicit capture list, or use a different name for your local variables. And in the cases you don’t want to do either, we'd introduce a way to explicitly call delegate’s members. Either a syntax to explicitly name it, à la capture list, or an implicit name, à la `self` (for exemple `it`).

Btw, this is effectively a variation on Erica Sadun's Method Cascade proposal which was filed as a bug report at bugs.swift.org <http://bugs.swift.org/>. You might want to look into that if you're not already aware of it.

Thanks for the note. Yes it raises the same kind of issues, and the use cases overlap. I think they complement one another pretty well.
Method Cascading is a tool for an interface consumer, Closure Delegation is more a tool for an interface provider (then used by the interface's consumers).

Just after the closure returns is a good point to make additional work, like destroying a context setup before the closure call, or taking into account members changed during the closure call. You don’t have that with Method Cascading.

···

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

Sent from my iPad

On Dec 11, 2015, at 6:46 AM, Pierre Monod-Broca via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

To answer James:
Binding is probably a better word than delegation :), it seems more consistent.

I’m ok with the javascript syntax of binding, but the curried-like one has the advantage of being consistent with methods eg:
let f = BarImplementation.doStuff // BarImplementation -> () -> ()
f(bar)()

To answer Marc:
The bound value is not captured from the closure definition site, it’s explicitly bound, usually just before calling the closure. I think a strong reference is enough, like when calling a method. Besides the binding wouldn’t mutate the closure, but create a new one, usually short-lived.

I think `self` should keep it’s value, and we should find another word for the bound value, especially to avoid `self` being shadowed. I would suggest `this` if it wasn’t heavily used in other language to mean self.

Pierre

Le 11 déc. 2015 à 13:36, Marc Knaup <marc@knaup.koeln <mailto:marc@knaup.koeln>> a écrit :

How would capture semantics work?

I think this could easily lead to reference cycles as the parameter is captured strongly without any hint at the call-site.
The developer should be warned and have a way to state the capture semantics explicitly through the capture list.

If the delegate parameter "Bar" becomes "self" in the closure then using "self" everywhere should be required just as in normal closures. "@noescape" would avoid this but the closure reference must be stored then.
If the delegate parameter "Bar" really becomes "self" there's also the problem that the original "self" variable suddenly becomes hidden.

On Fri, Dec 11, 2015 at 1:15 PM, James Campbell via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Fri, Dec 11, 2015 at 12:14 PM, James Campbell <james@supmenow.com <mailto:james@supmenow.com>> wrote:
So I don't like this way of declaring it as it is not very clear what it is doing but I actually prefer the javascript way of binding, so in your example the code would be this:

func prepareSomething(setup:() -> ()) {
    let bar = BarImplementation()
    // code before
    setup().bind(bar)
    // code after
}

The bind method lets Swift know it should bind this to `bar`, so now this is the same as writing `bar`. So in your example:

without delegation
prepareSomething { delegate in
    delegate.someConfig = "Hello World"
}

with binding
prepareSomething {
    someConfig = "Hello World"
}

These would work the same :slight_smile:

On Fri, Dec 11, 2015 at 11:49 AM, Pierre Monod-Broca <pierremonodbroca@gmail.com <mailto:pierremonodbroca@gmail.com>> wrote:
without delegation
prepareSomething { delegate in
    delegate.someConfig = "Hello World"
}

with delegation
prepareSomething {
    someConfig = "Hello World"
}

The exemple is not very compelling, but in a manifest file it would reduce boiler plate.

Pierre

Le 11 déc. 2015 à 12:29, James Campbell <james@supmenow.com <mailto:james@supmenow.com>> a écrit :

Whats the advantage over this ?

protocol Bar {
    var someConfig: String { get set }
}

func prepareSomething(setup: (delegate: Bar) -> ()) {

On Fri, Dec 11, 2015 at 11:27 AM, Pierre Monod-Broca <pierremonodbroca@gmail.com <mailto:pierremonodbroca@gmail.com>> wrote:

Le 11 déc. 2015 à 11:54, James Campbell <james@supmenow.com <mailto:james@supmenow.com>> a écrit :

Could you explain a little more its a bit confusing ?

On Fri, Dec 11, 2015 at 10:32 AM, Pierre Monod-Broca via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
A groovy closure can have a delegate which replaces `this` as the default receiver. The issue in groovy is that it is not compatible with static compilation, and there is no way to know from the code what is the type of the delegate.

It works great for DSL. It would work great the Swift Package Manager manifest, among other things.

It could look like this in swift

protocol Bar {
    var someConfig: String { get set }
}

func prepareSomething(setup: @delegate Bar -> () -> ()) {

Here we define a function `prepareSomething(_:)` which receives one parameter: a closure that takes a delegate conforming to `Bar`, and otherwise take no parameter and returns nothing

    let bar = BarImplementation()
    // code before
    setup(bar)()

Here we pass a delegate to the closure, then call the closure

    // code after
}

prepareSomething { () -> () in

Here we call the function `prepareSomething(_:)` with a closure which we define a the same time

    someConfig = "Hello world"

Here `someConfig` is a property of the closure’s delegate

}

Where `someConfig` would refer to bar.

I’m not sure about the syntax, we could also declare the delegate that way, maybe :
func prepareSomething(doSomething: @delegate(Bar) () -> ()) {
    /**/
}

Pierre

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

--
 Wizard
james@supmenow.com <mailto:james@supmenow.com>
+44 7523 279 698 <tel:%2B44%207523%20279%20698>

--
 Wizard
james@supmenow.com <mailto:james@supmenow.com>
+44 7523 279 698 <tel:%2B44%207523%20279%20698>

--
 Wizard
james@supmenow.com <mailto:james@supmenow.com>
+44 7523 279 698 <tel:%2B44%207523%20279%20698>

--
 Wizard
james@supmenow.com <mailto:james@supmenow.com>
+44 7523 279 698 <tel:%2B44%207523%20279%20698>
_______________________________________________
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


(Pierre Monod-Broca) #11

I see three solutions which are not exclusive.

var suffix = "suffix"
prepareSomething {
    name = self.name + suffix
}

var name = "suffix"
prepareSomething { [suffix = name]
    name = self.name + suffix
}

var name = "suffix"
prepareSomething {
    it.name = self.name + name
}

or a fourth one where you'd explicitly name `it`, but I’m not sure of the syntax.

Pierre

···

Le 11 déc. 2015 à 18:10, ilya <ilya.nikokoshev@gmail.com> a écrit :

I'm still not sure I understand the capture semantics in this proposal. Let's say we want to do stuff inside NSOperation:

class C:NSOperation {

  func doStuff() {

    // We want to set the name of new operation to be equal the old name + some suffix that is currently bound to 'name'.
    var name = "suffix"

    // How we do it right now, assume prepareSomething creates a new NSOperation that we want to configure.

    prepareSomething { operation in
        operation.name <http://operation.name/> = self.name <http://self.name/> + name
    }

   // How do we do that in the proposal?

    prepareSomething {
        name = ?
    }
}

On Fri, Dec 11, 2015 at 3:46 PM, Pierre Monod-Broca via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
To answer James:
Binding is probably a better word than delegation :), it seems more consistent.

I’m ok with the javascript syntax of binding, but the curried-like one has the advantage of being consistent with methods eg:
let f = BarImplementation.doStuff // BarImplementation -> () -> ()
f(bar)()

To answer Marc:
The bound value is not captured from the closure definition site, it’s explicitly bound, usually just before calling the closure. I think a strong reference is enough, like when calling a method. Besides the binding wouldn’t mutate the closure, but create a new one, usually short-lived.

I think `self` should keep it’s value, and we should find another word for the bound value, especially to avoid `self` being shadowed. I would suggest `this` if it wasn’t heavily used in other language to mean self.

Pierre

Le 11 déc. 2015 à 13:36, Marc Knaup <marc@knaup.koeln <mailto:marc@knaup.koeln>> a écrit :

How would capture semantics work?

I think this could easily lead to reference cycles as the parameter is captured strongly without any hint at the call-site.
The developer should be warned and have a way to state the capture semantics explicitly through the capture list.

If the delegate parameter "Bar" becomes "self" in the closure then using "self" everywhere should be required just as in normal closures. "@noescape" would avoid this but the closure reference must be stored then.
If the delegate parameter "Bar" really becomes "self" there's also the problem that the original "self" variable suddenly becomes hidden.

On Fri, Dec 11, 2015 at 1:15 PM, James Campbell via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Fri, Dec 11, 2015 at 12:14 PM, James Campbell <james@supmenow.com <mailto:james@supmenow.com>> wrote:
So I don't like this way of declaring it as it is not very clear what it is doing but I actually prefer the javascript way of binding, so in your example the code would be this:

func prepareSomething(setup:() -> ()) {
    let bar = BarImplementation()
    // code before
    setup().bind(bar)
    // code after
}

The bind method lets Swift know it should bind this to `bar`, so now this is the same as writing `bar`. So in your example:

without delegation
prepareSomething { delegate in
    delegate.someConfig = "Hello World"
}

with binding
prepareSomething {
    someConfig = "Hello World"
}

These would work the same :slight_smile:

On Fri, Dec 11, 2015 at 11:49 AM, Pierre Monod-Broca <pierremonodbroca@gmail.com <mailto:pierremonodbroca@gmail.com>> wrote:
without delegation
prepareSomething { delegate in
    delegate.someConfig = "Hello World"
}

with delegation
prepareSomething {
    someConfig = "Hello World"
}

The exemple is not very compelling, but in a manifest file it would reduce boiler plate.

Pierre

Le 11 déc. 2015 à 12:29, James Campbell <james@supmenow.com <mailto:james@supmenow.com>> a écrit :

Whats the advantage over this ?

protocol Bar {
    var someConfig: String { get set }
}

func prepareSomething(setup: (delegate: Bar) -> ()) {

On Fri, Dec 11, 2015 at 11:27 AM, Pierre Monod-Broca <pierremonodbroca@gmail.com <mailto:pierremonodbroca@gmail.com>> wrote:

Le 11 déc. 2015 à 11:54, James Campbell <james@supmenow.com <mailto:james@supmenow.com>> a écrit :

Could you explain a little more its a bit confusing ?

On Fri, Dec 11, 2015 at 10:32 AM, Pierre Monod-Broca via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
A groovy closure can have a delegate which replaces `this` as the default receiver. The issue in groovy is that it is not compatible with static compilation, and there is no way to know from the code what is the type of the delegate.

It works great for DSL. It would work great the Swift Package Manager manifest, among other things.

It could look like this in swift

protocol Bar {
    var someConfig: String { get set }
}

func prepareSomething(setup: @delegate Bar -> () -> ()) {

Here we define a function `prepareSomething(_:)` which receives one parameter: a closure that takes a delegate conforming to `Bar`, and otherwise take no parameter and returns nothing

    let bar = BarImplementation()
    // code before
    setup(bar)()

Here we pass a delegate to the closure, then call the closure

    // code after
}

prepareSomething { () -> () in

Here we call the function `prepareSomething(_:)` with a closure which we define a the same time

    someConfig = "Hello world"

Here `someConfig` is a property of the closure’s delegate

}

Where `someConfig` would refer to bar.

I’m not sure about the syntax, we could also declare the delegate that way, maybe :
func prepareSomething(doSomething: @delegate(Bar) () -> ()) {
    /**/
}

Pierre

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

--
 Wizard
james@supmenow.com <mailto:james@supmenow.com>
+44 7523 279 698 <tel:%2B44%207523%20279%20698>

--
 Wizard
james@supmenow.com <mailto:james@supmenow.com>
+44 7523 279 698 <tel:%2B44%207523%20279%20698>

--
 Wizard
james@supmenow.com <mailto:james@supmenow.com>
+44 7523 279 698 <tel:%2B44%207523%20279%20698>

--
 Wizard
james@supmenow.com <mailto:james@supmenow.com>
+44 7523 279 698 <tel:%2B44%207523%20279%20698>
_______________________________________________
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