Proposal: remove the requirement for `Self.` when referring to static members

(Apologies if this has been discussed before.)

The requirement to use Self. for static members when using them in instance members seems unnecessary and in my opinion only adds noise.

The older-school OOP languages allowed direct reference to static members without the additional qualifiers (Pascal, C++ and Java come to mind, but I'm sure there are more).

For example, every time I need some private constant within a class or struct, I hate the Self. so much that end up moving the constant outside of my class/struct to reduce the noise in the code, i.e.:

private let Height = 44.0

struct MyButton: View {
    var body: some View {
        // ...
        .frame(height: Height)
    }
}

which itself isn't very nice since Height is supposed to be used only inside MyButton. Wouldn't it be great if I could define it inside MyButton as static let and use directly?

Philosophically speaking, it is not the caller's concern whether something is static or instance-tied; I think it's why the earlier language designers allowed static calls without qualifiers.

Does anyone feel the same way about Self.?

It has. You can use the search function to find those threads.

It sounds like you're describing an instance property. Why not make it one? In Swift, static doesn't mean constant: it means that it's a property of the type.

Of course it is: again, a static property means that it's a property of the type, not of its instances. It doesn't mean something is constant.

I tried and couldn't find anything. Can you help me find a similar topic?

But that's exactly what I'm talking about: a static property would force me to use Self. which I'm proposing to remove as a requirement. Instance properties take extra memory with each instance which you normally want to avoid for things that don't change.

You can use a search term such as "self static member" to pull up a number of relevant threads. For example, this one which also nicely links out to other related ones.

Instance properties can be either stored or computed. And Swift has decided that that's not a caller concern whether a property is stored or computed. Since you've described an instance property that you don't want to be stored, you can write a computed instance property.

In many languages, the typename is than used, not self, so that multiple types can have the same language and static, type owned values, are accessible with defined reference levels. It would seem that if it was referenced through the Type name, and the Type name was in the tree of visibility, that this would work with just the static property name in the type, and outside of the type, with the type name as is already the case in many other languages.

It's a possibility but using a computed property, i.e. a function in the end, for something that's let's say supposed to be a compile time constant seems like overkill.

That's exactly how we define—static and instance—constants in the standard library:

extension UnsignedInteger where Self: FixedWidthInteger {
  // ...
  public static var min: Self { return 0 }
}
extension Double: BinaryFloatingPoint {
  // ...
  @inlinable
  public static var pi: Double {
    return 0x1.921fb54442d18p1
  }
}
3 Likes

I think we are talking about different things.

I'm proposing to make the Self. qualifier optional when used within the context of the same class or struct, just like self. is optional in similar situations.

So for the example I showed in the post, it would be:

struct MyButton: View {
    private static let Height = 44.0

    var body: some View {
        // ...
        .frame(height: Height)
    }
}

Currently the above will require Self.Height even though in case of an instant property self. would be optional.

I'm saying it's unnecessarily adds noise to the code. C++ and Java (and possibly some other OOP languages too) don't require the full class qualifier in similar situations.

1 Like

Right, and I'm saying that there's already a way of expressing this property in the language that gets you what you want, which is to delete static and make this a computed property.

Put another way, you're proposing a language change that would have a static property behave like an instance property, and I'm exploring your motivation for doing so given that the code you write can just...be an instance property.

I'm not sure what you mean by "behave like instance property" but a static property remains a static property, just that syntactic access to it is simplified by dropping the requirement for Self.

And again, yes, a computed instance property would allow me to use it directly but I'm not comfortable with using an instance thing for something that is constant by nature.

Then there's a broader question of why Self. is required while self. is not. I wonder if there's a reason for that in Swift other than "you need to know you are calling a static function or using a static property". My argument here is that other OOP languages do not require it and it's never a problem there.

1 Like

This is the underlying question you’re actually asking: why doesn’t bare name lookup find type-level members of the enclosing type, as it does in most other OOP languages? While Swift often prefers lexical name resolution, that isn’t the reason here because even type members declared in enclosing scope are not found without a preceding Self..

3 Likes

I don't understand this assertion. It cannot both be true that you don't feel that there should be a user-facing distinction between a static or instance property and also that your particular example shouldn't be an instance property because it "is constant by nature."

Indeed, I'm arguing that both assertions are false: there is a semantic, user-facing distinction between static and instance properties, and your example is appropriately an example of the latter.

Again, static doesn't mean constant: it means that it's a property of the type rather than its instances.

For example, Double.pi is not appropriate as an instance property, not because it is a constant, but because it is not a property of a Double value: it makes no sense to write (4.0 as Double).pi. By contrast, both Int.bitWidth and (42 as Int).bitWidth exist because both the type has a fixed bit width and each instance of it has some bit width as well (which, of course, has to be fixed).

In your example, while all instances of MyButton may have the same height, this is of no consequence when you invoke frame(height:) inside body: surely, you want to pass along the height of this specific instance whether or not every other MyButton also has the same height.

If, for some reason, you also need to write an algorithm (say, a generic one over all UI elements with fixed height: I can see this being useful, say, for a scrollable container that allows only homogeneous elements) that works with the fixed height of MyButton without having an instance in hand, then you can appropriately declare a static property in addition (just like Int.bitWidth), but that does not mean that the height of a button is somehow not also a property of MyButton instances.

Because there is a user-facing, semantic difference between static and instance properties.

Unless I'm mistaken, those other OOP languages do not allow static and instance properties to have the same name.

3 Likes

Similarly there is a huge semantic difference between global functions and instance methods and yet both are invoked "bare". Honestly the choice of requirements seems arbitrary, however:

aha, so that's where the problem lies and I think it originates from the ObjC UIKit API's. Now I realize that because there are e.g. both instance and static variants of viewDidLoad and many other (all?) methods in UIKit, it is probably why Swift requires Self.. I believe there may be a solution though it will likely break some code.

There definitely aren’t. It would make no sense to have a +[UIViewController viewDidLoad] method.

This however exists:

class MyView: UIView { 
	override class func awakeFromNib() {
	}

	override func awakeFromNib() {
	}
}

Yes, because -awakeFromNib is defined in a category on NSObject, and class objects conform to NSObject. There is no +awakeFromNib declaration.

1 Like

Right, so back to the original problem, a call to awakeFromNib() within an instance context could select the instance one by default unless called with Self.. But where there's no ambiguity Self. could be made optional. In fact the change shouldn't break any older UIKit code (I think).

And that sometimes causes problems for which we have an ad hoc solution of slapping the attribute @warn_unqualified_access on certain confusable members. We do not want to create more such problems when the solution to the motivating problem is just spelling a property of an instance as an actual instance property.

And remember that it is not just a problem when perfectly written code is ambiguous to the compiler; code that is easily confused by a human reader, even if unambiguous to the compiler, as well as invalid code for which it becomes impossible to emit helpful diagnostics are problems too.

Both instance and static methods can be invoked without qualification in specific contexts. Instance members (other than those marked with the attribute above) can be invoked without explicitly qualifying self specifically in the implementation of another instance member, and static members similarly in the implementation of another static member.

5 Likes

For the record, this is the same rule as Java, which also allows static and instance members to have the same name.

1 Like

A thousand times this! Especially if static properties are mutable it can be very confusing to deduce what a function is doing, changing state of an instance or the type entirely. When this relates to limited resources like file descriptors and the like this can really suck.
IIRC most source code color schemes for Java in e.g. JetBrains specifically use different colors to distinguish between instance variables and static ones to help you avoid confusion. That's a bandaid on the language's less than safe choice here, and I'd say it's a bad thing if you have to rely on IDEs to help out here and fix questionable syntax choices.

@crontab, I don't understand why you would feel uncomfortable with the solution @xwu suggested for your desire to leave out the qualifying Self.. Instance variables bring you the option to leave out self. already, so if you turn what in your case are basically constants defined on the type to be used by instances into actual (calculated) instance variables, you get exactly the behavior that you want. I wager that deep down the machine code isn't even different as the optimizer can probably determine that it's basically a constant.

If all your reason to not want to do that is being uncomfortable I don't think this proposal will find much agreement. There's going to be enough people who are as uncomfortable with introducing ambiguity between instance and type properties and to be frank I think that's a stronger point.

1 Like