[Pitch] Computed properties as aliases


(Harshil Shah) #1

Hi all,

The idea behind this is to add a shorter, cleaner syntax for the common pattern of using computed properties to provide public API for properties of private variables.

For example:

public var foo: Type {
    get { return privateBar.baz }
    set { privateBar.baz = newValue }
}

While this is a great improvement over the previous convention of `someVar:` and `setSomeVar:` as found in a lot of UIKit, it still has some issues.

It is overly verbose for the task at hand. It requires specifying the type of property that the variable is effectively acting as an alias for, stating the property name twice, and also doesn’t preclude the possibility of mistakenly using `privateBar.baz = foo` in the setter, which looks correct at a glance but just assigns the existing value of `baz` back to it, effectively doing nothing.

It could potentially be shortened, as follows:

public alias var foo = privateBar.baz

This new syntax omits additional type information which could be inferred from the type of the original variable, prevents any assignment mistakes in the setter, reduces the number of lines of code needed, and makes it clear that the purpose of the variable is simply to act as an alias.

I have used the `alias` keyword above as a placeholder. I suppose there might be needed some way to clarify that `foo` is still a computed property, and not a stored property declared as having the value of `privateBar.baz`. I realise that introducing a new keyword is not a trivial change, however this was simply the best idea I could come up with; I’m sure the community can find better solutions.

At the same time, a new keyword makes the intention of the variable explicit, and would break no Swift 3.* code.

I realise this is just syntactic sugar, however the pattern is common enough that I feel like it merits some discussion.

– Harshil


(Xiaodi Wu) #2

It's reasonable to ask for sugar when a common pattern is unusually
verbose, but I'd like to explore if that's really the case here.

I've had occasion to use a public computed property which returns a private
stored property, but it's _never_ been only that. There's usually some
tricky validation going on or something else that prevents me from just
making the private property `public` or `public internal(set)`. It seems
you're showing one such case where the stored property is in fact stored in
some other variable of a different type. Is it actually very common? In
what sort of programming?

···

On Thu, May 25, 2017 at 10:08 AM, Harshil Shah via swift-evolution < swift-evolution@swift.org> wrote:

Hi all,

The idea behind this is to add a shorter, cleaner syntax for the common
pattern of using computed properties to provide public API for properties
of private variables.

For example:

public var foo: Type {
    get { return privateBar.baz }
    set { privateBar.baz = newValue }
}

While this is a great improvement over the previous convention of
`someVar:` and `setSomeVar:` as found in a lot of UIKit, it still has some
issues.

It is overly verbose for the task at hand. It requires specifying the type
of property that the variable is effectively acting as an alias for,
stating the property name twice, and also doesn’t preclude the possibility
of mistakenly using `privateBar.baz = foo` in the setter, which looks
correct at a glance but just assigns the existing value of `baz` back to
it, effectively doing nothing.

It could potentially be shortened, as follows:

public alias var foo = privateBar.baz

This new syntax omits additional type information which could be inferred
from the type of the original variable, prevents any assignment mistakes in
the setter, reduces the number of lines of code needed, and makes it clear
that the purpose of the variable is simply to act as an alias.

I have used the `alias` keyword above as a placeholder. I suppose there
might be needed some way to clarify that `foo` is still a computed
property, and not a stored property declared as having the value of
`privateBar.baz`. I realise that introducing a new keyword is not a trivial
change, however this was simply the best idea I could come up with; I’m
sure the community can find better solutions.

At the same time, a new keyword makes the intention of the variable
explicit, and would break no Swift 3.* code.

I realise this is just syntactic sugar, however the pattern is common
enough that I feel like it merits some discussion.


(Charles Srstka) #3

I’ve used this pattern on occasion to expose properties of value types to KVO:

