Should we relax restriction on closing over outer scope in class declarations?


(Micah Hainline) #1

Currently we allow declarations of a new class in local scope, but
nothing can be referenced from the outer scope. Thus this is illegal:

func foo() {
   let widgetString: String = createWidgetString()
   class SimpleProvider: WidgetStringProvider {
      func provideWidgetString() -> String {
         return widgetString // Illegal, defined in outer scope
      }
   }
   doThingsWithWidget(provider: WidgetStringProvider())
}

I'm trying to feel out the edges of this decision, and figure out why
exactly this isn't allowed now, and how we might want to relax this in
the future if possible. While not a common construct, it is a useful
one.

Obviously there are ways around it, very simple to create an init and
a private let and do something like this:

func foo() {
   let widgetString: String = createWidgetString()
   class SimpleProvider: WidgetStringProvider {
      private let widgetString: String

      init(widgetString: String) {
         self.widgetString = widgetString
      }

      func provideWidgetString() -> String {
         return widgetString // now legal, references
SimpleProvider.widgetString
      }
   }
   doThingsWithWidget(provider: WidgetStringProvider(widgetString:
widgetString))
}

That's boilerplate I don't want to write though, as it somewhat
detracts from the readability of the structure. I'm not super
interested in defending the concept of local class definitions itself,
it's already allowed in the language, I'm just interested in the
syntax limitation and where that line might be able to be redrawn.


(Michael Ilseman) #2

Are you asking for a class declaration to implicitly capture and extend the lifetime of local variables? That seems like something that’s better done explicitly. Perhaps it’s better to think about how to reduce the boiler plate code, e.g. better default initializers.

(this is of course, additive and beyond the current scope of Swift 4 phase 1 planning)

···

On Dec 22, 2016, at 2:39 PM, Micah Hainline via swift-evolution <swift-evolution@swift.org> wrote:

Currently we allow declarations of a new class in local scope, but
nothing can be referenced from the outer scope. Thus this is illegal:

func foo() {
  let widgetString: String = createWidgetString()
  class SimpleProvider: WidgetStringProvider {
     func provideWidgetString() -> String {
        return widgetString // Illegal, defined in outer scope
     }
  }
  doThingsWithWidget(provider: WidgetStringProvider())
}

I'm trying to feel out the edges of this decision, and figure out why
exactly this isn't allowed now, and how we might want to relax this in
the future if possible. While not a common construct, it is a useful
one.

Obviously there are ways around it, very simple to create an init and
a private let and do something like this:

func foo() {
  let widgetString: String = createWidgetString()
  class SimpleProvider: WidgetStringProvider {
     private let widgetString: String

     init(widgetString: String) {
        self.widgetString = widgetString
     }

     func provideWidgetString() -> String {
        return widgetString // now legal, references
SimpleProvider.widgetString
     }
  }
  doThingsWithWidget(provider: WidgetStringProvider(widgetString:
widgetString))
}

That's boilerplate I don't want to write though, as it somewhat
detracts from the readability of the structure. I'm not super
interested in defending the concept of local class definitions itself,
it's already allowed in the language, I'm just interested in the
syntax limitation and where that line might be able to be redrawn.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Micah Hainline) #3

That's exactly what I'm suggesting, the class declaration could work similarly to a closure.

···

On Dec 22, 2016, at 4:15 PM, Michael Ilseman <milseman@apple.com> wrote:

Are you asking for a class declaration to implicitly capture and extend the lifetime of local variables? That seems like something that’s better done explicitly. Perhaps it’s better to think about how to reduce the boiler plate code, e.g. better default initializers.

(this is of course, additive and beyond the current scope of Swift 4 phase 1 planning)

On Dec 22, 2016, at 2:39 PM, Micah Hainline via swift-evolution <swift-evolution@swift.org> wrote:

Currently we allow declarations of a new class in local scope, but
nothing can be referenced from the outer scope. Thus this is illegal:

func foo() {
 let widgetString: String = createWidgetString()
 class SimpleProvider: WidgetStringProvider {
    func provideWidgetString() -> String {
       return widgetString // Illegal, defined in outer scope
    }
 }
 doThingsWithWidget(provider: WidgetStringProvider())
}

I'm trying to feel out the edges of this decision, and figure out why
exactly this isn't allowed now, and how we might want to relax this in
the future if possible. While not a common construct, it is a useful
one.

Obviously there are ways around it, very simple to create an init and
a private let and do something like this:

func foo() {
 let widgetString: String = createWidgetString()
 class SimpleProvider: WidgetStringProvider {
    private let widgetString: String

    init(widgetString: String) {
       self.widgetString = widgetString
    }

    func provideWidgetString() -> String {
       return widgetString // now legal, references
SimpleProvider.widgetString
    }
 }
 doThingsWithWidget(provider: WidgetStringProvider(widgetString:
widgetString))
}

That's boilerplate I don't want to write though, as it somewhat
detracts from the readability of the structure. I'm not super
interested in defending the concept of local class definitions itself,
it's already allowed in the language, I'm just interested in the
syntax limitation and where that line might be able to be redrawn.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Marinus van der Lugt) #4

Currently we allow declarations of a new class in local scope, but
nothing can be referenced from the outer scope. Thus this is illegal:

func foo() {
  let widgetString: String = createWidgetString()
  class SimpleProvider: WidgetStringProvider {
     func provideWidgetString() -> String {
        return widgetString // Illegal, defined in outer scope
     }
  }
  doThingsWithWidget(provider: WidgetStringProvider())
}

I'm trying to feel out the edges of this decision, and figure out why
exactly this isn't allowed now, and how we might want to relax this in

IMO that questions is backward. The scoping rules being what they are ensure that designers do this exactly because of the scoping rules.

Thus the answer is: Because the designer of the code wanted that restriction.

···

On 22 Dec 2016, at 22:39, Micah Hainline via swift-evolution <swift-evolution@swift.org> wrote:

the future if possible. While not a common construct, it is a useful
one.

Obviously there are ways around it, very simple to create an init and
a private let and do something like this:

func foo() {
  let widgetString: String = createWidgetString()
  class SimpleProvider: WidgetStringProvider {
     private let widgetString: String

     init(widgetString: String) {
        self.widgetString = widgetString
     }

     func provideWidgetString() -> String {
        return widgetString // now legal, references
SimpleProvider.widgetString
     }
  }
  doThingsWithWidget(provider: WidgetStringProvider(widgetString:
widgetString))
}

That's boilerplate I don't want to write though, as it somewhat
detracts from the readability of the structure. I'm not super
interested in defending the concept of local class definitions itself,
it's already allowed in the language, I'm just interested in the
syntax limitation and where that line might be able to be redrawn.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Xiaodi Wu) #5

I have to agree with Michael; it seems dangerous to allow implicit capture
by classes. A primary purpose (telos?) of closures is to provide this
functionality, which is actually quite an advanced concept. One knows to
think carefully about this when encountering a closure expression. A
primary purpose of classes is to provide for encapsulation of code.
Accidentally extending the lifetime of a local variable in a containing
scope would be hard to notice and highly unexpected functionality. Better
not to mix these things.

···

On Thu, Dec 22, 2016 at 17:54 Micah Hainline via swift-evolution < swift-evolution@swift.org> wrote:

That's exactly what I'm suggesting, the class declaration could work
similarly to a closure.

> On Dec 22, 2016, at 4:15 PM, Michael Ilseman <milseman@apple.com> wrote:
>
> Are you asking for a class declaration to implicitly capture and extend
the lifetime of local variables? That seems like something that’s better
done explicitly. Perhaps it’s better to think about how to reduce the
boiler plate code, e.g. better default initializers.
>
> (this is of course, additive and beyond the current scope of Swift 4
phase 1 planning)
>
>> On Dec 22, 2016, at 2:39 PM, Micah Hainline via swift-evolution < > swift-evolution@swift.org> wrote:
>>
>> Currently we allow declarations of a new class in local scope, but
>> nothing can be referenced from the outer scope. Thus this is illegal:
>>
>> ```
>> func foo() {
>> let widgetString: String = createWidgetString()
>> class SimpleProvider: WidgetStringProvider {
>> func provideWidgetString() -> String {
>> return widgetString // Illegal, defined in outer scope
>> }
>> }
>> doThingsWithWidget(provider: WidgetStringProvider())
>> }
>> ```
>>
>> I'm trying to feel out the edges of this decision, and figure out why
>> exactly this isn't allowed now, and how we might want to relax this in
>> the future if possible. While not a common construct, it is a useful
>> one.
>>
>> Obviously there are ways around it, very simple to create an init and
>> a private let and do something like this:
>>
>> ```
>> func foo() {
>> let widgetString: String = createWidgetString()
>> class SimpleProvider: WidgetStringProvider {
>> private let widgetString: String
>>
>> init(widgetString: String) {
>> self.widgetString = widgetString
>> }
>>
>> func provideWidgetString() -> String {
>> return widgetString // now legal, references
>> SimpleProvider.widgetString
>> }
>> }
>> doThingsWithWidget(provider: WidgetStringProvider(widgetString:
>> widgetString))
>> }
>> ```
>>
>> That's boilerplate I don't want to write though, as it somewhat
>> detracts from the readability of the structure. I'm not super
>> interested in defending the concept of local class definitions itself,
>> it's already allowed in the language, I'm just interested in the
>> syntax limitation and where that line might be able to be redrawn.
>> _______________________________________________
>> 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


(Callionica (Swift)) #6

Assuming capture were allowed, people defining classes within functions who
didn't want them to capture could position the class definition prior to
any other code in the function so that there would be nothing to capture.

···

On Thu, Dec 22, 2016 at 4:13 PM Xiaodi Wu via swift-evolution < swift-evolution@swift.org> wrote:

I have to agree with Michael; it seems dangerous to allow implicit capture
by classes. A primary purpose (telos?) of closures is to provide this
functionality, which is actually quite an advanced concept. One knows to
think carefully about this when encountering a closure expression. A
primary purpose of classes is to provide for encapsulation of code.
Accidentally extending the lifetime of a local variable in a containing
scope would be hard to notice and highly unexpected functionality. Better
not to mix these things.

On Thu, Dec 22, 2016 at 17:54 Micah Hainline via swift-evolution < > swift-evolution@swift.org> wrote:

That's exactly what I'm suggesting, the class declaration could work
similarly to a closure.

> On Dec 22, 2016, at 4:15 PM, Michael Ilseman <milseman@apple.com> wrote:

>

> Are you asking for a class declaration to implicitly capture and extend
the lifetime of local variables? That seems like something that’s better
done explicitly. Perhaps it’s better to think about how to reduce the
boiler plate code, e.g. better default initializers.

>

> (this is of course, additive and beyond the current scope of Swift 4
phase 1 planning)

>

>> On Dec 22, 2016, at 2:39 PM, Micah Hainline via swift-evolution < > swift-evolution@swift.org> wrote:

>>

>> Currently we allow declarations of a new class in local scope, but

>> nothing can be referenced from the outer scope. Thus this is illegal:

>>

>> ```

>> func foo() {

>> let widgetString: String = createWidgetString()

>> class SimpleProvider: WidgetStringProvider {

>> func provideWidgetString() -> String {

>> return widgetString // Illegal, defined in outer scope

>> }

>> }

>> doThingsWithWidget(provider: WidgetStringProvider())

>> }

>> ```

>>

>> I'm trying to feel out the edges of this decision, and figure out why

>> exactly this isn't allowed now, and how we might want to relax this in

>> the future if possible. While not a common construct, it is a useful

>> one.

>>

>> Obviously there are ways around it, very simple to create an init and

>> a private let and do something like this:

>>

>> ```

>> func foo() {

>> let widgetString: String = createWidgetString()

>> class SimpleProvider: WidgetStringProvider {

>> private let widgetString: String

>>

>> init(widgetString: String) {

>> self.widgetString = widgetString

>> }

>>

>> func provideWidgetString() -> String {

>> return widgetString // now legal, references

>> SimpleProvider.widgetString

>> }

>> }

>> doThingsWithWidget(provider: WidgetStringProvider(widgetString:

>> widgetString))

>> }

>> ```

>>

>> That's boilerplate I don't want to write though, as it somewhat

>> detracts from the readability of the structure. I'm not super

>> interested in defending the concept of local class definitions itself,

>> it's already allowed in the language, I'm just interested in the

>> syntax limitation and where that line might be able to be redrawn.

>> _______________________________________________

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


(Xiaodi Wu) #7

Only if you're also assuming that people defining classes within functions
would know about capturing. This violates the principle of progressive
disclosure, since people naturally learn about functions and classes before
they learn about closures and capturing.

···

On Fri, Dec 23, 2016 at 01:51 Callionica (Swift) < swift-callionica@callionica.com> wrote:

Assuming capture were allowed, people defining classes within functions
who didn't want them to capture could position the class definition prior
to any other code in the function so that there would be nothing to
capture.

On Thu, Dec 22, 2016 at 4:13 PM Xiaodi Wu via swift-evolution < > swift-evolution@swift.org> wrote:

I have to agree with Michael; it seems dangerous to allow implicit capture
by classes. A primary purpose (telos?) of closures is to provide this
functionality, which is actually quite an advanced concept. One knows to
think carefully about this when encountering a closure expression. A
primary purpose of classes is to provide for encapsulation of code.
Accidentally extending the lifetime of a local variable in a containing
scope would be hard to notice and highly unexpected functionality. Better
not to mix these things.

On Thu, Dec 22, 2016 at 17:54 Micah Hainline via swift-evolution < > swift-evolution@swift.org> wrote:

That's exactly what I'm suggesting, the class declaration could work
similarly to a closure.

> On Dec 22, 2016, at 4:15 PM, Michael Ilseman <milseman@apple.com> wrote:

>

> Are you asking for a class declaration to implicitly capture and extend
the lifetime of local variables? That seems like something that’s better
done explicitly. Perhaps it’s better to think about how to reduce the
boiler plate code, e.g. better default initializers.

>

> (this is of course, additive and beyond the current scope of Swift 4
phase 1 planning)

>

>> On Dec 22, 2016, at 2:39 PM, Micah Hainline via swift-evolution < > swift-evolution@swift.org> wrote:

>>

>> Currently we allow declarations of a new class in local scope, but

>> nothing can be referenced from the outer scope. Thus this is illegal:

>>

>> ```

>> func foo() {

>> let widgetString: String = createWidgetString()

>> class SimpleProvider: WidgetStringProvider {

>> func provideWidgetString() -> String {

>> return widgetString // Illegal, defined in outer scope

>> }

>> }

>> doThingsWithWidget(provider: WidgetStringProvider())

>> }

>> ```

>>

>> I'm trying to feel out the edges of this decision, and figure out why

>> exactly this isn't allowed now, and how we might want to relax this in

>> the future if possible. While not a common construct, it is a useful

>> one.

>>

>> Obviously there are ways around it, very simple to create an init and

>> a private let and do something like this:

>>

>> ```

>> func foo() {

>> let widgetString: String = createWidgetString()

>> class SimpleProvider: WidgetStringProvider {

>> private let widgetString: String

>>

>> init(widgetString: String) {

>> self.widgetString = widgetString

>> }

>>

>> func provideWidgetString() -> String {

>> return widgetString // now legal, references

>> SimpleProvider.widgetString

>> }

>> }

>> doThingsWithWidget(provider: WidgetStringProvider(widgetString:

>> widgetString))

>> }

>> ```

>>

>> That's boilerplate I don't want to write though, as it somewhat

>> detracts from the readability of the structure. I'm not super

>> interested in defending the concept of local class definitions itself,

>> it's already allowed in the language, I'm just interested in the

>> syntax limitation and where that line might be able to be redrawn.

>> _______________________________________________

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


(Callionica (Swift)) #8

I'm certainly assuming that users have a basic understanding of scope
(without that, what would be the intention behind defining the class within
the function?).

I'm not clear on what you see as the downside of letting classes capture
locals for users that are unaware of capturing.

···

On Thu, Dec 22, 2016 at 11:26 PM Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Only if you're also assuming that people defining classes within functions
would know about capturing. This violates the principle of progressive
disclosure, since people naturally learn about functions and classes before
they learn about closures and capturing.

On Fri, Dec 23, 2016 at 01:51 Callionica (Swift) < > swift-callionica@callionica.com> wrote:

Assuming capture were allowed, people defining classes within functions
who didn't want them to capture could position the class definition prior
to any other code in the function so that there would be nothing to
capture.

On Thu, Dec 22, 2016 at 4:13 PM Xiaodi Wu via swift-evolution < > swift-evolution@swift.org> wrote:

I have to agree with Michael; it seems dangerous to allow implicit capture
by classes. A primary purpose (telos?) of closures is to provide this
functionality, which is actually quite an advanced concept. One knows to
think carefully about this when encountering a closure expression. A
primary purpose of classes is to provide for encapsulation of code.
Accidentally extending the lifetime of a local variable in a containing
scope would be hard to notice and highly unexpected functionality. Better
not to mix these things.

On Thu, Dec 22, 2016 at 17:54 Micah Hainline via swift-evolution < > swift-evolution@swift.org> wrote:

That's exactly what I'm suggesting, the class declaration could work
similarly to a closure.

> On Dec 22, 2016, at 4:15 PM, Michael Ilseman <milseman@apple.com> wrote:

>

> Are you asking for a class declaration to implicitly capture and extend
the lifetime of local variables? That seems like something that’s better
done explicitly. Perhaps it’s better to think about how to reduce the
boiler plate code, e.g. better default initializers.

>

> (this is of course, additive and beyond the current scope of Swift 4
phase 1 planning)

>

>> On Dec 22, 2016, at 2:39 PM, Micah Hainline via swift-evolution < > swift-evolution@swift.org> wrote:

>>

>> Currently we allow declarations of a new class in local scope, but

>> nothing can be referenced from the outer scope. Thus this is illegal:

>>

>> ```

>> func foo() {

>> let widgetString: String = createWidgetString()

>> class SimpleProvider: WidgetStringProvider {

>> func provideWidgetString() -> String {

>> return widgetString // Illegal, defined in outer scope

>> }

>> }

>> doThingsWithWidget(provider: WidgetStringProvider())

>> }

>> ```

>>

>> I'm trying to feel out the edges of this decision, and figure out why

>> exactly this isn't allowed now, and how we might want to relax this in

>> the future if possible. While not a common construct, it is a useful

>> one.

>>

>> Obviously there are ways around it, very simple to create an init and

>> a private let and do something like this:

>>

>> ```

>> func foo() {

>> let widgetString: String = createWidgetString()

>> class SimpleProvider: WidgetStringProvider {

>> private let widgetString: String

>>

>> init(widgetString: String) {

>> self.widgetString = widgetString

>> }

>>

>> func provideWidgetString() -> String {

>> return widgetString // now legal, references

>> SimpleProvider.widgetString

>> }

>> }

>> doThingsWithWidget(provider: WidgetStringProvider(widgetString:

>> widgetString))

>> }

>> ```

>>

>> That's boilerplate I don't want to write though, as it somewhat

>> detracts from the readability of the structure. I'm not super

>> interested in defending the concept of local class definitions itself,

