Proposal: 'selfless' keyword for refactoring duplicate code from initialisers


(Ross O'Brien) #1

Hi all,

I'm a new member of the list, so apologies if this is a duplicate of an
existing idea or if there's already a way to do this in Swift 2.1 that I've
missed.

In Objective C, and C-like languages, an initialiser function represents a
stage after allocation of memory where properties are given values. In
Swift, init appears to precede (or overlap with) allocation. The benefit of
this is that for type-safety reasons, all properties of a type (or new
properties of a derived type) can be verified as having values. The
disadvantage, and one of the stumbling blocks for those who learned
Objective-C, is that until all the properties have values, the instance
does not exist and instance functions cannot be called.

There's an invisible threshold in Swift init() functions marking this
transition. In derived classes it's the point where super.init() is called
- after the derived type has provided initial values, but before any type
functions can be called.

Some types have multiple initialisers, and may be duplicating a lot of code
in those distinct inits before they cross the threshold. This code can't be
refactored into an instance function because the instance doesn't exist
yet. The instance function may not even require the use of any properties
of the type.

If the compiler can read an init function and its varied control flow and
determine a threshold where all properties have values, presumably it can
read the code of any function called before that threshold, determine which
properties they read and which they assign to, and provide a warning if a
path assigns to a constant a second time, etc.. But this isn't currently
happening.

I'm guessing there are multiple contributing factors for this: the
combinatorial explosion of possible control flow paths with functions
(particularly if they're recursive); the possibility that the function
calls are used by the compiler to mark the end of a control flow path, by
which point it can determine whether everything has a value; the function
genuinely can't exist without allocation. I don't know the reasons but I'd
be interested to learn them.

I'm proposing the keyword 'selfless' for a function which could be called
before the threshold. It either only uses local properties or properties
belonging to the type - never to the 'super' type (in the case of a derived
class). It can't call any instance functions which aren't themselves
selfless.

Example of use:
class FooView : UIView
{
    var property : Int

    init()
    {
        initialiseProperty()
        super.init()
    }

    init(frame:CGRect)
    {
        initialiseProperty()
        super.init(frame)
    }

    selfless func initialiseProperty()
    {
        property = 4
    }
}

Is this something of interest?

Regards,
Ross O'Brien


(Colin Cornaby) #2

+1 on not being sure selfless is the right keyword, but liking the direction of this proposal. I haven’t hit this issue yet in my Swift projects, but I see this pattern all the time in Obj-C.

I’m trying to play devils advocate with myself and figure out if there is a way around without adding a new concept to the language… Not coming up with much so far.

···

On Dec 15, 2015, at 4:30 PM, Charles Srstka via swift-evolution <swift-evolution@swift.org> wrote:

On Dec 15, 2015, at 5:59 PM, Ross O'Brien via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hi all,

I'm a new member of the list, so apologies if this is a duplicate of an existing idea or if there's already a way to do this in Swift 2.1 that I've missed.

In Objective C, and C-like languages, an initialiser function represents a stage after allocation of memory where properties are given values. In Swift, init appears to precede (or overlap with) allocation. The benefit of this is that for type-safety reasons, all properties of a type (or new properties of a derived type) can be verified as having values. The disadvantage, and one of the stumbling blocks for those who learned Objective-C, is that until all the properties have values, the instance does not exist and instance functions cannot be called.

There's an invisible threshold in Swift init() functions marking this transition. In derived classes it's the point where super.init() is called - after the derived type has provided initial values, but before any type functions can be called.

Some types have multiple initialisers, and may be duplicating a lot of code in those distinct inits before they cross the threshold. This code can't be refactored into an instance function because the instance doesn't exist yet. The instance function may not even require the use of any properties of the type.

If the compiler can read an init function and its varied control flow and determine a threshold where all properties have values, presumably it can read the code of any function called before that threshold, determine which properties they read and which they assign to, and provide a warning if a path assigns to a constant a second time, etc.. But this isn't currently happening.

I'm guessing there are multiple contributing factors for this: the combinatorial explosion of possible control flow paths with functions (particularly if they're recursive); the possibility that the function calls are used by the compiler to mark the end of a control flow path, by which point it can determine whether everything has a value; the function genuinely can't exist without allocation. I don't know the reasons but I'd be interested to learn them.

I'm proposing the keyword 'selfless' for a function which could be called before the threshold. It either only uses local properties or properties belonging to the type - never to the 'super' type (in the case of a derived class). It can't call any instance functions which aren't themselves selfless.

Example of use:
class FooView : UIView
{
    var property : Int

    init()
    {
        initialiseProperty()
        super.init()
    }

    init(frame:CGRect)
    {
        initialiseProperty()
        super.init(frame)
    }

    selfless func initialiseProperty()
    {
        property = 4
    }
}

Is this something of interest?

Regards,
Ross O'Brien
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

+1. This is something that I was planning to propose. It comes up particularly often in Cocoa objects that implement NSCoding, where you have to implement both init(coder:) and the designated initializer. Currently, if you have a bunch of complicated code involved in setting defaults for your properties, in a manner that’s too complex to solve with simple default values, you end up with a lot of copy-paste code in the two initializers, which can easily get out of sync if one is edited without being diligent about editing the other one in the same way. The exception, of course, if if you make init(coder:) a convenience initializer, but then subclasses cannot call super’s implementation of init(coder:), which makes this unworkable in a lot of circumstances.

I’m not sure “selfless” is the right keyword for this, but some sort of feature along these lines would be incredibly helpful.

Charles

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


(Charles Srstka) #3

+1. This is something that I was planning to propose. It comes up particularly often in Cocoa objects that implement NSCoding, where you have to implement both init(coder:) and the designated initializer. Currently, if you have a bunch of complicated code involved in setting defaults for your properties, in a manner that’s too complex to solve with simple default values, you end up with a lot of copy-paste code in the two initializers, which can easily get out of sync if one is edited without being diligent about editing the other one in the same way. The exception, of course, if if you make init(coder:) a convenience initializer, but then subclasses cannot call super’s implementation of init(coder:), which makes this unworkable in a lot of circumstances.

I’m not sure “selfless” is the right keyword for this, but some sort of feature along these lines would be incredibly helpful.

Charles

···

On Dec 15, 2015, at 5:59 PM, Ross O'Brien via swift-evolution <swift-evolution@swift.org> wrote:

Hi all,

I'm a new member of the list, so apologies if this is a duplicate of an existing idea or if there's already a way to do this in Swift 2.1 that I've missed.

In Objective C, and C-like languages, an initialiser function represents a stage after allocation of memory where properties are given values. In Swift, init appears to precede (or overlap with) allocation. The benefit of this is that for type-safety reasons, all properties of a type (or new properties of a derived type) can be verified as having values. The disadvantage, and one of the stumbling blocks for those who learned Objective-C, is that until all the properties have values, the instance does not exist and instance functions cannot be called.

There's an invisible threshold in Swift init() functions marking this transition. In derived classes it's the point where super.init() is called - after the derived type has provided initial values, but before any type functions can be called.

Some types have multiple initialisers, and may be duplicating a lot of code in those distinct inits before they cross the threshold. This code can't be refactored into an instance function because the instance doesn't exist yet. The instance function may not even require the use of any properties of the type.

If the compiler can read an init function and its varied control flow and determine a threshold where all properties have values, presumably it can read the code of any function called before that threshold, determine which properties they read and which they assign to, and provide a warning if a path assigns to a constant a second time, etc.. But this isn't currently happening.

I'm guessing there are multiple contributing factors for this: the combinatorial explosion of possible control flow paths with functions (particularly if they're recursive); the possibility that the function calls are used by the compiler to mark the end of a control flow path, by which point it can determine whether everything has a value; the function genuinely can't exist without allocation. I don't know the reasons but I'd be interested to learn them.