class SomeObservableThing: NSObject {
  var someValueType: SomeValueType {
    willSet {
      self.willChangeValue(forKey: #keyPath(foo))
      self.willChangeValue(forKey: #keyPath(bar))
    }
    didSet {
      self.didChangeValue(forKey: #keyPath(foo))
      self.didChangeValue(forKey: #keyPath(bar))
    }
  }

  @objc private static let automaticallyNotifiesObserversOfFoo = false
  @objc var foo: Foo {
    get { return self.someValueType.foo }
    set { self.someValueType.foo = newValue }
  }

  @objc private static let automaticallyNotifiesObserversOfBar = false
  @objc var bar: Bar {
    get { return self.someValueType.bar }
    set { self.someValueType.bar = newValue }
  }
}

The accessors on foo and bar aren’t really the most obnoxious bits of boilerplate here, though.

Charles

···

On May 25, 2017, at 1:34 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

On Thu, May 25, 2017 at 10:08 AM, Harshil Shah via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Hi all,

The idea behind this is to add a shorter, cleaner syntax for the common pattern of using computed properties to provide public API for properties of private variables.

For example:

public var foo: Type {
    get { return privateBar.baz }
    set { privateBar.baz = newValue }
}

While this is a great improvement over the previous convention of `someVar:` and `setSomeVar:` as found in a lot of UIKit, it still has some issues.

It is overly verbose for the task at hand. It requires specifying the type of property that the variable is effectively acting as an alias for, stating the property name twice, and also doesn’t preclude the possibility of mistakenly using `privateBar.baz = foo` in the setter, which looks correct at a glance but just assigns the existing value of `baz` back to it, effectively doing nothing.

It could potentially be shortened, as follows:

public alias var foo = privateBar.baz

This new syntax omits additional type information which could be inferred from the type of the original variable, prevents any assignment mistakes in the setter, reduces the number of lines of code needed, and makes it clear that the purpose of the variable is simply to act as an alias.

I have used the `alias` keyword above as a placeholder. I suppose there might be needed some way to clarify that `foo` is still a computed property, and not a stored property declared as having the value of `privateBar.baz`. I realise that introducing a new keyword is not a trivial change, however this was simply the best idea I could come up with; I’m sure the community can find better solutions.

At the same time, a new keyword makes the intention of the variable explicit, and would break no Swift 3.* code.

I realise this is just syntactic sugar, however the pattern is common enough that I feel like it merits some discussion.

It's reasonable to ask for sugar when a common pattern is unusually verbose, but I'd like to explore if that's really the case here.

I've had occasion to use a public computed property which returns a private stored property, but it's _never_ been only that. There's usually some tricky validation going on or something else that prevents me from just making the private property `public` or `public internal(set)`. It seems you're showing one such case where the stored property is in fact stored in some other variable of a different type. Is it actually very common? In what sort of programming?


(Hooman Mehr) #4

This is common when you are using façade design pattern. You use a façade to flatten and hide the internal composition of a composite object and present it as a single flat object. In some types of projects this comes up rather frequently. Both in client / UI programming and server side / business logic.

···

On May 25, 2017, at 11:34 AM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

Is it actually very common? In what sort of programming?


(Charles Srstka) #5

You know, what I would like to see instead of this would be a revival of the ‘automatic protocol forwarding’ concept that was pitched here very early on in the swift-evolution process:

https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151228/004737.html

This would be a much more flexible way to solve the same problem, and could also solve some problems that we used to use methods like forwardingTargetForSelector: for in Objective-C.

Charles

···

On May 25, 2017, at 9:26 PM, Hooman Mehr via swift-evolution <swift-evolution@swift.org> wrote:

This is common when you are using façade design pattern. You use a façade to flatten and hide the internal composition of a composite object and present it as a single flat object. In some types of projects this comes up rather frequently. Both in client / UI programming and server side / business logic.


(Harshil Shah) #6

Personally I write a lot of UI code, and so the pattern is used quite a lot to expose certain properties, for example a button's `title` and `textColor`.

Oftentimes, the view hierarchy itself is complicated enough that exposing views directly is not feasible, so a host of such aliases are needed to offer a usable public API.

– Harshil

···

On 26-May-2017, at 1:04 AM, Charles Srstka <cocoadev@charlessoft.com> wrote:

On May 25, 2017, at 1:34 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

On Thu, May 25, 2017 at 10:08 AM, Harshil Shah via swift-evolution <swift-evolution@swift.org> wrote:
Hi all,

The idea behind this is to add a shorter, cleaner syntax for the common pattern of using computed properties to provide public API for properties of private variables.

For example:

public var foo: Type {
    get { return privateBar.baz }
    set { privateBar.baz = newValue }
}

While this is a great improvement over the previous convention of `someVar:` and `setSomeVar:` as found in a lot of UIKit, it still has some issues.

It is overly verbose for the task at hand. It requires specifying the type of property that the variable is effectively acting as an alias for, stating the property name twice, and also doesn’t preclude the possibility of mistakenly using `privateBar.baz = foo` in the setter, which looks correct at a glance but just assigns the existing value of `baz` back to it, effectively doing nothing.

It could potentially be shortened, as follows:

public alias var foo = privateBar.baz

This new syntax omits additional type information which could be inferred from the type of the original variable, prevents any assignment mistakes in the setter, reduces the number of lines of code needed, and makes it clear that the purpose of the variable is simply to act as an alias.

I have used the `alias` keyword above as a placeholder. I suppose there might be needed some way to clarify that `foo` is still a computed property, and not a stored property declared as having the value of `privateBar.baz`. I realise that introducing a new keyword is not a trivial change, however this was simply the best idea I could come up with; I’m sure the community can find better solutions.

At the same time, a new keyword makes the intention of the variable explicit, and would break no Swift 3.* code.

I realise this is just syntactic sugar, however the pattern is common enough that I feel like it merits some discussion.

It's reasonable to ask for sugar when a common pattern is unusually verbose, but I'd like to explore if that's really the case here.

I've had occasion to use a public computed property which returns a private stored property, but it's _never_ been only that. There's usually some tricky validation going on or something else that prevents me from just making the private property `public` or `public internal(set)`. It seems you're showing one such case where the stored property is in fact stored in some other variable of a different type. Is it actually very common? In what sort of programming?

I’ve used this pattern on occasion to expose properties of value types to KVO:

class SomeObservableThing: NSObject {
  var someValueType: SomeValueType {
    willSet {
      self.willChangeValue(forKey: #keyPath(foo))
      self.willChangeValue(forKey: #keyPath(bar))
    }
    didSet {
      self.didChangeValue(forKey: #keyPath(foo))
      self.didChangeValue(forKey: #keyPath(bar))
    }
  }

  @objc private static let automaticallyNotifiesObserversOfFoo = false
  @objc var foo: Foo {
    get { return self.someValueType.foo }
    set { self.someValueType.foo = newValue }
  }

  @objc private static let automaticallyNotifiesObserversOfBar = false
  @objc var bar: Bar {
    get { return self.someValueType.bar }
    set { self.someValueType.bar = newValue }
  }
}

The accessors on foo and bar aren’t really the most obnoxious bits of boilerplate here, though.

Charles


(Hooman Mehr) #7

Agreed.

Actually, I was thinking about it while writing my response and wanted to go back and dig that thread to refresh my memory and bring it up.

Thank you for taking care of that.

···

On May 25, 2017, at 7:33 PM, Charles Srstka <cocoadev@charlessoft.com> wrote:

On May 25, 2017, at 9:26 PM, Hooman Mehr via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

This is common when you are using façade design pattern. You use a façade to flatten and hide the internal composition of a composite object and present it as a single flat object. In some types of projects this comes up rather frequently. Both in client / UI programming and server side / business logic.

You know, what I would like to see instead of this would be a revival of the ‘automatic protocol forwarding’ concept that was pitched here very early on in the swift-evolution process:

https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151228/004737.html

This would be a much more flexible way to solve the same problem, and could also solve some problems that we used to use methods like forwardingTargetForSelector: for in Objective-C.

Charles