>> it's already allowed in the language, I'm just interested in the

>> syntax limitation and where that line might be able to be redrawn.

>> _______________________________________________

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


(Robert Widmann) #9

I'm certainly assuming that users have a basic understanding of scope (without that, what would be the intention behind defining the class within the function?).

Despite its simplicity to you (given the example you’ve cited) this feature is quite an advanced extension to the current scoping rules.

I'm not clear on what you see as the downside of letting classes capture locals for users that are unaware of capturing.

I’ll be blunt: This opens a gaping hole in the semantics of variable lifetimes and potentially introduces an ambiguity into resolution of the `self` keyword. For the former, consider a light extension to your original example that introduces a nested class:

class Outer {
  let widgetString: String = createWidgetString()

  // Initializer suppressed
  func foo() -> WidgetStringProvider {
    class SimpleProvider: WidgetStringProvider {
      // Initializer suppressed
      func provideWidgetString() -> String {
        return widgetString
      }
    }
    return SimpleProvider()
  }
}

Now, from SIL’s point of view, widgetString must escape the scope it’s in and its lifetime should be extended beyond this block. Fine. But what about Outer? Because we’re returning a reference to SimpleProvider which escapes the scope of foo(), and instance members of a SimpleProvider capture outer context, this means we also have to keep Outer alive and around for destruction (perhaps at the point where SimpleProvider is destroyed?)

For the latter consider trying to explicitly reference widgetString here. In provideWidgetString() we close over self in both foo() and provideWidgetString(). So, if I wish to make reference to Outer’s self explicitly, how would I go about doing it? Given that today a nested aggregate can have members with the same identifiers as their parent, how do we know if we’re capturing them or not when they’re referenced in the inner aggregate?

~Robert Widmann

···

On Dec 23, 2016, at 1:10 AM, Callionica (Swift) via swift-evolution <swift-evolution@swift.org> wrote:

On Thu, Dec 22, 2016 at 11:26 PM Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:
Only if you're also assuming that people defining classes within functions would know about capturing. This violates the principle of progressive disclosure, since people naturally learn about functions and classes before they learn about closures and capturing.

On Fri, Dec 23, 2016 at 01:51 Callionica (Swift) <swift-callionica@callionica.com <mailto:swift-callionica@callionica.com>> wrote:
Assuming capture were allowed, people defining classes within functions who didn't want them to capture could position the class definition prior to any other code in the function so that there would be nothing to capture.

On Thu, Dec 22, 2016 at 4:13 PM Xiaodi Wu via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
I have to agree with Michael; it seems dangerous to allow implicit capture by classes. A primary purpose (telos?) of closures is to provide this functionality, which is actually quite an advanced concept. One knows to think carefully about this when encountering a closure expression. A primary purpose of classes is to provide for encapsulation of code. Accidentally extending the lifetime of a local variable in a containing scope would be hard to notice and highly unexpected functionality. Better not to mix these things.

On Thu, Dec 22, 2016 at 17:54 Micah Hainline via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
That's exactly what I'm suggesting, the class declaration could work similarly to a closure.

> On Dec 22, 2016, at 4:15 PM, Michael Ilseman <milseman@apple.com <mailto:milseman@apple.com>> wrote:

>

> Are you asking for a class declaration to implicitly capture and extend the lifetime of local variables? That seems like something that’s better done explicitly. Perhaps it’s better to think about how to reduce the boiler plate code, e.g. better default initializers.

>

> (this is of course, additive and beyond the current scope of Swift 4 phase 1 planning)

>

>> On Dec 22, 2016, at 2:39 PM, Micah Hainline via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

>>

>> Currently we allow declarations of a new class in local scope, but

>> nothing can be referenced from the outer scope. Thus this is illegal:

>>

>> ```

>> func foo() {

>> let widgetString: String = createWidgetString()

>> class SimpleProvider: WidgetStringProvider {

>> func provideWidgetString() -> String {

>> return widgetString // Illegal, defined in outer scope

>> }

>> }

>> doThingsWithWidget(provider: WidgetStringProvider())

>> }

>> ```

>>

>> I'm trying to feel out the edges of this decision, and figure out why

>> exactly this isn't allowed now, and how we might want to relax this in

>> the future if possible. While not a common construct, it is a useful

>> one.

>>

>> Obviously there are ways around it, very simple to create an init and

>> a private let and do something like this:

>>

>> ```

>> func foo() {

>> let widgetString: String = createWidgetString()

>> class SimpleProvider: WidgetStringProvider {

>> private let widgetString: String

>>

>> init(widgetString: String) {

>> self.widgetString = widgetString

>> }

>>

>> func provideWidgetString() -> String {

>> return widgetString // now legal, references

>> SimpleProvider.widgetString

>> }

>> }

>> doThingsWithWidget(provider: WidgetStringProvider(widgetString:

>> widgetString))

>> }

>> ```

>>

>> That's boilerplate I don't want to write though, as it somewhat

>> detracts from the readability of the structure. I'm not super

>> interested in defending the concept of local class definitions itself,

>> it's already allowed in the language, I'm just interested in the

>> syntax limitation and where that line might be able to be redrawn.

>> _______________________________________________

>> swift-evolution mailing list

>> swift-evolution@swift.org <mailto:swift-evolution@swift.org>

>> https://lists.swift.org/mailman/listinfo/swift-evolution

>

_______________________________________________

swift-evolution mailing list

swift-evolution@swift.org <mailto:swift-evolution@swift.org>

https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________

swift-evolution mailing list

swift-evolution@swift.org <mailto:swift-evolution@swift.org>

https://lists.swift.org/mailman/listinfo/swift-evolution

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


(Xiaodi Wu) #10

I'm certainly assuming that users have a basic understanding of scope
(without that, what would be the intention behind defining the class within
the function?).

The notion of closing over variables far exceeds what one might reasonably
call "a basic understanding of scope." I doubt I'm alone on this opinion,
as _Advanced Swift_ (an excellent text written for users already familiar
with the basics) reviews closures and closure expressions from the ground
up; by contrast, it does not review functions, classes, or even (if I
recall correctly) defining classes inside functions.

I'm not clear on what you see as the downside of letting classes capture

locals for users that are unaware of capturing.

It violates the notion of progressive disclosure, which has been an
explicit design goal for Swift. In one of the documents on GitHub, the core
team writes: "One goal of swift is to provide a very 'progressive
disclosure' model of writing code and learning how to write code.
Therefore, it is desirable that someone be able to start out with
`print("hello world\n")` as their first application." Chris Lattner
recently reiterated this idea in a presentation he gave at IBM about the
Swift language.

The idea of progressive disclosure is that it should be possible to teach
concepts a learner must understand first without exposing them to more
advanced concepts they must pay attention to but cannot understand. Let's
look at some examples:

- In Java, one cannot write "Hello World" without being exposed to `public
static void main`. However, one cannot understand what it means to be a
public or static method until _after_ one has mastered printing "Hello
World". This violates progressive disclosure.
- In Swift, one's first "Hello World" is a one-liner that introduces the
print function and nothing else. This is progressive disclosure, and as
quoted above, Swift has been explicitly designed that way.

- In Swift, `struct Point { let x, y: Int }` is a fully usable type. With
that single line, you can now make a new `Point` by writing `let point =
Point(x: 42, y: 42)`.
- To do a similar thing in some other languages, you'd have to write a
constructor or initializer. However, one cannot really understand what
initialization is without first having initialized something, but one
cannot initialize something unless there is an initializer. This violates
progressive disclosure. In Swift, an initializer is synthesized for you;
this promotes progressive disclosure.
- There have been proposals to change the default access level to private.
If `x` and `y` were private, then `Point` would be unusable without access
modifiers for its members. However, one must first understand how structs
and other types encapsulate code before one can understand how access
modifiers work, but one would not be able to create useful structs or other
types without first using access modifiers. This would violate progressive
disclosure. In Swift, the default access level is internal; this promotes
progressive disclosure.

- In Swift, _closure expressions_ don't have to capture anything (i.e.,
they don't have to be true _closures_). But they are introduced as closure
expressions rather than, say, function expressions, and they are taught in
the same breath as the concept of capturing variables. This ensures that
every person who reaches for a closure expression knows about its potential
for capturing and extending the lifetime of a variable. Closure expression
syntax and the concept of closing over its environment are thereby
inextricably linked. Thus, one can be well assured that every user who
extends the lifetime of a captured variable--unintentionally or no--has
already been introduced to the fact that such a thing is a possibility.
- If a user who has mastered only functions and classes can silently extend
the lifetime of a variable, this would be to that user an inexplicable
behavior. One must teach someone either "never" to nest classes inside
functions, or to do so only by putting classes before any other code, but
one would not be able to explain to the learner _why_ that must be the
case. This violates progressive disclosure.

Pedagogical points aside, introducing another way to extend the lifetime of
a variable with a syntax that currently doesn't do so is, IMO, creating
another avenue for footguns. I'm not convinced that this functionality
would be used intentionally more often that it would be triggered
unintentionally and not discovered.

···

On Fri, Dec 23, 2016 at 03:10 Callionica (Swift) < swift-callionica@callionica.com> wrote:

On Thu, Dec 22, 2016 at 11:26 PM Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Only if you're also assuming that people defining classes within functions
would know about capturing. This violates the principle of progressive
disclosure, since people naturally learn about functions and classes before
they learn about closures and capturing.

On Fri, Dec 23, 2016 at 01:51 Callionica (Swift) < > swift-callionica@callionica.com> wrote:

Assuming capture were allowed, people defining classes within functions
who didn't want them to capture could position the class definition prior
to any other code in the function so that there would be nothing to
capture.