I'm proposing the keyword 'selfless' for a function which could be called before the threshold. It either only uses local properties or properties belonging to the type - never to the 'super' type (in the case of a derived class). It can't call any instance functions which aren't themselves selfless.

Example of use:
class FooView : UIView
{
    var property : Int

    init()
    {
        initialiseProperty()
        super.init()
    }

    init(frame:CGRect)
    {
        initialiseProperty()
        super.init(frame)
    }

    selfless func initialiseProperty()
    {
        property = 4
    }
}

Is this something of interest?

Regards,
Ross O'Brien
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Marc Knaup) #4

In its current state with the initial example -1 from me for the proposal.

The example could easily be written like this:

class FooView: UIView {

    var property = 4

    init() {
        super.init()
    }

    init(frame: CGRect) {
        super.init(frame)
    }
}

In cases where the initially value is computed in a complex way a closure
can be used:

class FooView: UIView {

    var property: Int = {
        // some complicated computation
        return value
    }()

    init() {
        super.init()
    }

    init(frame: CGRect) {
        super.init(frame)
    }
}

And that value could even delegate computation to static methods of the
class.

In cases where the computation is even more complex and refers to other
properties a lazy var can be used, which even allows the value to refer to
self:

class FooView: UIView {

    private(set) lazy var property: Int = {
        // some complicated computation which can use self
        return value
    }()

    init() {
        super.init()
    }

    init(frame: CGRect) {
        super.init(frame)
    }
}

···

On Wed, Dec 16, 2015 at 1:30 AM, Charles Srstka via swift-evolution < swift-evolution@swift.org> wrote:

On Dec 15, 2015, at 5:59 PM, Ross O'Brien via swift-evolution < > swift-evolution@swift.org> wrote:

Hi all,

I'm a new member of the list, so apologies if this is a duplicate of an
existing idea or if there's already a way to do this in Swift 2.1 that I've
missed.

In Objective C, and C-like languages, an initialiser function represents a
stage after allocation of memory where properties are given values. In
Swift, init appears to precede (or overlap with) allocation. The benefit of
this is that for type-safety reasons, all properties of a type (or new
properties of a derived type) can be verified as having values. The
disadvantage, and one of the stumbling blocks for those who learned
Objective-C, is that until all the properties have values, the instance
does not exist and instance functions cannot be called.

There's an invisible threshold in Swift init() functions marking this
transition. In derived classes it's the point where super.init() is called
- after the derived type has provided initial values, but before any type
functions can be called.

Some types have multiple initialisers, and may be duplicating a lot of
code in those distinct inits before they cross the threshold. This code
can't be refactored into an instance function because the instance doesn't
exist yet. The instance function may not even require the use of any
properties of the type.

