[swift-evolution] Fwd: Should we relax restriction on closing over outer scope in class declarations?

Callionica (Swift) swift-callionica at callionica.com
Sat Dec 24 14:58:30 CST 2016


Maybe you could show me what difference you'd expect with classes by
comparing to this code that uses functions? Sorry for being slow here.

The following code prints
W-002 25 W-002 25

// https://swiftlang.ng.bluemix.net/#/repl
// Swift Ver. 3.0.2 (Release)
// Platform: Linux (x86_64)

class WidgetStore {
  var widget: String = "W-001"
  var count: Int = 1;

  func getWidgetProvider() -> ()->String {
      func provideWidget() -> String {
        return widget
      }

    return provideWidget
  }

  func getCountProvider() -> ()->Int {
      func provideCount() -> Int {
        return count
      }

    return provideCount
  }
}

var store = WidgetStore()
var getWidget = store.getWidgetProvider()
var getCount = store.getCountProvider()

store.widget = "W-002"
store.count = 25;

print(store.widget)
print(store.count)

print(getWidget())
print(getCount())


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

> The point here is that the escape of the inner value necessitates the
> (implicit) escape of the outer captured value.  That's something closures
> don't have to deal with.
>
> ~Robert Widmann
>
> 2016/12/24 13:13、Callionica (Swift) <swift-callionica at callionica.com>
> のメッセージ:
>
> 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 at 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 at swift.org> のメッセージ:
>>
>> Missed swift-evol in my reply
>>
>> ---------- Forwarded message ---------
>> From: Callionica (Swift) <swift-callionica at 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 at 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 at gmail.com> wrote:
>>
>>
>> On Dec 23, 2016, at 1:10 AM, Callionica (Swift) via swift-evolution <
>> swift-evolution at 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 at 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 at 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 at 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 at 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 at 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 at 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 at swift.org
>>
>>
>> >> https://lists.swift.org/mailman/listinfo/swift-evolution
>>
>>
>> >
>>
>>
>> _______________________________________________
>>
>>
>> swift-evolution mailing list
>>
>>
>> swift-evolution at swift.org
>>
>>
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>
>>
>>
>>
>> _______________________________________________
>>
>> swift-evolution mailing list
>>
>> swift-evolution at swift.org
>>
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>
>>
>>
>>
>>
>>
>>
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>
>>
>>
>>
>>
>>
>>
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20161224/acecf896/attachment.html>


More information about the swift-evolution mailing list