Why swift uses let keyword for constants?

Hi Team,

I'd like to know the decision behind using let key word for constants. I come from JavaScript background and in JavaScript we use const key word for a constant. I find the use of let keyword a bit unintuitive but at the same time I believe Swift team has a strong reason for using let keyword for constants.

Thanks,

Arun

2 Likes

Reaching into the haze of my memory (I couldn't find the very old thread where I remember this being talked about) I think it had something to do with let and var being the same length, so multiple variable declarations would line up nicely.

The CHANGELOG.md entry where immutability was introduced starts with let and doesn't revisit the name: swift/CHANGELOG.md at main · swiftlang/swift · GitHub

5 Likes

It may well be no more complicated than @harlanhaskins said, but there's also a somewhat theoretical (in the sense of: it makes no real practical difference) distinction between let and a "constant".

let's contract is that it's a variable whose value is compile-time-guaranteed not to change when accessed at run time.

That's a slightly stronger requirement than a variable whose value does not change, and a slightly different semantic than a variable whose value is known at compile time.

For example, when compiling for an executable that has read-only and read-write segments, a let variable cannot necessarily be placed in a read-only segment, since its value may need to be calculated at run-time.

4 Likes

The term originates from mathematics, then was borrowed by a number of programming languages in the past and became a term of art in computer science as well. https://math.stackexchange.com/questions/5084128/what-does-the-word-let-mean-in-mathematics

I think we also considered val at one point, but val and var were too easy to misread as each other at a glance.

15 Likes

For a very brief period (around two weeks), let was even replaced with val, with the following accompanying entry in the changelog:

We are renaming the let keyword to val. The let keyword didn't work out primarily because it is not a noun, so "defining a let" never sounded right. We chose val over const and other options because var and val have similar semantics (making syntactic similarity useful), because const has varied and sordid connotations in C that we don't want to bring over, and because we don't want to punish the "preferred" case with a longer keyword.

7 Likes

That "punish the preferred case" was a real consideration! We knew the initial audience for Swift was going to be people used to Objective-C, used to having mutability by default. We wanted them to explore getting away from that without feeling like it would be easier to "just use var". const is only two more letters, but you type let all the time in Swift, and so having it be the same length as var felt like it could end up make-or-break for tipping the scales towards immutability. We didn't know for sure, but we didn't want to sabotage ourselves. (At the same time, we also didn't feel like going as compact as Go's :=—they didn't invent this but they were the most mainstream language to use it at the time—and felt that having some keyword introducer for new names was still a good idea.) The rest is history.

By the way, JavaScript didn't have this option for naming, because JavaScript's introduction of let (more desirable scoping semantics than var) happened separately from and well before the introduction of const (enforcing immutability, with the same good scoping as let). But you can see in Swift's contemporaries the same kind of logic is there: Kotlin did go with val and var, and Rust (which was using let before Swift was) did decide to "punish" mutability by spelling it let mut and not have a short var form.

19 Likes

Maybe, Swift should admit the const into the fold, making it possible to define immutable variables, just like let variables, but which must be initialised at the point of declaration. However, unlike let immutable variables, their memory footprint should be the same as if they were declared as static variables.

struct Foo {
   const u: Int = 32
   const v: Int = 64
}

print (MemoryLayout <Foo>.stride)
// prints 1

let p = Foo ()
print (p.u)
// prints 32

// Contrast with this
struct Bar {
   let u: Int = 32
   let v: Int = 64
   static let sv: Int = 64
}

print (MemoryLayout <Bar>.stride)
// prints 16

let q = Bar ()
print (q.sv) // error: static member 'sv' cannot be used on instance of type 'Bar'

1 Like

I think that is the point of the new literal expressions, ie manifest constants.

1 Like

Had a very quick look. I am not entirely sure it is the same thing. const is basically let less the memory requirement per instance.

Why is static let the wrong tool for that?

6 Likes

Not the wrong tool; it is just that it forces one to spell out the type explicitly.

struct Foo {
   static let u: Int = 3
}

extension Foo {
   func go () {
      print (Self.u)
      print (Foo.u)
      print (u) // error: static member 'u' cannot be used on instance of type 'Foo'
   }
}

On the bright side you may have the same name u as both static and instance member..
Self.u is a good compromise to not spell the type name explicitly.

2 Likes

I'm thinking that we already have type alias as defined below. Similarly we can have an alias for the keyword let so that while passing the code to the compiler, it replaces const with let in the AST and helps in backward compatibility.

typealias StudentID = String

You may also use dynamic property var u: Int { 3 } - that won't take space and doesn't require type/Self prefixing.

We only have typealias... No keywordalias that would be required here to redefine let into const, and not even a closer concept of varalias / funcalias (e.g. to be able calling func foo() via the name bar())

1 Like

We do have that in several forms depending on where the caller lives, it's just not spelled that way:

func bar() { foo() }
@abi(func bar()) func foo() {}
@_silgen_name("bar") func foo() {}
@c("bar") func foo() {}

Though I suppose that's not what you mean. :slightly_smiling_face:

1 Like

For Self, I picked = for constants and <- for variables, FWIW.

Someday, someone will design a language that, instead of var" uses "Schrödinger".

Remind me to pick your brain about those!