If the compiler can read an init function and its varied control flow and
determine a threshold where all properties have values, presumably it can
read the code of any function called before that threshold, determine which
properties they read and which they assign to, and provide a warning if a
path assigns to a constant a second time, etc.. But this isn't currently
happening.

I'm guessing there are multiple contributing factors for this: the
combinatorial explosion of possible control flow paths with functions
(particularly if they're recursive); the possibility that the function
calls are used by the compiler to mark the end of a control flow path, by
which point it can determine whether everything has a value; the function
genuinely can't exist without allocation. I don't know the reasons but I'd
be interested to learn them.

I'm proposing the keyword 'selfless' for a function which could be called
before the threshold. It either only uses local properties or properties
belonging to the type - never to the 'super' type (in the case of a derived
class). It can't call any instance functions which aren't themselves
selfless.

Example of use:
class FooView : UIView
{
    var property : Int

    init()
    {
        initialiseProperty()
        super.init()
    }

    init(frame:CGRect)
    {
        initialiseProperty()
        super.init(frame)
    }

    selfless func initialiseProperty()
    {
        property = 4
    }
}

Is this something of interest?

Regards,
Ross O'Brien
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

+1. This is something that I was planning to propose. It comes up
particularly often in Cocoa objects that implement NSCoding, where you have
to implement both init(coder:) and the designated initializer. Currently,
if you have a bunch of complicated code involved in setting defaults for
your properties, in a manner that’s too complex to solve with simple
default values, you end up with a lot of copy-paste code in the two
initializers, which can easily get out of sync if one is edited without
being diligent about editing the other one in the same way. The exception,
of course, if if you make init(coder:) a convenience initializer, but then
subclasses cannot call super’s implementation of init(coder:), which makes
this unworkable in a lot of circumstances.

I’m not sure “selfless” is the right keyword for this, but some sort of
feature along these lines would be incredibly helpful.

Charles

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


(Charles Srstka) #5

Okay, resurrecting this proposal (which some may not have seen due to it having been sent over the holiday break), because I thought of a really major use case.

In addition to the obvious use cases, like init methods that get too long because they can’t be broken up into smaller methods, duplicated code between init() and init(coder:), and the like, I just realized that this feature, if implemented with an appropriate annotation to make it bridgeable from Objective-C headers, could make NSDocument not suck in Swift.

Currently we have this situation:

class MyDoc: NSDocument {
  var foo: Foo!
  var bar: Bar!

  override func readFromData(data: NSData, ofType: String) throws {
    self.foo = // parse something from the data
    self.bar = // parse something from the data

    // etc.
  }
}

There are a number of problems with this.

1. Since there’s no way to know what any of the document’s state is supposed to be at the time the class is initialized, pretty much everything has to be optional. And in practice, these are going to be of the implicitly-unwrapped variety much of the time, since many of these variables have no real reason to be optional once readFromData() has been called, and no one is going to want to needlessly unwrap these things Every. Damn. Time.

2. Unlike code in init methods, the code above will call the setters for foo and bar, *not* just set the underlying ivars directly. This means that willSets and didSets, as well as things like KVO notifications, will get fired. If the document has a decent number of properties, it is easy for properties that haven’t been initialized yet to be inadvertently called as a result of the side-effects of setting properties in readFromData(), leading to a crash.

3. Redesigning NSDocument to work better with Swift is an option, except that as the language currently stands, this would be hard to do and make it work similarly to how it currently does. Particularly, the current system provides several override points. If you just need the file contents, you override readFromData(). However, if you need to access file metadata or something beyond just the contents, you can override readFromURL() instead. The default implementation of readFromURL(), of course, calls readFromData(), so you can choose which override point is more appropriate and use that. The obvious solution to this would be to turn the readFrom*() methods into initializers, but for init(URL:) to call init(data:) in its default implementation, it would need to be declared as a convenience initializer—which can’t be overridden.

If we had a way to declare methods as init helpers, a way to annotate these in Objective-C, and an annotation to put on the designated initializer indicating which init helpers the initializer will call, this might be able to be all separated out. I’m not sure whether NSDocument could be made to conform to this as is, but a hypothetical redesigned document class could look something like this:

class NSHypotheticalDocument: NSObject {
  @uses_helper(readFromURL:ofType:) init(URL url: NSURL, ofType type: String) throws {
    try self.readFromURL(url, ofType: type)
    super.init()
  }

  @uses_helper(readFromData:ofType:) init_helper readFromURL(url: NSURL, ofType type: String) throws {
    let data = try NSData(contentsOfURL: url, options: [])
    try self.readFromData(data, ofType: type)
  }

  init_helper readFromData(data: NSData, ofType type: String) throws {
    throw NSCocoaError.FeatureUnsupportedError
  }

  // other stuff
}

A subclass could now override one of the two helper methods, and since the designated initializer is documented as calling these particular helpers, the compiler could know that any properties set in an overridden helper method would be set by the end of the super.init call in the subclass’s own initializer, and thus these properties would be set without side effects. This could allow properties in document classes to be safely set without unnecessary use of optionals.

I dunno, what do you think?

Charles

···

On Dec 15, 2015, at 5:59 PM, Ross O'Brien via swift-evolution <swift-evolution@swift.org> wrote:

Hi all,

I'm a new member of the list, so apologies if this is a duplicate of an existing idea or if there's already a way to do this in Swift 2.1 that I've missed.

In Objective C, and C-like languages, an initialiser function represents a stage after allocation of memory where properties are given values. In Swift, init appears to precede (or overlap with) allocation. The benefit of this is that for type-safety reasons, all properties of a type (or new properties of a derived type) can be verified as having values. The disadvantage, and one of the stumbling blocks for those who learned Objective-C, is that until all the properties have values, the instance does not exist and instance functions cannot be called.

There's an invisible threshold in Swift init() functions marking this transition. In derived classes it's the point where super.init() is called - after the derived type has provided initial values, but before any type functions can be called.

Some types have multiple initialisers, and may be duplicating a lot of code in those distinct inits before they cross the threshold. This code can't be refactored into an instance function because the instance doesn't exist yet. The instance function may not even require the use of any properties of the type.

If the compiler can read an init function and its varied control flow and determine a threshold where all properties have values, presumably it can read the code of any function called before that threshold, determine which properties they read and which they assign to, and provide a warning if a path assigns to a constant a second time, etc.. But this isn't currently happening.

I'm guessing there are multiple contributing factors for this: the combinatorial explosion of possible control flow paths with functions (particularly if they're recursive); the possibility that the function calls are used by the compiler to mark the end of a control flow path, by which point it can determine whether everything has a value; the function genuinely can't exist without allocation. I don't know the reasons but I'd be interested to learn them.

