How exactly does the "let" keyword work in Swift? - constants

I've read this simple explanation in the guide:
The value of a constant doesn’t need to be known at compile time, but you must assign it a value exactly once.
But I want a little more detail than this. If the constant references an object, can I still modify its properties? If it references a collection, can I add or remove elements from it? I come from a C# background; is it similar to how readonly works (apart from being able to use it in method bodies), and if it's not, how is it different?

let is a little bit like a const pointer in C. If you reference an object with a let, you can change the object's properties or call methods on it, but you cannot assign a different object to that identifier.
let also has implications for collections and non-object types. If you reference a struct with a let, you cannot change its properties or call any of its mutating func methods.
Using let/var with collections works much like mutable/immutable Foundation collections: If you assign an array to a let, you can't change its contents. If you reference a dictionary with let, you can't add/remove key/value pairs or assign a new value for a key — it's truly immutable. If you want to assign to subscripts in, append to, or otherwise mutate an array or dictionary, you must declare it with var.
(Prior to Xcode 6 beta 3, Swift arrays had a weird mix of value and reference semantics, and were partially mutable when assigned to a let -- that's gone now.)

It's best to think of let in terms of Static Single Assignment (SSA) -- every SSA variable is assigned to exactly once. In functional languages like lisp you don't (normally) use an assignment operator -- names are bound to a value exactly once. For example, the names y and z below are bound to a value exactly once (per invocation):
func pow(x: Float, n : Int) -> Float {
if n == 0 {return 1}
if n == 1 {return x}
let y = pow(x, n/2)
let z = y*y
if n & 1 == 0 {
return z
}
return z*x
}
This lends itself to more correct code since it enforces invariance and is side-effect free.
Here is how an imperative-style programmer might compute the first 6 powers of 5:
var powersOfFive = Int[]()
for n in [1, 2, 3, 4, 5, 6] {
var n2 = n*n
powersOfFive += n2*n2*n
}
Obviously n2 is is a loop invariant so we could use let instead:
var powersOfFive = Int[]()
for n in [1, 2, 3, 4, 5, 6] {
let n2 = n*n
powersOfFive += n2*n2*n
}
But a truly functional programmer would avoid all the side-effects and mutations:
let powersOfFive = [1, 2, 3, 4, 5, 6].map(
{(num: Int) -> Int in
let num2 = num*num
return num2*num2*num})

Let
Swift uses two basic techniques to store values for a programmer to access by using a name: let and var. Use let if you're never going to change the value associated with that name. Use var if you expect for that name to refer to a changing set of values.
let a = 5 // This is now a constant. "a" can never be changed.
var b = 2 // This is now a variable. Change "b" when you like.
The value that a constant refers to can never be changed, however the thing that a constant refers to can change if it is an instance of a class.
let a = 5
let b = someClass()
a = 6 // Nope.
b = someOtherClass() // Nope.
b.setCookies( newNumberOfCookies: 5 ) // Ok, sure.
Let and Collections
When you assign an array to a constant, elements can no longer be added or removed from that array. However, the value of any of that array's elements may still be changed.
let a = [1, 2, 3]
a.append(4) // This is NOT OK. You may not add a new value.
a[0] = 0 // This is OK. You can change an existing value.
A dictionary assigned to a constant can not be changed in any way.
let a = [1: "Awesome", 2: "Not Awesome"]
a[3] = "Bogus" // This is NOT OK. You may not add new key:value pairs.
a[1] = "Totally Awesome" // This is NOT OK. You may not change a value.
That is my understanding of this topic. Please correct me where needed. Excuse me if the question is already answered, I am doing this in part to help myself learn.

First of all, "The let keyword defines a constant" is confusing for beginners who are coming from C# background (like me). After reading many Stack Overflow answers, I came to the conclusion that
Actually, in swift there is no concept of constant
A constant is an expression that is resolved at compilation time. For both C# and Java, constants must be assigned during declaration:
public const double pi = 3.1416; // C#
public static final double pi = 3.1416 // Java
Apple doc ( defining constant using "let" ):
The value of a constant doesn’t need to be known at compile time, but you must assign the value exactly once.
In C# terms, you can think of "let" as "readonly" variable
Swift "let" == C# "readonly"

F# users will feel right at home with Swift's let keyword. :)
In C# terms, you can think of "let" as "readonly var", if that construct was allowed, i.e.: an identifier that can only be bound at the point of declaration.

Swift properties:
Swift Properties official documentation
In its simplest form, a stored property is a constant or variable that is stored as part of an instance of a particular class or structure. Stored properties can be either variable stored properties (introduced by the varkeyword) or constant stored properties (introduced by the let keyword).
Example:
The example below defines a structure called FixedLengthRange, which describes a range of integers whose range length cannot be changed once it is created:
struct FixedLengthRange {
var firstValue: Int
let length: Int
}
Instances of FixedLengthRange have a variable stored property called firstValue and a constant stored property called length. In the example above, length is initialized when the new range is created and cannot be changed thereafter, because it is a constant property.

Related

Why does assignment return the old value instead of the new one?

Why does assignment return the previous value instead of the new value, re example: assigns 0 to y instead of 2? This is dangerously unusual as it violates the principle of least surprise.
var x: I32 = 0
let y = (x = 2)
I am pretty sure this was done to get consistent results for iso variables. Assignment to an iso variable would not be able to return the new value because that created an alias. But it's true that a less surprising design would involve an assignment operator returning None and some other operation (swap?) for the recovery of the original value that is being overwritten.

Does Swift copy on mutation in this scenario?

Essentially what I want is a temporary alias for a class property to improve readability.
I'm in the situation described by the following code and I can't see a straightforward solution. What I want to avoid is y being copied on mutation and then copied back.
Renaming y would reduce the readability of the actual algorithm a lot.
Is the Swift compiler smart enough to not actually allocate new memory and how would I be able to know that?
If not, how to prevent copying?
class myClass {
var propertyWithLongDescriptiveName: [Float]
func foo() {
var y = propertyWithLongDescriptiveName
// mutate y with formulas where y corresponds to a `y` from some paper
// ...
propertyWithLongDescriptiveName = y
}
// ...
}
struct Array is a value types in Swift, which means that they are always
copied when assigned to another variable. However, each struct Array
contains pointers (not visible in the public interface) to the actual
element storage. Therefore after
var a = [1, 2, 3, 4]
var b = a
both a and b are (formally independent) values, but with pointers to the same element storage.
Only when one of them is mutated, a copy of the element storage is made.
This is called "copy on write" and for example explained in
Friday Q&A 2015-04-17: Let's Build Swift.Array
So after
b[0] = 17
a and b are values with pointers to different (independent) element storage.
Further mutation of b does not copy the element storage again
(unless b is copied to another variable). Finally, if you assign
the value back
a = b
the old element storage of a is released, and both values are pointers to the same storage again.
Therefore in your example:
var y = propertyWithLongDescriptiveName
// ... mutate y ...
propertyWithLongDescriptiveName = y
a copy of the element storage is made exactly once (assuming
that you don't copy y to an additional variable).
If the array size does not change then a possible approach could be
var propertyWithLongDescriptiveName = [1.0, 2.0, 3.0, 4.0]
propertyWithLongDescriptiveName.withUnsafeMutableBufferPointer { y in
// ... mutate y ...
y[0] = 13
}
print(propertyWithLongDescriptiveName) // [13.0, 2.0, 3.0, 4.0]
withUnsafeMutableBufferPointer() calls the closure with an
UnsafeMutableBufferPointer to the element storage.
A UnsafeMutableBufferPointer is a RandomAccessCollection and
therefore offers an array-like interface.
No, the Swift compiler is not that smart. All you need is a small test to see what it does:
class MyClass {
var propertyWithLongDescriptiveName: [Float] = [1,2]
func foo() {
var y = propertyWithLongDescriptiveName
y[0] = 3 // copied an mutated
print(y) // [3,2]
print(propertyWithLongDescriptiveName) // [1,2]
}
}
let mc = MyClass()
mc.foo()
You have 2 optons:
Change propertyWithLongDescriptiveName to NSMutableArray, which is a reference type
Accept the overhead cost of copy-and-mutate to trade for readability of your algorithm. In many cases memory allocation cost is minimal compared to your algorithm's.

Swift Variable Declaration and Initialize

Is there a difference between how following bits of code work?
let x: Int = 4
and
let x: Int
x = 4
This one:
let x: Int = 4
creates a non-optional variable x and initialises it to 4. x can be used without issue.
This one:
let x: Int
// Cannot do anything with x yet
x = 4
creates a non-optional variable x with no defined value. It cannot be used without first assigning it to a value, either directly (as in your example) or by the result of some other statement. If you do try and use it, you'll get a compile-time error.
The only difference is that on the first one you are declaring a variable and assigning it at the same time, and the second one you declare it first and then assign it.
But there is no mayor difference.

What the difference between function and subscript, particularly in below code?

I read "The swift programming language" and the subscript make me confused, there's a example below with subscript, but I could also implement it with a function, so what the subscript exactly mean compared with function?
There were same output "6 times 3 is 18" with below example.
struct TimesTable {
let multiplier: Int
subscript(index: Int) -> Int {
return multiplier * index
}
}
let threeTimesTable = TimesTable(multiplier: 3)
println("6 times 3 is \(threeTimesTable[6])")
struct TimesTable2 {
let multiplier: Int
func times (index: Int) -> Int {
return multiplier * index
}
}
let threeTimesTable2 = TimesTable2(multiplier: 3)
println("6 times 3 is \(threeTimesTable2.times(6))")
Subscripts are a subset of functions. They can't quite do all the things a function can do (they can't take inout parameters, for instance), but they do other things very well, with a very convenient syntax (the square brackets [ ]).
They are most often used to retrieve an item from a collection by its index. So instead of having to write,
let array = [7, 3, 6, 8]
let x = array.itemAtIndex(0) // x == 7
we can just write,
let x = array[0]
Or instead of,
let dictionary = ["one": 1, "two": 2]
let x = dictionary.objectForKey("one") // x == Optional(1)
we can just write,
let x = dictionary["one"] // x == Optional(1)
The syntax is short and intuitive. And as Okapi said, they can act as getters and as setters for variable properties, just like a computed property.
The example in the documentation is a somewhat non-traditional use of subscripts. I think it is supposed to illustrate the very point that you are making - subscripts can be used in place of a function or a computed property just about anywhere that you think the [bracket] syntax would be convenient and useful. Their use is not limited to accessing items in a collection.
You get to refine your own syntactic sugar.

How do you assign a slice of numbers to an array in swift

x is an object that holds an array called point.
x implements the subscript operator so you can do things, like x[i] to get the array's ith element (of type T, which is usually an Int or Double).
This is what I want to do:
x[0...2] = [0...2]
But I get an error that says ClosedInterval<T> is not convertible to Int/Double.
Edit1:
Here is my object x:
let x = Point<Double>(dimensions:3)
For kicks and giggles: define x as [1.0,2.0,0.0]
I can get the first n elements via x[0...2].
What I want to know is how to update x[0...2] to hold [0.0, 0.0.0.0] in one fell swoop. Intuitively, I would want to do x[0...2] = [0...2]. This does not work as can be seen in the answers. I want to update x without iteration (on my end) and by hiding the fact that x is not an array (even though it is not).
[0...2] is an array with one element which, at best, will be a Range<Int> from 0 through 2. You can't assign that to a slice containing, say, Ints.
x[0...2] on the other hand is (probably) a slice, and Sliceable only defines a get subscript, not a setter. So even if the types were more compatible - that is, if you tried x[0...2] = 0...2, which at least is attempting to replace a range within x with the values of a similarly-sized collection - it still wouldn't work.
edit: as #rintaro points out, Array does support a setter subscript for ranges – so if x were a range you could do x[0...2] = Slice(0...2) – but it has to be a slice you assign, so I'd still go with replaceRange.
If what you mean is you want to replace entries 0 through 2 with some values, what you want is replaceRange, as long as your collection conforms to RangeReplaceableCollection (which, for example, Array does):
var x = [0,1,2,3,4,5]
var y = [200,300,400]
x.replaceRange(2..<5, with: y)
// x is now [0,1,200,300,400,5]
Note, the replaced range and y don't have to be the same size, the collection will expand/contract as necessary.
Also, y doesn't have to an array, it can be any kind of collection (has to be a collection though, not a sequence). So the above code could have been written as:
var x = [0,1,2,3,4,5]
var y = lazy(2...4).map { $0 * 100 }
x.replaceRange(2..<5, with: y)
edit: so, per your edit, to in-place zero out an array of any size in one go, you can do:
var x = [1.0,2.0,0.0]
// range to replace is the whole array's range,
// Repeat just generates any given value n times
x.replaceRange(indices(x), with: Repeat(count: x.count, repeatedValue: 0.0))
Adjust the range (and count of replacing entries) accordingly if you want to just zero out a subrange.
Given your example Point class, here is how you could implement this behavior assuming it's backed by an array under the hood:
struct Point<T: FloatLiteralConvertible> {
private var _vals: [T]
init(dimensions: Int) {
_vals = Array(count: dimensions, repeatedValue: 0.0)
}
mutating func replaceRange
<C : CollectionType where C.Generator.Element == T>
(subRange: Range<Array<T>.Index>, with newElements: C) {
// just forwarding on the request - you could perhaps
// do some additional validation first to ensure dimensions
// aren't being altered...
_vals.replaceRange(subRange, with: newElements)
}
}
var x = Point<Double>(dimensions:3)
x.replaceRange(0...2, with: [1.1,2.2,3.3])
You need to implement subscript(InvervalType) to handle the case of multiple assignments like this. That isn't done for you automatically.