On Thu, Dec 22, 2016 at 4:13 PM Xiaodi Wu via swift-evolution < > swift-evolution@swift.org> wrote:

I have to agree with Michael; it seems dangerous to allow implicit capture
by classes. A primary purpose (telos?) of closures is to provide this
functionality, which is actually quite an advanced concept. One knows to
think carefully about this when encountering a closure expression. A
primary purpose of classes is to provide for encapsulation of code.
Accidentally extending the lifetime of a local variable in a containing
scope would be hard to notice and highly unexpected functionality. Better
not to mix these things.

On Thu, Dec 22, 2016 at 17:54 Micah Hainline via swift-evolution < > swift-evolution@swift.org> wrote:

That's exactly what I'm suggesting, the class declaration could work
similarly to a closure.

> On Dec 22, 2016, at 4:15 PM, Michael Ilseman <milseman@apple.com> wrote:

>

> Are you asking for a class declaration to implicitly capture and extend
the lifetime of local variables? That seems like something that’s better
done explicitly. Perhaps it’s better to think about how to reduce the
boiler plate code, e.g. better default initializers.

>

> (this is of course, additive and beyond the current scope of Swift 4
phase 1 planning)

>

>> On Dec 22, 2016, at 2:39 PM, Micah Hainline via swift-evolution < > swift-evolution@swift.org> wrote:

>>

>> Currently we allow declarations of a new class in local scope, but

>> nothing can be referenced from the outer scope. Thus this is illegal:

>>

>> ```

>> func foo() {

>> let widgetString: String = createWidgetString()

>> class SimpleProvider: WidgetStringProvider {

>> func provideWidgetString() -> String {

>> return widgetString // Illegal, defined in outer scope

>> }

>> }

>> doThingsWithWidget(provider: WidgetStringProvider())

>> }

>> ```

>>

>> I'm trying to feel out the edges of this decision, and figure out why

>> exactly this isn't allowed now, and how we might want to relax this in

>> the future if possible. While not a common construct, it is a useful

>> one.

>>

>> Obviously there are ways around it, very simple to create an init and

>> a private let and do something like this:

>>

>> ```

>> func foo() {

>> let widgetString: String = createWidgetString()

>> class SimpleProvider: WidgetStringProvider {

>> private let widgetString: String

>>

>> init(widgetString: String) {

>> self.widgetString = widgetString

>> }

>>

>> func provideWidgetString() -> String {

>> return widgetString // now legal, references

>> SimpleProvider.widgetString

>> }

>> }

>> doThingsWithWidget(provider: WidgetStringProvider(widgetString:

>> widgetString))

>> }

>> ```

>>

>> That's boilerplate I don't want to write though, as it somewhat

>> detracts from the readability of the structure. I'm not super

>> interested in defending the concept of local class definitions itself,

>> it's already allowed in the language, I'm just interested in the

>> syntax limitation and where that line might be able to be redrawn.

>> _______________________________________________

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


(Callionica (Swift)) #11

I support the principle of progressive disclosure. I still fail to see how
if class capture were introduced it would prevent people from learning
about classes, functions, closures or any other existing concept in
whatever order the student or teacher preferred.

I disagree that there is any significant risk that a novice user that
doesn't expect to be able to use names that are in scope would be worse off
with this change. Think about the exact sequence of events that has to
occur to trigger this user's confusion. OTOH beginners that expect to be
able to use names that are in scope will be better off.

In general I don't believe learning has the linearity you suggest and I
don't think there is anything particularly natural about the specific
sequence of concepts you've listed.

I think it's worthwhile to analyze features from the point of view of
beginner users of the language, but I reach the opposite conclusion in this
case.

-- Callionica

···

On Fri, Dec 23, 2016 at 9:46 AM Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Fri, Dec 23, 2016 at 03:10 Callionica (Swift) < > swift-callionica@callionica.com> wrote:

I'm certainly assuming that users have a basic understanding of scope
(without that, what would be the intention behind defining the class within
the function?).

The notion of closing over variables far exceeds what one might reasonably
call "a basic understanding of scope." I doubt I'm alone on this opinion,
as _Advanced Swift_ (an excellent text written for users already familiar
with the basics) reviews closures and closure expressions from the ground
up; by contrast, it does not review functions, classes, or even (if I
recall correctly) defining classes inside functions.

I'm not clear on what you see as the downside of letting classes capture
locals for users that are unaware of capturing.

It violates the notion of progressive disclosure, which has been an
explicit design goal for Swift. In one of the documents on GitHub, the core
team writes: "One goal of swift is to provide a very 'progressive
disclosure' model of writing code and learning how to write code.
Therefore, it is desirable that someone be able to start out with
`print("hello world\n")` as their first application." Chris Lattner
recently reiterated this idea in a presentation he gave at IBM about the
Swift language.

The idea of progressive disclosure is that it should be possible to teach
concepts a learner must understand first without exposing them to more
advanced concepts they must pay attention to but cannot understand. Let's
look at some examples:

- In Java, one cannot write "Hello World" without being exposed to `public
static void main`. However, one cannot understand what it means to be a
public or static method until _after_ one has mastered printing "Hello
World". This violates progressive disclosure.
- In Swift, one's first "Hello World" is a one-liner that introduces the
print function and nothing else. This is progressive disclosure, and as
quoted above, Swift has been explicitly designed that way.

- In Swift, `struct Point { let x, y: Int }` is a fully usable type. With
that single line, you can now make a new `Point` by writing `let point =
Point(x: 42, y: 42)`.
- To do a similar thing in some other languages, you'd have to write a
constructor or initializer. However, one cannot really understand what
initialization is without first having initialized something, but one
cannot initialize something unless there is an initializer. This violates
progressive disclosure. In Swift, an initializer is synthesized for you;
this promotes progressive disclosure.
- There have been proposals to change the default access level to private.
If `x` and `y` were private, then `Point` would be unusable without access
modifiers for its members. However, one must first understand how structs
and other types encapsulate code before one can understand how access
modifiers work, but one would not be able to create useful structs or other
types without first using access modifiers. This would violate progressive
disclosure. In Swift, the default access level is internal; this promotes
progressive disclosure.

- In Swift, _closure expressions_ don't have to capture anything (i.e.,
they don't have to be true _closures_). But they are introduced as closure
expressions rather than, say, function expressions, and they are taught in
the same breath as the concept of capturing variables. This ensures that
every person who reaches for a closure expression knows about its potential
for capturing and extending the lifetime of a variable. Closure expression
syntax and the concept of closing over its environment are thereby
inextricably linked. Thus, one can be well assured that every user who
extends the lifetime of a captured variable--unintentionally or no--has
already been introduced to the fact that such a thing is a possibility.
- If a user who has mastered only functions and classes can silently
extend the lifetime of a variable, this would be to that user an
inexplicable behavior. One must teach someone either "never" to nest
classes inside functions, or to do so only by putting classes before any
other code, but one would not be able to explain to the learner _why_ that
must be the case. This violates progressive disclosure.

Pedagogical points aside, introducing another way to extend the lifetime
of a variable with a syntax that currently doesn't do so is, IMO, creating
another avenue for footguns. I'm not convinced that this functionality
would be used intentionally more often that it would be triggered
unintentionally and not discovered.

On Thu, Dec 22, 2016 at 11:26 PM Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Only if you're also assuming that people defining classes within functions
would know about capturing. This violates the principle of progressive
disclosure, since people naturally learn about functions and classes before
they learn about closures and capturing.

On Fri, Dec 23, 2016 at 01:51 Callionica (Swift) < > swift-callionica@callionica.com> wrote:

Assuming capture were allowed, people defining classes within functions
who didn't want them to capture could position the class definition prior
to any other code in the function so that there would be nothing to
capture.

On Thu, Dec 22, 2016 at 4:13 PM Xiaodi Wu via swift-evolution < > swift-evolution@swift.org> wrote:

I have to agree with Michael; it seems dangerous to allow implicit capture
by classes. A primary purpose (telos?) of closures is to provide this
functionality, which is actually quite an advanced concept. One knows to
think carefully about this when encountering a closure expression. A
primary purpose of classes is to provide for encapsulation of code.
Accidentally extending the lifetime of a local variable in a containing
scope would be hard to notice and highly unexpected functionality. Better
not to mix these things.

On Thu, Dec 22, 2016 at 17:54 Micah Hainline via swift-evolution < > swift-evolution@swift.org> wrote:

That's exactly what I'm suggesting, the class declaration could work
similarly to a closure.

> On Dec 22, 2016, at 4:15 PM, Michael Ilseman <milseman@apple.com> wrote:

>

> Are you asking for a class declaration to implicitly capture and extend
the lifetime of local variables? That seems like something that’s better
done explicitly. Perhaps it’s better to think about how to reduce the
boiler plate code, e.g. better default initializers.

>

> (this is of course, additive and beyond the current scope of Swift 4
phase 1 planning)

>

>> On Dec 22, 2016, at 2:39 PM, Micah Hainline via swift-evolution < > swift-evolution@swift.org> wrote:

>>

>> Currently we allow declarations of a new class in local scope, but

>> nothing can be referenced from the outer scope. Thus this is illegal:

>>

>> ```

>> func foo() {

>> let widgetString: String = createWidgetString()

>> class SimpleProvider: WidgetStringProvider {

>> func provideWidgetString() -> String {

>> return widgetString // Illegal, defined in outer scope

>> }

>> }

>> doThingsWithWidget(provider: WidgetStringProvider())

>> }

>> ```

>>

>> I'm trying to feel out the edges of this decision, and figure out why

>> exactly this isn't allowed now, and how we might want to relax this in

>> the future if possible. While not a common construct, it is a useful

>> one.

>>

>> Obviously there are ways around it, very simple to create an init and