I'm proposing the keyword 'selfless' for a function which could be called before the threshold. It either only uses local properties or properties belonging to the type - never to the 'super' type (in the case of a derived class). It can't call any instance functions which aren't themselves selfless.

Example of use:
class FooView : UIView
{
    var property : Int

    init()
    {
        initialiseProperty()
        super.init()
    }

    init(frame:CGRect)
    {
        initialiseProperty()
        super.init(frame)
    }

    selfless func initialiseProperty()
    {
        property = 4
    }
}

Is this something of interest?

Regards,
Ross O'Brien
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Charles Srstka) #6

That works for simplistic cases such as this. However, if you have a large number of properties that all need to be set, and if their values are all interconnected in some way, dependent on some of the initializer’s parameters, or dependent on some value computed at runtime, this pattern cannot accomplish the task cleanly.

Charles

···

On Dec 15, 2015, at 7:06 PM, Marc Knaup <marc@knaup.koeln> wrote:

In its current state with the initial example -1 from me for the proposal.

The example could easily be written like this:

class FooView: UIView {

    var property = 4

    init() {
        super.init()
    }

    init(frame: CGRect) {
        super.init(frame)
    }
}

In cases where the initially value is computed in a complex way a closure can be used:

class FooView: UIView {

    var property: Int = {
        // some complicated computation
        return value
    }()

    init() {
        super.init()
    }

    init(frame: CGRect) {
        super.init(frame)
    }
}


(Ross O'Brien) #7

Lazy properties can't make use of parameters of the init() without storing
them, and can't calculate parameters to be used in a super.init() (or
decide which super.init() is called, if there are several to choose from).
Part of the idea of the selfless function is to keep local lets/vars at
local scope rather than type scope, and allow functions which perform
calculations in an initialisation to be reusable.
Static functions can perform these calculations but can't set instance
properties.
At the moment, for an init() to use initial parameters without storing
them, all the code for setting properties has to be in the init; I don't
think there's a way to break an init up into a sequence of smaller function
calls, each dealing with a lower level of abstraction of the init. i.e.
something like this:

init(<initial arguments>)
{
    initialiseColor(<subset of arguments>)
    initialiseSize(<subset of arguments>)
    initialiseFont(<subset of arguments>)
}

···

On Wed, Dec 16, 2015 at 1:06 AM, Marc Knaup <marc@knaup.koeln> wrote:

In its current state with the initial example -1 from me for the proposal.

The example could easily be written like this:

class FooView: UIView {

    var property = 4

    init() {
        super.init()
    }

    init(frame: CGRect) {
        super.init(frame)
    }
}

In cases where the initially value is computed in a complex way a closure
can be used:

class FooView: UIView {

    var property: Int = {
        // some complicated computation
        return value
    }()

    init() {
        super.init()
    }

    init(frame: CGRect) {
        super.init(frame)
    }
}

And that value could even delegate computation to static methods of the
class.

In cases where the computation is even more complex and refers to other
properties a lazy var can be used, which even allows the value to refer
to self:

class FooView: UIView {

    private(set) lazy var property: Int = {
        // some complicated computation which can use self
        return value
    }()

    init() {
        super.init()
    }

    init(frame: CGRect) {
        super.init(frame)
    }
}

On Wed, Dec 16, 2015 at 1:30 AM, Charles Srstka via swift-evolution < > swift-evolution@swift.org> wrote:

On Dec 15, 2015, at 5:59 PM, Ross O'Brien via swift-evolution < >> swift-evolution@swift.org> wrote:

Hi all,

I'm a new member of the list, so apologies if this is a duplicate of an
existing idea or if there's already a way to do this in Swift 2.1 that I've
missed.

