Read-only computed properties returning constants

I see that constants in the standard library and other Apple libraries are defined like this:

public var M_PI: Double { get }

rather than eg:

public let M_PI: Double = 3.1415...


and like this:

public struct UInt8 : ... {
    public static var max: UInt8 { get }
}

rather than eg:

public struct UInt8 : ... {
    public static let max: UInt8 = 255
}


Why is that?


How is a read-only computed property that returns a constant different from a simple let?

That is, how is constantA different from constantB:

var constantA: Int { return 1234 }
let constantB = 1234

?


(I'd like to know about any and all differences, even subtle ones, and also global vs struct, class, function scope, debug vs release builds, etc.)

Answered by SevenTenEleven in 40975022

The promise of 'let' is that the value will not change during its lifetime. For globals, that's the entire life of the program; for properties, that's the life of the containing object. A get-only 'var' is allowed to return a different value every time.


In practice it would probably be fine for most constants coming from C to be imported using 'let', but the compiler currently doesn't differentiate.

Interesting observation. Yeah, I wonder why it's that way too. Maybe it's more preferable from a compiler or compatibility standpoint? I really don't know…it's not exactly like the value of pi or UInt8.max will ever change!


Is someone from the Swift team available to help us out?

My completely non-authoritative guess about M_PI would be that since Swift doesn't have a preprocessor, and certainly not a C preprocessor, maybe Apple's code to generate the modules creates little Swift functions for each #define, so that Swift code can call them. I'm not sure how else Swift would be able to access #defines at all, since those could contain who knows what (Obj-)C code.


As UInt8.max, that particular value will almost certainly never change, but others (like Int.max), for example, may. I can understand why they'd want to leave the sizes of types open — .max is basically the same sort of thing as sizeof, whose entire purpose is so that you don't have to hardcode sizes of things.

While the constants you’ve referenced are all C macros, and thus the values are right there in the C header, there are a lot of constants exported by the system where that’s not the case. For example,

UIApplicationDidEnterBackgroundNotification
. The Swift import style makes perfect sense for those constants.

ps I’m not on the Swift team, so can’t give you an authoritative answer to ‘why’ questions.

Share and Enjoy

Quinn "The Eskimo!"
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Oh yeah, I didn't think about those. Good point.

Thanks, I guess my remaining question could be simplified to:


Are there any benefits of declaring constants like constantA rather than constantB

var constantA: Int { return 1234 }
let constantB = 1234

?


If we consider eg struct scope (rather than global scope), then I guess one obvious difference is that a stored property will be just that, ie it will occupy some space in the struct (which a computed property will not). But declaring the stored property as static would make that difference disappear.

If you talk about a single module scope (like an application) you should use the simpler `let` variant, as there is no good reason to use computed property.


On the other hand, if your constant crosses module boundary (if it is a public constant exposed from a framework) then you should use computed property.


In the latter scenario, the benefit of the computed property is avoiding the dependance of client code to the actual value of your constant instead of its symbolic representation.


If you use `let`, the compiler that is compiling the client code will see the value and will inline it. This means that if you change the value of that constant in the future, the existing compiled client code will break and stop working with a new version of your framework.


As you see, there is a very good reason that public constants are coded that way. To avoid the possibility of this type of error, I think the compiler should automatically always expose any public constant as computed property regardless of how you actually declare it. (Is it doing this already?)

Interesting, is this documented in any way anywhere?

Accepted Answer

The promise of 'let' is that the value will not change during its lifetime. For globals, that's the entire life of the program; for properties, that's the life of the containing object. A get-only 'var' is allowed to return a different value every time.


In practice it would probably be fine for most constants coming from C to be imported using 'let', but the compiler currently doesn't differentiate.

The thing about #defines like M_PI is that because they're preprocessor macros, they're meant to be always inlined. The C/Objective-C clients for whom these constants were intended will be inlining them, because they're handled by the preprocessor.


It's also possible to define 'let' constants without revealing to callers what they are; things like NSURLNameKey that are declared extern NSString * const just show up as


public let NSURLNameKey: String


with no shown value for the compiler to inline. So, I think the most likely explanation is a technical reason.


Edit: Never mind, looks like SevenTenEleven already gave a canonical answer below (which seems to indeed be a technical reason; the compiler currently isn't differentiating between macros that are true constants and ones that might change during a program's runtime).

Read-only computed properties returning constants
 
 
Q