>> a private let and do something like this:

>>

>> ```

>> func foo() {

>> let widgetString: String = createWidgetString()

>> class SimpleProvider: WidgetStringProvider {

>> private let widgetString: String

>>

>> init(widgetString: String) {

>> self.widgetString = widgetString

>> }

>>

>> func provideWidgetString() -> String {

>> return widgetString // now legal, references

>> SimpleProvider.widgetString

>> }

>> }

>> doThingsWithWidget(provider: WidgetStringProvider(widgetString:

>> widgetString))

>> }

>> ```

>>

>> That's boilerplate I don't want to write though, as it somewhat

>> detracts from the readability of the structure. I'm not super

>> interested in defending the concept of local class definitions itself,

>> it's already allowed in the language, I'm just interested in the

>> syntax limitation and where that line might be able to be redrawn.

>> _______________________________________________

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


(Callionica (Swift)) #12

Missed swift-evol in my reply

···

---------- Forwarded message ---------
From: Callionica (Swift) <swift-callionica@callionica.com>
Date: Sat, Dec 24, 2016 at 9:44 AM
Subject: Re: [swift-evolution] Should we relax restriction on closing over
outer scope in class declarations?
To: Robert Widmann <devteam.codafi@gmail.com>

These are good questions.

I believe Swift closures currently do implicit self-capture. I wouldn't
expect that behavior to be different when capturing for classes.

If a class needs to refer to an outer self, I would expect the user to have
to give the outer self a new name by assigning to a local. Same deal for
referring to members in an outer class where there are members of the same
name in the inner class: assign outer self to a local and refer to members
through that local.

On Fri, Dec 23, 2016 at 10:16 AM, Robert Widmann <devteam.codafi@gmail.com> wrote:

On Dec 23, 2016, at 1:10 AM, Callionica (Swift) via swift-evolution < swift-evolution@swift.org> wrote:

I'm certainly assuming that users have a basic understanding of scope
(without that, what would be the intention behind defining the class within
the function?).

Despite its simplicity to you (given the example you’ve cited) this feature
is quite an advanced extension to the current scoping rules.

I'm not clear on what you see as the downside of letting classes capture
locals for users that are unaware of capturing.

I’ll be blunt: This opens a gaping hole in the semantics of variable
lifetimes and potentially introduces an ambiguity into resolution of the
`self` keyword. For the former, consider a light extension to your
original example that introduces a nested class:

class Outer {
  let widgetString: String = createWidgetString()

  // Initializer suppressed
  func foo() -> WidgetStringProvider {
    class SimpleProvider: WidgetStringProvider {
      // Initializer suppressed
      func provideWidgetString() -> String {
        return widgetString
      }
    }
    return SimpleProvider()
  }
}

Now, from SIL’s point of view, widgetString must escape the scope it’s in
and its lifetime should be extended beyond this block. Fine. But what
about Outer? Because we’re returning a reference to SimpleProvider which
escapes the scope of foo(), and instance members of a SimpleProvider
capture outer context, this means we also have to keep Outer alive and
around for destruction (perhaps at the point where SimpleProvider is
destroyed?)

For the latter consider trying to explicitly reference widgetString here.
In provideWidgetString() we close over self in both foo() and
provideWidgetString(). So, if I wish to make reference to Outer’s self
explicitly,
how would I go about doing it? Given that today a nested aggregate can
have members with the same identifiers as their parent, how do we know if
we’re capturing them or not when they’re referenced in the inner aggregate?

~Robert Widmann

On Thu, Dec 22, 2016 at 11:26 PM Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Only if you're also assuming that people defining classes within functions
would know about capturing. This violates the principle of progressive
disclosure, since people naturally learn about functions and classes before
they learn about closures and capturing.

On Fri, Dec 23, 2016 at 01:51 Callionica (Swift) < swift-callionica@callionica.com> wrote:

Assuming capture were allowed, people defining classes within functions who
didn't want them to capture could position the class definition prior to
any other code in the function so that there would be nothing to capture.

On Thu, Dec 22, 2016 at 4:13 PM Xiaodi Wu via swift-evolution < swift-evolution@swift.org> wrote:

I have to agree with Michael; it seems dangerous to allow implicit capture
by classes. A primary purpose (telos?) of closures is to provide this
functionality, which is actually quite an advanced concept. One knows to
think carefully about this when encountering a closure expression. A
primary purpose of classes is to provide for encapsulation of code.
Accidentally extending the lifetime of a local variable in a containing
scope would be hard to notice and highly unexpected functionality. Better
not to mix these things.

On Thu, Dec 22, 2016 at 17:54 Micah Hainline via swift-evolution < swift-evolution@swift.org> wrote:

That's exactly what I'm suggesting, the class declaration could work
similarly to a closure.

On Dec 22, 2016, at 4:15 PM, Michael Ilseman <milseman@apple.com> wrote:

Are you asking for a class declaration to implicitly capture and extend

the lifetime of local variables? That seems like something that’s better
done explicitly. Perhaps it’s better to think about how to reduce the
boiler plate code, e.g. better default initializers.

(this is of course, additive and beyond the current scope of Swift 4

phase 1 planning)

On Dec 22, 2016, at 2:39 PM, Micah Hainline via swift-evolution < swift-evolution@swift.org> wrote:

Currently we allow declarations of a new class in local scope, but

nothing can be referenced from the outer scope. Thus this is illegal:

func foo() {

let widgetString: String = createWidgetString()

class SimpleProvider: WidgetStringProvider {

func provideWidgetString() -> String {
   return widgetString // Illegal, defined in outer scope
}

}

doThingsWithWidget(provider: WidgetStringProvider())

}

I'm trying to feel out the edges of this decision, and figure out why

exactly this isn't allowed now, and how we might want to relax this in

the future if possible. While not a common construct, it is a useful

one.

Obviously there are ways around it, very simple to create an init and

a private let and do something like this:

func foo() {

let widgetString: String = createWidgetString()

class SimpleProvider: WidgetStringProvider {

private let widgetString: String
init(widgetString: String) {
   self.widgetString = widgetString
}
func provideWidgetString() -> String {
   return widgetString // now legal, references

SimpleProvider.widgetString

}

}

doThingsWithWidget(provider: WidgetStringProvider(widgetString:

widgetString))

}

That's boilerplate I don't want to write though, as it somewhat

detracts from the readability of the structure. I'm not super

interested in defending the concept of local class definitions itself,

it's already allowed in the language, I'm just interested in the

syntax limitation and where that line might be able to be redrawn.

_______________________________________________

swift-evolution mailing list

swift-evolution@swift.org

https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________

swift-evolution mailing list

swift-evolution@swift.org

https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________

swift-evolution mailing list

swift-evolution@swift.org

https://lists.swift.org/mailman/listinfo/swift-evolution

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


(David Sweeris) #13

If that's the only objection, couldn't we put it behind a per-file "#enableScaryFeatures" flag? Maybe up by any import statements, so it's easy to see? Seems shortsighted to not let advanced programmers use a feature just because it's tricky for beginners. Or maybe "#enableExperimentalSyntaxes", for stuff that we know we want, but for which we don't yet have a syntax that's sufficiently "swifty" for us to commit to it (which would have the added "benefit" of giving us a place to put any feature whose syntax is a bit unstable).