In Objective C, and C-like languages, an initialiser function represents
a stage after allocation of memory where properties are given values. In
Swift, init appears to precede (or overlap with) allocation. The benefit of
this is that for type-safety reasons, all properties of a type (or new
properties of a derived type) can be verified as having values. The
disadvantage, and one of the stumbling blocks for those who learned
Objective-C, is that until all the properties have values, the instance
does not exist and instance functions cannot be called.

There's an invisible threshold in Swift init() functions marking this
transition. In derived classes it's the point where super.init() is called
- after the derived type has provided initial values, but before any type
functions can be called.

Some types have multiple initialisers, and may be duplicating a lot of
code in those distinct inits before they cross the threshold. This code
can't be refactored into an instance function because the instance doesn't
exist yet. The instance function may not even require the use of any
properties of the type.

If the compiler can read an init function and its varied control flow and
determine a threshold where all properties have values, presumably it can
read the code of any function called before that threshold, determine which
properties they read and which they assign to, and provide a warning if a
path assigns to a constant a second time, etc.. But this isn't currently
happening.

I'm guessing there are multiple contributing factors for this: the
combinatorial explosion of possible control flow paths with functions
(particularly if they're recursive); the possibility that the function
calls are used by the compiler to mark the end of a control flow path, by
which point it can determine whether everything has a value; the function
genuinely can't exist without allocation. I don't know the reasons but I'd
be interested to learn them.

I'm proposing the keyword 'selfless' for a function which could be called
before the threshold. It either only uses local properties or properties
belonging to the type - never to the 'super' type (in the case of a derived
class). It can't call any instance functions which aren't themselves
selfless.

Example of use:
class FooView : UIView
{
    var property : Int

    init()
    {
        initialiseProperty()
        super.init()
    }

    init(frame:CGRect)
    {
        initialiseProperty()
        super.init(frame)
    }

    selfless func initialiseProperty()
    {
        property = 4
    }
}

Is this something of interest?

Regards,
Ross O'Brien
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

+1. This is something that I was planning to propose. It comes up
particularly often in Cocoa objects that implement NSCoding, where you have
to implement both init(coder:) and the designated initializer. Currently,
if you have a bunch of complicated code involved in setting defaults for
your properties, in a manner that’s too complex to solve with simple
default values, you end up with a lot of copy-paste code in the two
initializers, which can easily get out of sync if one is edited without
being diligent about editing the other one in the same way. The exception,
of course, if if you make init(coder:) a convenience initializer, but then
subclasses cannot call super’s implementation of init(coder:), which makes
this unworkable in a lot of circumstances.

I’m not sure “selfless” is the right keyword for this, but some sort of
feature along these lines would be incredibly helpful.

Charles

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


(Marc Knaup) #8

Although I understand your concerns (Charles and Ross) I think an extensive
example is still necessary to properly discuss this proposal. We'd have to
figure out what exactly causes the limitation, if there are alternatives
and if there is no alternative which would render the change unnecessary.
So far this is a very theoretical discussion.

···

On Wed, Dec 16, 2015 at 2:48 AM, Charles Srstka <cocoadev@charlessoft.com> wrote:

On Dec 15, 2015, at 7:06 PM, Marc Knaup <marc@knaup.koeln> wrote:

In its current state with the initial example -1 from me for the proposal.

The example could easily be written like this:

class FooView: UIView {

    var property = 4

    init() {
        super.init()
    }

    init(frame: CGRect) {
        super.init(frame)
    }
}

In cases where the initially value is computed in a complex way a closure
can be used:

class FooView: UIView {

    var property: Int = {
        // some complicated computation
        return value
    }()

    init() {
        super.init()
    }

    init(frame: CGRect) {
        super.init(frame)
    }
}

That works for simplistic cases such as this. However, if you have a large
number of properties that all need to be set, and if their values are all
interconnected in some way, dependent on some of the initializer’s
parameters, or dependent on some value computed at runtime, this pattern
cannot accomplish the task cleanly.

Charles


(ilya) #9

You can do

init(<initial arguments>)
{
    color = initialiseColor(<subset of arguments>)
    size = initialiseSize(<subset of arguments>)
    font = initialiseFont(<subset of arguments>)
}

···

On Wed, Dec 16, 2015 at 04:36 Ross O'Brien via swift-evolution < swift-evolution@swift.org> wrote:

Lazy properties can't make use of parameters of the init() without storing
them, and can't calculate parameters to be used in a super.init() (or
decide which super.init() is called, if there are several to choose from).
Part of the idea of the selfless function is to keep local lets/vars at
local scope rather than type scope, and allow functions which perform
calculations in an initialisation to be reusable.
Static functions can perform these calculations but can't set instance
properties.
At the moment, for an init() to use initial parameters without storing
them, all the code for setting properties has to be in the init; I don't
think there's a way to break an init up into a sequence of smaller function
calls, each dealing with a lower level of abstraction of the init. i.e.
something like this:

init(<initial arguments>)
{
    initialiseColor(<subset of arguments>)
    initialiseSize(<subset of arguments>)
    initialiseFont(<subset of arguments>)
}

On Wed, Dec 16, 2015 at 1:06 AM, Marc Knaup <marc@knaup.koeln> wrote:

In its current state with the initial example -1 from me for the proposal.

The example could easily be written like this:

class FooView: UIView {

    var property = 4

    init() {
        super.init()
    }

    init(frame: CGRect) {
        super.init(frame)
    }
}

In cases where the initially value is computed in a complex way a closure
can be used:

class FooView: UIView {

    var property: Int = {
        // some complicated computation
        return value
    }()

    init() {
        super.init()
    }

    init(frame: CGRect) {
        super.init(frame)
    }
}

And that value could even delegate computation to static methods of the
class.

In cases where the computation is even more complex and refers to other
properties a lazy var can be used, which even allows the value to refer
to self:

class FooView: UIView {

    private(set) lazy var property: Int = {
        // some complicated computation which can use self
        return value
    }()

    init() {
        super.init()
    }

    init(frame: CGRect) {
        super.init(frame)
    }
}

On Wed, Dec 16, 2015 at 1:30 AM, Charles Srstka via swift-evolution < >> swift-evolution@swift.org> wrote:

On Dec 15, 2015, at 5:59 PM, Ross O'Brien via swift-evolution < >>> swift-evolution@swift.org> wrote:

Hi all,

I'm a new member of the list, so apologies if this is a duplicate of an
existing idea or if there's already a way to do this in Swift 2.1 that I've
missed.

In Objective C, and C-like languages, an initialiser function represents
a stage after allocation of memory where properties are given values. In
Swift, init appears to precede (or overlap with) allocation. The benefit of
this is that for type-safety reasons, all properties of a type (or new
properties of a derived type) can be verified as having values. The
disadvantage, and one of the stumbling blocks for those who learned
Objective-C, is that until all the properties have values, the instance
does not exist and instance functions cannot be called.

There's an invisible threshold in Swift init() functions marking this
transition. In derived classes it's the point where super.init() is called
- after the derived type has provided initial values, but before any type
functions can be called.

Some types have multiple initialisers, and may be duplicating a lot of
code in those distinct inits before they cross the threshold. This code
can't be refactored into an instance function because the instance doesn't
exist yet. The instance function may not even require the use of any
properties of the type.

If the compiler can read an init function and its varied control flow
and determine a threshold where all properties have values, presumably it
can read the code of any function called before that threshold, determine
which properties they read and which they assign to, and provide a warning
if a path assigns to a constant a second time, etc.. But this isn't
currently happening.