(That's it... I don't have much of an opinion one way or the other on the thread's proposal yet.)

- Dave Sweeris

···

On Dec 23, 2016, at 11:07, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

On Fri, Dec 23, 2016 at 1:33 PM, Callionica (Swift) <swift-callionica@callionica.com> wrote:
I support the principle of progressive disclosure. I still fail to see how if class capture were introduced it would prevent people from learning about classes, functions, closures or any other existing concept in whatever order the student or teacher preferred.

I disagree that there is any significant risk that a novice user that doesn't expect to be able to use names that are in scope would be worse off with this change. Think about the exact sequence of events that has to occur to trigger this user's confusion. OTOH beginners that expect to be able to use names that are in scope will be better off.

In general I don't believe learning has the linearity you suggest and I don't think there is anything particularly natural about the specific sequence of concepts you've listed.

I'm making no claim that learning _has_ to be linear or ought to be, nor that any particular sequence of learning is _particularly_ natural. Rather, I understand the notion of progressive disclosure to mean that there ought to be _some_ sequence or sequences of learning where it is possible to master things in a linear fashion _if_ any particular learner wishes to proceed linearly, with concepts introduced earlier not making reference to concepts learned later. It is entirely fair if someone finds it more intuitive to learn or teach the same material in a different order, or even to learn or teach multiple concepts simultaneously.

Put another way, the question is not whether it's reasonable to have all learners progress through Swift in one particular blessed way. The question is whether, _if_ a learner is not yet ready to learn concept Z, it's possible to teach unrelated concept Y without reference to Z, for some reasonable ordering(s) of concepts from A to Z.

In this analysis, I think that classes capturing variables from a containing function would be detrimental. If one considers classes, functions, and closures as three topics, it is currently possible (I think) to master classes and functions without reference to closures. In a scenario where classes can capture variables in a containing function, one must understand the concept of escaping closures to master classes and functions. The argument isn't that one linear sequence of learning is superior to the others; the argument is that a reasonable linear ordering of these concepts conducive for at least some learners would become non-linear by having this new feature.

I think it's worthwhile to analyze features from the point of view of beginner users of the language, but I reach the opposite conclusion in this case.

-- Callionica


(Xiaodi Wu) #14

I support the principle of progressive disclosure. I still fail to see how
if class capture were introduced it would prevent people from learning
about classes, functions, closures or any other existing concept in
whatever order the student or teacher preferred.

I disagree that there is any significant risk that a novice user that
doesn't expect to be able to use names that are in scope would be worse off
with this change. Think about the exact sequence of events that has to
occur to trigger this user's confusion. OTOH beginners that expect to be
able to use names that are in scope will be better off.

In general I don't believe learning has the linearity you suggest and I
don't think there is anything particularly natural about the specific
sequence of concepts you've listed.

I'm making no claim that learning _has_ to be linear or ought to be, nor
that any particular sequence of learning is _particularly_ natural. Rather,
I understand the notion of progressive disclosure to mean that there ought
to be _some_ sequence or sequences of learning where it is possible to
master things in a linear fashion _if_ any particular learner wishes to
proceed linearly, with concepts introduced earlier not making reference to
concepts learned later. It is entirely fair if someone finds it more
intuitive to learn or teach the same material in a different order, or even
to learn or teach multiple concepts simultaneously.

Put another way, the question is not whether it's reasonable to have all
learners progress through Swift in one particular blessed way. The question
is whether, _if_ a learner is not yet ready to learn concept Z, it's
possible to teach unrelated concept Y without reference to Z, for some
reasonable ordering(s) of concepts from A to Z.

In this analysis, I think that classes capturing variables from a
containing function would be detrimental. If one considers classes,
functions, and closures as three topics, it is currently possible (I think)
to master classes and functions without reference to closures. In a
scenario where classes can capture variables in a containing function, one
must understand the concept of escaping closures to master classes and
functions. The argument isn't that one linear sequence of learning is
superior to the others; the argument is that a reasonable linear ordering
of these concepts conducive for at least some learners would become
non-linear by having this new feature.

I think it's worthwhile to analyze features from the point of view of

···

On Fri, Dec 23, 2016 at 1:33 PM, Callionica (Swift) < swift-callionica@callionica.com> wrote:

beginner users of the language, but I reach the opposite conclusion in this
case.

-- Callionica

On Fri, Dec 23, 2016 at 9:46 AM Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Fri, Dec 23, 2016 at 03:10 Callionica (Swift) < >> swift-callionica@callionica.com> wrote:

I'm certainly assuming that users have a basic understanding of scope
(without that, what would be the intention behind defining the class within
the function?).

The notion of closing over variables far exceeds what one might
reasonably call "a basic understanding of scope." I doubt I'm alone on this
opinion, as _Advanced Swift_ (an excellent text written for users already
familiar with the basics) reviews closures and closure expressions from the
ground up; by contrast, it does not review functions, classes, or even (if
I recall correctly) defining classes inside functions.

I'm not clear on what you see as the downside of letting classes capture
locals for users that are unaware of capturing.

It violates the notion of progressive disclosure, which has been an
explicit design goal for Swift. In one of the documents on GitHub, the core
team writes: "One goal of swift is to provide a very 'progressive
disclosure' model of writing code and learning how to write code.
Therefore, it is desirable that someone be able to start out with
`print("hello world\n")` as their first application." Chris Lattner
recently reiterated this idea in a presentation he gave at IBM about the
Swift language.

The idea of progressive disclosure is that it should be possible to teach
concepts a learner must understand first without exposing them to more
advanced concepts they must pay attention to but cannot understand. Let's
look at some examples:

- In Java, one cannot write "Hello World" without being exposed to
`public static void main`. However, one cannot understand what it means to
be a public or static method until _after_ one has mastered printing "Hello
World". This violates progressive disclosure.
- In Swift, one's first "Hello World" is a one-liner that introduces the
print function and nothing else. This is progressive disclosure, and as
quoted above, Swift has been explicitly designed that way.

- In Swift, `struct Point { let x, y: Int }` is a fully usable type. With
that single line, you can now make a new `Point` by writing `let point =
Point(x: 42, y: 42)`.
- To do a similar thing in some other languages, you'd have to write a
constructor or initializer. However, one cannot really understand what
initialization is without first having initialized something, but one
cannot initialize something unless there is an initializer. This violates
progressive disclosure. In Swift, an initializer is synthesized for you;
this promotes progressive disclosure.
- There have been proposals to change the default access level to
private. If `x` and `y` were private, then `Point` would be unusable
without access modifiers for its members. However, one must first
understand how structs and other types encapsulate code before one can
understand how access modifiers work, but one would not be able to create
useful structs or other types without first using access modifiers. This
would violate progressive disclosure. In Swift, the default access level is
internal; this promotes progressive disclosure.

- In Swift, _closure expressions_ don't have to capture anything (i.e.,
they don't have to be true _closures_). But they are introduced as closure
expressions rather than, say, function expressions, and they are taught in
the same breath as the concept of capturing variables. This ensures that
every person who reaches for a closure expression knows about its potential
for capturing and extending the lifetime of a variable. Closure expression
syntax and the concept of closing over its environment are thereby
inextricably linked. Thus, one can be well assured that every user who
extends the lifetime of a captured variable--unintentionally or no--has
already been introduced to the fact that such a thing is a possibility.
- If a user who has mastered only functions and classes can silently
extend the lifetime of a variable, this would be to that user an
inexplicable behavior. One must teach someone either "never" to nest
classes inside functions, or to do so only by putting classes before any
other code, but one would not be able to explain to the learner _why_ that
must be the case. This violates progressive disclosure.

Pedagogical points aside, introducing another way to extend the lifetime
of a variable with a syntax that currently doesn't do so is, IMO, creating
another avenue for footguns. I'm not convinced that this functionality
would be used intentionally more often that it would be triggered
unintentionally and not discovered.

On Thu, Dec 22, 2016 at 11:26 PM Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Only if you're also assuming that people defining classes within
functions would know about capturing. This violates the principle of
progressive disclosure, since people naturally learn about functions and
classes before they learn about closures and capturing.

On Fri, Dec 23, 2016 at 01:51 Callionica (Swift) < >> swift-callionica@callionica.com> wrote:

Assuming capture were allowed, people defining classes within functions
who didn't want them to capture could position the class definition prior
to any other code in the function so that there would be nothing to
capture.

On Thu, Dec 22, 2016 at 4:13 PM Xiaodi Wu via swift-evolution < >> swift-evolution@swift.org> wrote:

I have to agree with Michael; it seems dangerous to allow implicit
capture by classes. A primary purpose (telos?) of closures is to provide
this functionality, which is actually quite an advanced concept. One knows
to think carefully about this when encountering a closure expression. A
primary purpose of classes is to provide for encapsulation of code.
Accidentally extending the lifetime of a local variable in a containing
scope would be hard to notice and highly unexpected functionality. Better
not to mix these things.

On Thu, Dec 22, 2016 at 17:54 Micah Hainline via swift-evolution < >> swift-evolution@swift.org> wrote:

That's exactly what I'm suggesting, the class declaration could work
similarly to a closure.

> On Dec 22, 2016, at 4:15 PM, Michael Ilseman <milseman@apple.com> >> wrote:

>

> Are you asking for a class declaration to implicitly capture and extend
the lifetime of local variables? That seems like something that’s better
done explicitly. Perhaps it’s better to think about how to reduce the
boiler plate code, e.g. better default initializers.

>

> (this is of course, additive and beyond the current scope of Swift 4
phase 1 planning)

>

>> On Dec 22, 2016, at 2:39 PM, Micah Hainline via swift-evolution < >> swift-evolution@swift.org> wrote:

>>

>> Currently we allow declarations of a new class in local scope, but

>> nothing can be referenced from the outer scope. Thus this is illegal:

>>

>> ```

>> func foo() {

>> let widgetString: String = createWidgetString()

>> class SimpleProvider: WidgetStringProvider {

>> func provideWidgetString() -> String {

>> return widgetString // Illegal, defined in outer scope

>> }

>> }

>> doThingsWithWidget(provider: WidgetStringProvider())

>> }

>> ```

>>

>> I'm trying to feel out the edges of this decision, and figure out why

>> exactly this isn't allowed now, and how we might want to relax this in

>> the future if possible. While not a common construct, it is a useful

>> one.

>>

>> Obviously there are ways around it, very simple to create an init and

>> a private let and do something like this:

>>

>> ```

>> func foo() {

>> let widgetString: String = createWidgetString()

>> class SimpleProvider: WidgetStringProvider {

>> private let widgetString: String

>>

>> init(widgetString: String) {

>> self.widgetString = widgetString

>> }

>>

>> func provideWidgetString() -> String {

>> return widgetString // now legal, references

>> SimpleProvider.widgetString

>> }

>> }

>> doThingsWithWidget(provider: WidgetStringProvider(widgetString:

>> widgetString))

>> }

>> ```

>>

>> That's boilerplate I don't want to write though, as it somewhat

>> detracts from the readability of the structure. I'm not super

>> interested in defending the concept of local class definitions itself,

>> it's already allowed in the language, I'm just interested in the

>> syntax limitation and where that line might be able to be redrawn.

>> _______________________________________________

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


(David Sweeris) #15

That puts the nail on it for me… if it’s already been rejected, I don’t want to rehash it.

- Dave Sweeris

···

On Dec 23, 2016, at 1:37 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

This has been an idea brought up in the distant past about other features. The core team has said very clearly that they do not want "dialects" of Swift.


(Xiaodi Wu) #16

I support the principle of progressive disclosure. I still fail to see
how if class capture were introduced it would prevent people from learning
about classes, functions, closures or any other existing concept in
whatever order the student or teacher preferred.

I disagree that there is any significant risk that a novice user that
doesn't expect to be able to use names that are in scope would be worse off
with this change. Think about the exact sequence of events that has to
occur to trigger this user's confusion. OTOH beginners that expect to be
able to use names that are in scope will be better off.

In general I don't believe learning has the linearity you suggest and I
don't think there is anything particularly natural about the specific
sequence of concepts you've listed.

I'm making no claim that learning _has_ to be linear or ought to be, nor
that any particular sequence of learning is _particularly_ natural. Rather,
I understand the notion of progressive disclosure to mean that there ought
to be _some_ sequence or sequences of learning where it is possible to
master things in a linear fashion _if_ any particular learner wishes to
proceed linearly, with concepts introduced earlier not making reference to
concepts learned later. It is entirely fair if someone finds it more
intuitive to learn or teach the same material in a different order, or even
to learn or teach multiple concepts simultaneously.

Put another way, the question is not whether it's reasonable to have all
learners progress through Swift in one particular blessed way. The question
is whether, _if_ a learner is not yet ready to learn concept Z, it's
possible to teach unrelated concept Y without reference to Z, for some
reasonable ordering(s) of concepts from A to Z.

In this analysis, I think that classes capturing variables from a
containing function would be detrimental. If one considers classes,
functions, and closures as three topics, it is currently possible (I think)
to master classes and functions without reference to closures. In a
scenario where classes can capture variables in a containing function, one
must understand the concept of escaping closures to master classes and
functions. The argument isn't that one linear sequence of learning is
superior to the others; the argument is that a reasonable linear ordering
of these concepts conducive for at least some learners would become
non-linear by having this new feature.

I think it's worthwhile to analyze features from the point of view of

beginner users of the language, but I reach the opposite conclusion in this
case.

-- Callionica

If that's the only objection,

It's not. See Robert's excellent analysis.

couldn't we put it behind a per-file "#enableScaryFeatures" flag?

This has been an idea brought up in the distant past about other features.
The core team has said very clearly that they do not want "dialects" of
Swift.

Maybe up by any import statements, so it's easy to see? Seems shortsighted
to not let advanced programmers use a feature just because it's tricky for
beginners.

There are many, many features that are tricky for _beginners_ in Swift, and
that's totally fine. The idea behind progressive disclosure is that it's
possible to design features of arbitrary complexity in a pedagogically
sound way. Attention to the learning and teaching aspects of language
design does _not_ mean accommodating beginners at the expense of advanced
users. It's precisely the most advanced features that need the greatest
thought and care devoted to the teachability of their design, so that the
greatest number of people can learn the most advanced features in the most
approachable way.

Or maybe "#enableExperimentalSyntaxes", for stuff that we know we want, but

···

On Fri, Dec 23, 2016 at 4:27 PM, David Sweeris <davesweeris@mac.com> wrote:

On Dec 23, 2016, at 11:07, Xiaodi Wu via swift-evolution < > swift-evolution@swift.org> wrote:
On Fri, Dec 23, 2016 at 1:33 PM, Callionica (Swift) < > swift-callionica@callionica.com> wrote:
for which we don't yet have a syntax that's sufficiently "swifty" for us to
commit to it (which would have the added "benefit" of giving us a place to
put any feature whose syntax is a bit unstable).

(That's it... I don't have much of an opinion one way or the other on the
thread's proposal yet.)

- Dave Sweeris


(Guillaume DIDIER) #17

If I understand correctly we are speaking of class definitions inside functions, so this doesn't affect classes defined at the global scope, which are what the beginner will first be told. Teaching them about defining classes in other places would then wait until they know more about closure, scopes and variable capture.

I would suggest treating a class defined within a function body the same way a function defined within a function body would be.

Guillaume DIDIER (ASP)
Section Escrime, 3ème Compagnie,
Promotion X 2014

···


ÉCOLE POLYTECHNIQUE
91128 PALAISEAU CEDEX
M. +33 (0)7 70 43 18 40
guillaume.didier@polytechnique.edu
www.polytechnique.edu

Le 23 déc. 2016 à 23:49, David Sweeris via swift-evolution <swift-evolution@swift.org> a écrit :

On Dec 23, 2016, at 1:37 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

This has been an idea brought up in the distant past about other features. The core team has said very clearly that they do not want "dialects" of Swift.

That puts the nail on it for me… if it’s already been rejected, I don’t want to rehash it.

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


(Robert Widmann) #18

That still doesn't answer the lifetime extension problems. What is being described here is a complex extension to the lifetime of memory that is only being kept alive because of these implicit captures. It sounds like what you want is a member, and I'm not sure it's worth saving 5 lines to avoid that fact.

~Robert Widmann

2016/12/24 11:45、Callionica (Swift) via swift-evolution <swift-evolution@swift.org> のメッセージ:

···

Missed swift-evol in my reply

---------- Forwarded message ---------
From: Callionica (Swift) <swift-callionica@callionica.com>
Date: Sat, Dec 24, 2016 at 9:44 AM
Subject: Re: [swift-evolution] Should we relax restriction on closing over outer scope in class declarations?
To: Robert Widmann <devteam.codafi@gmail.com>

These are good questions.

I believe Swift closures currently do implicit self-capture. I wouldn't expect that behavior to be different when capturing for classes.

If a class needs to refer to an outer self, I would expect the user to have to give the outer self a new name by assigning to a local. Same deal for referring to members in an outer class where there are members of the same name in the inner class: assign outer self to a local and refer to members through that local.

On Fri, Dec 23, 2016 at 10:16 AM, Robert Widmann <devteam.codafi@gmail.com> wrote:

On Dec 23, 2016, at 1:10 AM, Callionica (Swift) via swift-evolution <swift-evolution@swift.org> wrote:

I'm certainly assuming that users have a basic understanding of scope (without that, what would be the intention behind defining the class within the function?).

Despite its simplicity to you (given the example you’ve cited) this feature is quite an advanced extension to the current scoping rules.

I'm not clear on what you see as the downside of letting classes capture locals for users that are unaware of capturing.

I’ll be blunt: This opens a gaping hole in the semantics of variable lifetimes and potentially introduces an ambiguity into resolution of the `self` keyword. For the former, consider a light extension to your original example that introduces a nested class:

class Outer {
  let widgetString: String = createWidgetString()

  // Initializer suppressed
  func foo() -> WidgetStringProvider {
    class SimpleProvider: WidgetStringProvider {
      // Initializer suppressed
      func provideWidgetString() -> String {
        return widgetString
      }
    }
    return SimpleProvider()
  }
}

Now, from SIL’s point of view, widgetString must escape the scope it’s in and its lifetime should be extended beyond this block. Fine. But what about Outer? Because we’re returning a reference to SimpleProvider which escapes the scope of foo(), and instance members of a SimpleProvider capture outer context, this means we also have to keep Outer alive and around for destruction (perhaps at the point where SimpleProvider is destroyed?)

For the latter consider trying to explicitly reference widgetString here. In provideWidgetString() we close over self in both foo() and provideWidgetString(). So, if I wish to make reference to Outer’s self explicitly, how would I go about doing it? Given that today a nested aggregate can have members with the same identifiers as their parent, how do we know if we’re capturing them or not when they’re referenced in the inner aggregate?

~Robert Widmann

On Thu, Dec 22, 2016 at 11:26 PM Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
Only if you're also assuming that people defining classes within functions would know about capturing. This violates the principle of progressive disclosure, since people naturally learn about functions and classes before they learn about closures and capturing.

On Fri, Dec 23, 2016 at 01:51 Callionica (Swift) <swift-callionica@callionica.com> wrote:
Assuming capture were allowed, people defining classes within functions who didn't want them to capture could position the class definition prior to any other code in the function so that there would be nothing to capture.

On Thu, Dec 22, 2016 at 4:13 PM Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:
I have to agree with Michael; it seems dangerous to allow implicit capture by classes. A primary purpose (telos?) of closures is to provide this functionality, which is actually quite an advanced concept. One knows to think carefully about this when encountering a closure expression. A primary purpose of classes is to provide for encapsulation of code. Accidentally extending the lifetime of a local variable in a containing scope would be hard to notice and highly unexpected functionality. Better not to mix these things.

On Thu, Dec 22, 2016 at 17:54 Micah Hainline via swift-evolution <swift-evolution@swift.org> wrote:
That's exactly what I'm suggesting, the class declaration could work similarly to a closure.

> On Dec 22, 2016, at 4:15 PM, Michael Ilseman <milseman@apple.com> wrote:

>

> Are you asking for a class declaration to implicitly capture and extend the lifetime of local variables? That seems like something that’s better done explicitly. Perhaps it’s better to think about how to reduce the boiler plate code, e.g. better default initializers.

>

> (this is of course, additive and beyond the current scope of Swift 4 phase 1 planning)

>

>> On Dec 22, 2016, at 2:39 PM, Micah Hainline via swift-evolution <swift-evolution@swift.org> wrote:

>>

>> Currently we allow declarations of a new class in local scope, but

>> nothing can be referenced from the outer scope. Thus this is illegal:

>>

>> ```

>> func foo() {

>> let widgetString: String = createWidgetString()

>> class SimpleProvider: WidgetStringProvider {

>> func provideWidgetString() -> String {

>> return widgetString // Illegal, defined in outer scope

>> }

>> }

>> doThingsWithWidget(provider: WidgetStringProvider())

>> }

>> ```

>>

>> I'm trying to feel out the edges of this decision, and figure out why

>> exactly this isn't allowed now, and how we might want to relax this in

>> the future if possible. While not a common construct, it is a useful

>> one.

>>

>> Obviously there are ways around it, very simple to create an init and

>> a private let and do something like this:

>>

>> ```

>> func foo() {

>> let widgetString: String = createWidgetString()

>> class SimpleProvider: WidgetStringProvider {

>> private let widgetString: String

>>

>> init(widgetString: String) {

>> self.widgetString = widgetString

>> }

>>

>> func provideWidgetString() -> String {

>> return widgetString // now legal, references

>> SimpleProvider.widgetString

>> }

>> }

>> doThingsWithWidget(provider: WidgetStringProvider(widgetString:

>> widgetString))

>> }

>> ```

>>

>> That's boilerplate I don't want to write though, as it somewhat

>> detracts from the readability of the structure. I'm not super

>> interested in defending the concept of local class definitions itself,

>> it's already allowed in the language, I'm just interested in the

>> syntax limitation and where that line might be able to be redrawn.

>> _______________________________________________

>> swift-evolution mailing list

>> swift-evolution@swift.org

>> https://lists.swift.org/mailman/listinfo/swift-evolution

>

_______________________________________________

swift-evolution mailing list

swift-evolution@swift.org

https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________

swift-evolution mailing list

swift-evolution@swift.org

https://lists.swift.org/mailman/listinfo/swift-evolution

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

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


(Callionica (Swift)) #19

I may be missing your point about lifetime extension problems. How would
lifetime extension be different for captures by classes than for captures
by function closures?

···

On Sat, Dec 24, 2016 at 12:00 PM Robert Widmann <devteam.codafi@gmail.com> wrote:

That still doesn't answer the lifetime extension problems. What is being
described here is a complex extension to the lifetime of memory that is
only being kept alive because of these implicit captures. It sounds like
what you want is a member, and I'm not sure it's worth saving 5 lines to
avoid that fact.

~Robert Widmann

2016/12/24 11:45、Callionica (Swift) via swift-evolution <
swift-evolution@swift.org> のメッセージ:

Missed swift-evol in my reply

---------- Forwarded message ---------
From: Callionica (Swift) <swift-callionica@callionica.com>
Date: Sat, Dec 24, 2016 at 9:44 AM
Subject: Re: [swift-evolution] Should we relax restriction on closing over
outer scope in class declarations?
To: Robert Widmann <devteam.codafi@gmail.com>

These are good questions.

I believe Swift closures currently do implicit self-capture. I wouldn't
expect that behavior to be different when capturing for classes.

If a class needs to refer to an outer self, I would expect the user to
have to give the outer self a new name by assigning to a local. Same deal
for referring to members in an outer class where there are members of the
same name in the inner class: assign outer self to a local and refer to
members through that local.

On Fri, Dec 23, 2016 at 10:16 AM, Robert Widmann <devteam.codafi@gmail.com > > wrote:

On Dec 23, 2016, at 1:10 AM, Callionica (Swift) via swift-evolution < > swift-evolution@swift.org> wrote:

I'm certainly assuming that users have a basic understanding of scope
(without that, what would be the intention behind defining the class within
the function?).

Despite its simplicity to you (given the example you’ve cited) this
feature is quite an advanced extension to the current scoping rules.

I'm not clear on what you see as the downside of letting classes capture
locals for users that are unaware of capturing.

I’ll be blunt: This opens a gaping hole in the semantics of variable
lifetimes and potentially introduces an ambiguity into resolution of the
`self` keyword. For the former, consider a light extension to your
original example that introduces a nested class:

class Outer {
  let widgetString: String = createWidgetString()

  // Initializer suppressed
  func foo() -> WidgetStringProvider {
    class SimpleProvider: WidgetStringProvider {
      // Initializer suppressed
      func provideWidgetString() -> String {
        return widgetString
      }
    }
    return SimpleProvider()
  }
}

Now, from SIL’s point of view, widgetString must escape the scope it’s
in and its lifetime should be extended beyond this block. Fine. But what
about Outer? Because we’re returning a reference to SimpleProvider which
escapes the scope of foo(), and instance members of a SimpleProvider
capture outer context, this means we also have to keep Outer alive and
around for destruction (perhaps at the point where SimpleProvider is
destroyed?)

For the latter consider trying to explicitly reference widgetString
here. In provideWidgetString() we close over self in both foo() and
provideWidgetString(). So, if I wish to make reference to Outer’s self explicitly,
how would I go about doing it? Given that today a nested aggregate can
have members with the same identifiers as their parent, how do we know if
we’re capturing them or not when they’re referenced in the inner aggregate?

~Robert Widmann

On Thu, Dec 22, 2016 at 11:26 PM Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Only if you're also assuming that people defining classes within functions
would know about capturing. This violates the principle of progressive
disclosure, since people naturally learn about functions and classes before
they learn about closures and capturing.

On Fri, Dec 23, 2016 at 01:51 Callionica (Swift) < > swift-callionica@callionica.com> wrote:

Assuming capture were allowed, people defining classes within functions
who didn't want them to capture could position the class definition prior
to any other code in the function so that there would be nothing to
capture.

On Thu, Dec 22, 2016 at 4:13 PM Xiaodi Wu via swift-evolution < > swift-evolution@swift.org> wrote:

I have to agree with Michael; it seems dangerous to allow implicit capture
by classes. A primary purpose (telos?) of closures is to provide this
functionality, which is actually quite an advanced concept. One knows to
think carefully about this when encountering a closure expression. A
primary purpose of classes is to provide for encapsulation of code.
Accidentally extending the lifetime of a local variable in a containing
scope would be hard to notice and highly unexpected functionality. Better
not to mix these things.

On Thu, Dec 22, 2016 at 17:54 Micah Hainline via swift-evolution < > swift-evolution@swift.org> wrote:

That's exactly what I'm suggesting, the class declaration could work
similarly to a closure.

> On Dec 22, 2016, at 4:15 PM, Michael Ilseman <milseman@apple.com> wrote:

>

> Are you asking for a class declaration to implicitly capture and extend
the lifetime of local variables? That seems like something that’s better
done explicitly. Perhaps it’s better to think about how to reduce the
boiler plate code, e.g. better default initializers.

>

> (this is of course, additive and beyond the current scope of Swift 4
phase 1 planning)

>

>> On Dec 22, 2016, at 2:39 PM, Micah Hainline via swift-evolution < > swift-evolution@swift.org> wrote:

>>

>> Currently we allow declarations of a new class in local scope, but

>> nothing can be referenced from the outer scope. Thus this is illegal:

>>

>> ```

>> func foo() {

>> let widgetString: String = createWidgetString()

>> class SimpleProvider: WidgetStringProvider {

>> func provideWidgetString() -> String {

>> return widgetString // Illegal, defined in outer scope

>> }

>> }

>> doThingsWithWidget(provider: WidgetStringProvider())

>> }

>> ```

>>

>> I'm trying to feel out the edges of this decision, and figure out why

>> exactly this isn't allowed now, and how we might want to relax this in

>> the future if possible. While not a common construct, it is a useful

>> one.

>>

>> Obviously there are ways around it, very simple to create an init and

>> a private let and do something like this:

>>

>> ```

>> func foo() {

>> let widgetString: String = createWidgetString()

>> class SimpleProvider: WidgetStringProvider {

>> private let widgetString: String

>>

>> init(widgetString: String) {

>> self.widgetString = widgetString

>> }

>>

>> func provideWidgetString() -> String {

>> return widgetString // now legal, references

>> SimpleProvider.widgetString

>> }

>> }

>> doThingsWithWidget(provider: WidgetStringProvider(widgetString:

>> widgetString))

>> }

>> ```

>>

>> That's boilerplate I don't want to write though, as it somewhat

>> detracts from the readability of the structure. I'm not super

>> interested in defending the concept of local class definitions itself,

>> it's already allowed in the language, I'm just interested in the

>> syntax limitation and where that line might be able to be redrawn.

>> _______________________________________________

>> swift-evolution mailing list

>> swift-evolution@swift.org

>> https://lists.swift.org/mailman/listinfo/swift-evolution

>

_______________________________________________

swift-evolution mailing list

swift-evolution@swift.org

https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________

swift-evolution mailing list

swift-evolution@swift.org

https://lists.swift.org/mailman/listinfo/swift-evolution

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

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


(Xiaodi Wu) #20

Pedagogically, that is unworkable. A user who has learned about functions
would already know that a function can be defined inside another. If you
are teaching the concept of classes to that user, either you must first lie
and say that a class cannot be defined inside a function, or your student
will naturally discover that in fact you can--but then potentially
encounter behaviors that he or she cannot make sense of.

In any case, Robert's excellent analysis shows why this proposed feature
would simply break Swift, so the pedagogical objection is not even foremost
among issues.

···

On Sat, Dec 24, 2016 at 03:46 Guillaume DIDIER < guillaume.didier@polytechnique.edu> wrote:

If I understand correctly we are speaking of class definitions inside
functions, so this doesn't affect classes defined at the global scope,
which are what the beginner will first be told. Teaching them about
defining classes in other places would then wait until they know more about
closure, scopes and variable capture.

I would suggest treating a class defined within a function body the same
way a function defined within a function body would be.

*Guillaume DIDIER (ASP)*

*Section Escrime, 3ème Compagnie, Promotion X 2014*


*ÉCOLE POLYTECHNIQUE*
91128 PALAISEAU CEDEX
M. +33 ( <+33%207%C2%A070%2043%2018%2040>0
<+33%207%C2%A070%2043%2018%2040>)7 <+33%207%C2%A070%2043%2018%2040> 70 43
18 40 <+33%207%C2%A070%2043%2018%2040>
guillaume.didier@polytechnique.edu
<guillaume.didier@polytechnique.edu?subject=>
www.polytechnique.edu

Le 23 déc. 2016 à 23:49, David Sweeris via swift-evolution < > swift-evolution@swift.org> a écrit :

On Dec 23, 2016, at 1:37 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

This has been an idea brought up in the distant past about other features.
The core team has said very clearly that they do not want "dialects" of
Swift.

That puts the nail on it for me… if it’s already been rejected, I don’t
want to rehash it.

- Dave Sweeris

_______________________________________________

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