I'm guessing there are multiple contributing factors for this: the
combinatorial explosion of possible control flow paths with functions
(particularly if they're recursive); the possibility that the function
calls are used by the compiler to mark the end of a control flow path, by
which point it can determine whether everything has a value; the function
genuinely can't exist without allocation. I don't know the reasons but I'd
be interested to learn them.

I'm proposing the keyword 'selfless' for a function which could be
called before the threshold. It either only uses local properties or
properties belonging to the type - never to the 'super' type (in the case
of a derived class). It can't call any instance functions which aren't
themselves selfless.

Example of use:
class FooView : UIView
{
    var property : Int

    init()
    {
        initialiseProperty()
        super.init()
    }

    init(frame:CGRect)
    {
        initialiseProperty()
        super.init(frame)
    }

    selfless func initialiseProperty()
    {
        property = 4
    }
}

Is this something of interest?

Regards,
Ross O'Brien
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

+1. This is something that I was planning to propose. It comes up
particularly often in Cocoa objects that implement NSCoding, where you have
to implement both init(coder:) and the designated initializer. Currently,
if you have a bunch of complicated code involved in setting defaults for
your properties, in a manner that’s too complex to solve with simple
default values, you end up with a lot of copy-paste code in the two
initializers, which can easily get out of sync if one is edited without
being diligent about editing the other one in the same way. The exception,
of course, if if you make init(coder:) a convenience initializer, but then
subclasses cannot call super’s implementation of init(coder:), which makes
this unworkable in a lot of circumstances.

I’m not sure “selfless” is the right keyword for this, but some sort of
feature along these lines would be incredibly helpful.

Charles

_______________________________________________
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


(Colin Cornaby) #10

In UIKIt a view can have two primary methods of initialization: initWithCoder and initWithFrame. initWithCoder is special in that it will not call up to initWithFrame, while any other initializer will use initWthFrame as it’s base initializer.

A common issue is that you might want to set defaults based on runtime information, so you define a common “setup” function that each one of the initializers calls. Again, I haven’t run into this case yet in Swift, but my understanding is that this would not work because the initializers would not be able to call a function on themselves until initialization was complete.

There is one possible way out. NIB outlets are declared implicitly unwrapped to work around this exact problem. A NIB can’t be loaded until after the object’s initialization is complete, and therefore the outlet properties of the object can’t be populated at init time. Any properties that need to have common setup between the two initializers could be declared as implicitly unwrapped. You’d lose some safety, but any issues could probably be quickly caught. This would work around the problem of needing to populate all the properties of the object before calling to the super’s init.

···

On Dec 15, 2015, at 5:51 PM, Marc Knaup via swift-evolution <swift-evolution@swift.org> wrote:

Although I understand your concerns (Charles and Ross) I think an extensive example is still necessary to properly discuss this proposal. We'd have to figure out what exactly causes the limitation, if there are alternatives and if there is no alternative which would render the change unnecessary.
So far this is a very theoretical discussion.

On Wed, Dec 16, 2015 at 2:48 AM, Charles Srstka <cocoadev@charlessoft.com <mailto:cocoadev@charlessoft.com>> wrote:

On Dec 15, 2015, at 7:06 PM, Marc Knaup <marc@knaup.koeln <mailto:marc@knaup.koeln>> wrote:

In its current state with the initial example -1 from me for the proposal.

The example could easily be written like this:

class FooView: UIView {

    var property = 4

    init() {
        super.init()
    }

    init(frame: CGRect) {
        super.init(frame)
    }
}

In cases where the initially value is computed in a complex way a closure can be used:

class FooView: UIView {

    var property: Int = {
        // some complicated computation
        return value
    }()

    init() {
        super.init()
    }

    init(frame: CGRect) {
        super.init(frame)
    }
}

That works for simplistic cases such as this. However, if you have a large number of properties that all need to be set, and if their values are all interconnected in some way, dependent on some of the initializer’s parameters, or dependent on some value computed at runtime, this pattern cannot accomplish the task cleanly.

Charles

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


(Marc Knaup) #11

initWithCoder(_:slight_smile: is exactly such an example you can solve using
initialization closures and/or lazy vars I mentioned earlier.

The only limitation would be that properties must be initialized
independently from each other.
If they really depend on each other then using a struct for them probably
makes sense since that can be initialized as a whole.

···

On Wed, Dec 16, 2015 at 3:00 AM, Colin Cornaby <colin.cornaby@mac.com> wrote:

In UIKIt a view can have two primary methods of initialization:
initWithCoder and initWithFrame. initWithCoder is special in that it will
not call up to initWithFrame, while any other initializer will use
initWthFrame as it’s base initializer.

A common issue is that you might want to set defaults based on runtime
information, so you define a common “setup” function that each one of the
initializers calls. Again, I haven’t run into this case yet in Swift, but
my understanding is that this would not work because the initializers would
not be able to call a function on themselves until initialization was
complete.

There is one possible way out. NIB outlets are declared implicitly
unwrapped to work around this exact problem. A NIB can’t be loaded until
after the object’s initialization is complete, and therefore the outlet
properties of the object can’t be populated at init time. Any properties
that need to have common setup between the two initializers could be
declared as implicitly unwrapped. You’d lose some safety, but any issues
could probably be quickly caught. This would work around the problem of
needing to populate all the properties of the object before calling to the
super’s init.

On Dec 15, 2015, at 5:51 PM, Marc Knaup via swift-evolution < > swift-evolution@swift.org> wrote:

Although I understand your concerns (Charles and Ross) I think an
extensive example is still necessary to properly discuss this proposal.
We'd have to figure out what exactly causes the limitation, if there are
alternatives and if there is no alternative which would render the change
unnecessary.
So far this is a very theoretical discussion.

On Wed, Dec 16, 2015 at 2:48 AM, Charles Srstka <cocoadev@charlessoft.com> > wrote:

On Dec 15, 2015, at 7:06 PM, Marc Knaup <marc@knaup.koeln> wrote:

In its current state with the initial example -1 from me for the proposal.

The example could easily be written like this:

class FooView: UIView {

    var property = 4

    init() {
        super.init()
    }

    init(frame: CGRect) {
        super.init(frame)
    }
}

In cases where the initially value is computed in a complex way a closure
can be used:

class FooView: UIView {

    var property: Int = {
        // some complicated computation
        return value
    }()

    init() {
        super.init()
    }

    init(frame: CGRect) {
        super.init(frame)
    }
}

That works for simplistic cases such as this. However, if you have a
large number of properties that all need to be set, and if their values are
all interconnected in some way, dependent on some of the initializer’s
parameters, or dependent on some value computed at runtime, this pattern
cannot accomplish the task cleanly.

Charles

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


(Marc Knaup) #12

Well lazy vars actually can refer to each other during their initialization.
Just make sure you don't create any cycles or else you'll end up in an
endless recursion.

···

On Wed, Dec 16, 2015 at 3:06 AM, Marc Knaup <marc@knaup.koeln> wrote:

initWithCoder(_:slight_smile: is exactly such an example you can solve using
initialization closures and/or lazy vars I mentioned earlier.

The only limitation would be that properties must be initialized
independently from each other.
If they really depend on each other then using a struct for them probably
makes sense since that can be initialized as a whole.

On Wed, Dec 16, 2015 at 3:00 AM, Colin Cornaby <colin.cornaby@mac.com> > wrote:

In UIKIt a view can have two primary methods of initialization:
initWithCoder and initWithFrame. initWithCoder is special in that it will
not call up to initWithFrame, while any other initializer will use
initWthFrame as it’s base initializer.

A common issue is that you might want to set defaults based on runtime
information, so you define a common “setup” function that each one of the
initializers calls. Again, I haven’t run into this case yet in Swift, but
my understanding is that this would not work because the initializers would
not be able to call a function on themselves until initialization was
complete.

There is one possible way out. NIB outlets are declared implicitly
unwrapped to work around this exact problem. A NIB can’t be loaded until
after the object’s initialization is complete, and therefore the outlet
properties of the object can’t be populated at init time. Any properties
that need to have common setup between the two initializers could be
declared as implicitly unwrapped. You’d lose some safety, but any issues
could probably be quickly caught. This would work around the problem of
needing to populate all the properties of the object before calling to the
super’s init.

On Dec 15, 2015, at 5:51 PM, Marc Knaup via swift-evolution < >> swift-evolution@swift.org> wrote:

Although I understand your concerns (Charles and Ross) I think an
extensive example is still necessary to properly discuss this proposal.
We'd have to figure out what exactly causes the limitation, if there are
alternatives and if there is no alternative which would render the change
unnecessary.
So far this is a very theoretical discussion.

On Wed, Dec 16, 2015 at 2:48 AM, Charles Srstka <cocoadev@charlessoft.com >> > wrote:

On Dec 15, 2015, at 7:06 PM, Marc Knaup <marc@knaup.koeln> wrote:

In its current state with the initial example -1 from me for the
proposal.

The example could easily be written like this:

class FooView: UIView {

    var property = 4

    init() {
        super.init()
    }

    init(frame: CGRect) {
        super.init(frame)
    }
}

In cases where the initially value is computed in a complex way a
closure can be used:

class FooView: UIView {

    var property: Int = {
        // some complicated computation
        return value
    }()

    init() {
        super.init()
    }

    init(frame: CGRect) {
        super.init(frame)
    }
}

That works for simplistic cases such as this. However, if you have a
large number of properties that all need to be set, and if their values are
all interconnected in some way, dependent on some of the initializer’s
parameters, or dependent on some value computed at runtime, this pattern
cannot accomplish the task cleanly.

Charles

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


(Colin Cornaby) #13

Yeah, thinking this one through… The use case below also mentioned the possibility of a setup function that had parameters to base it’s setup on. A lazy loading variable doesn’t necessarily cleanly replace that sort of usage without adding more variables to hold the parameters for lazy initialization… But I’m not sure if that’s realistic. Anything that’s part of an initWithCoder serialization /deserialization pattern is probably going to be a variable on the object anyway. Maybe someone else can come up with a better counterexample.

At least this list is a great way to touch on all the different corners of Swift. :slight_smile:

···

On Dec 15, 2015, at 6:14 PM, Marc Knaup <marc@knaup.koeln> wrote:

Well lazy vars actually can refer to each other during their initialization.
Just make sure you don't create any cycles or else you'll end up in an endless recursion.

On Wed, Dec 16, 2015 at 3:06 AM, Marc Knaup <marc@knaup.koeln <mailto:marc@knaup.koeln>> wrote:
initWithCoder(_:slight_smile: is exactly such an example you can solve using initialization closures and/or lazy vars I mentioned earlier.

The only limitation would be that properties must be initialized independently from each other.
If they really depend on each other then using a struct for them probably makes sense since that can be initialized as a whole.

On Wed, Dec 16, 2015 at 3:00 AM, Colin Cornaby <colin.cornaby@mac.com <mailto:colin.cornaby@mac.com>> wrote:
In UIKIt a view can have two primary methods of initialization: initWithCoder and initWithFrame. initWithCoder is special in that it will not call up to initWithFrame, while any other initializer will use initWthFrame as it’s base initializer.

A common issue is that you might want to set defaults based on runtime information, so you define a common “setup” function that each one of the initializers calls. Again, I haven’t run into this case yet in Swift, but my understanding is that this would not work because the initializers would not be able to call a function on themselves until initialization was complete.

There is one possible way out. NIB outlets are declared implicitly unwrapped to work around this exact problem. A NIB can’t be loaded until after the object’s initialization is complete, and therefore the outlet properties of the object can’t be populated at init time. Any properties that need to have common setup between the two initializers could be declared as implicitly unwrapped. You’d lose some safety, but any issues could probably be quickly caught. This would work around the problem of needing to populate all the properties of the object before calling to the super’s init.

On Dec 15, 2015, at 5:51 PM, Marc Knaup via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Although I understand your concerns (Charles and Ross) I think an extensive example is still necessary to properly discuss this proposal. We'd have to figure out what exactly causes the limitation, if there are alternatives and if there is no alternative which would render the change unnecessary.
So far this is a very theoretical discussion.

On Wed, Dec 16, 2015 at 2:48 AM, Charles Srstka <cocoadev@charlessoft.com <mailto:cocoadev@charlessoft.com>> wrote:

On Dec 15, 2015, at 7:06 PM, Marc Knaup <marc@knaup.koeln <mailto:marc@knaup.koeln>> wrote:

In its current state with the initial example -1 from me for the proposal.

The example could easily be written like this:

class FooView: UIView {

    var property = 4

    init() {
        super.init()
    }

    init(frame: CGRect) {
        super.init(frame)
    }
}

In cases where the initially value is computed in a complex way a closure can be used:

class FooView: UIView {

    var property: Int = {
        // some complicated computation
        return value
    }()

    init() {
        super.init()
    }

    init(frame: CGRect) {
        super.init(frame)
    }
}

That works for simplistic cases such as this. However, if you have a large number of properties that all need to be set, and if their values are all interconnected in some way, dependent on some of the initializer’s parameters, or dependent on some value computed at runtime, this pattern cannot accomplish the task cleanly.

Charles

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