If constants are immutable, why can I reassign them using let? - ios

From the Apple docs "Learn the Essentials of Swift"
A constant is a value that stays the same after it’s declared the
first time, while a variable is a value that can change. A constant is
referred to as immutable, meaning that it can’t be changed, and a
variable is mutable. If you know that a value won’t need to be changed
in your code, declare it as a constant instead of a variable.
Yet in the REPL, I can do this:
14> let favoriteNumber = 4
favoriteNumber: Int = 4
15> let favoriteNumber = 5
favoriteNumber: Int = 5
I am clearly missing something: does this discrepancy have to do with the compiler or runtime, or is it something else?

You can do things in the REPL that you can't do in normal code.
You can't re-assign constants in a playground or in compiled code.
I am not sure if this is a special convenience offered in the REPL, or a bug.

I can see how this gives the impression you are changing the value of a constant, but in actuality you aren't changing the value of favoriteNumber. What is happening is you are declaring a new constant with the same identifier as the previous declaration. In essence this hides the first declaration and it is if only the second favoriteNumber exists. If you try the following:
let favoriteNumber = 4
favoriteNumber = 5
You will see that you are not allowed to change the value of a constant.
It would probably be helpful if the REPL produced a warning that you redeclared an existing constant or variable.

This is not allowed
Reassigning constants is not allowed.
As of Swift 5.2, it is advertised that you cannot redeclare them either (or at least, make invalid redeclarations of such within the same scope):
let this = true
let this = false
// Compiler Error: Invalid redeclaration if 'this'
In the REPL you are probably allowed to redeclare values. I'm pretty sure because of the following:
But secretly, it is
Apparently, you don't need the REPL to redeclare values.
I accidentally discovered that you can do this, and it will compile:
let this = true
guard case let this = 100 else {}
print(this) // 1
It's cool that you can even redeclare a value to a different type.
Since we are only hid[ing] the first declaration, this is allowed.

Related

Using Xcode, Swift, and GRDB, why do I have to unwrap DatabaseQueue? before I can use its methods?

I'm using the GRDB library to integrate SQLite with my iOS application project. I declared a DatabaseQueue object in AppDelegate.swift like so:
var DB : DatabaseQueue!
In the same file, I had provided a function for connecting the above object to a SQLite database which is called when the app starts running. I had been able to use this in one of my controllers without problems (as in, the app doesn't have problems running using the database I connected to it), like so:
var building : Building?
do {
try DB.write { db in
let building = Building.fetchOne(db, "SELECT * FROM Building WHERE number = ?", arguments: [bldgNumber])
}
} catch {
print(error)
}
However, in another controller, the same construct is met with an error,
Value of optional type 'DatabaseQueue?' must be unwrapped to refer to member 'write' of wrapped base type 'DatabaseQueue'
with the only difference (aside from the code, of course) being that there are return statements inside the do-catch block, as the latter is inside a function (tableView for numberOfRowsInSection) that is supposed to return an integer. The erroneous section of code is shown below.
var locsCountInFloor : Int
do {
try DB.write { db in
if currentBuilding!.hasLGF == true {
locsCountInFloor = IndoorLocation.filter(bldg == currentBuilding! && level == floor).fetchCount(db)
} else {
locsCountInFloor = IndoorLocation.filter(bldg == currentBuilding! && level == floor + 1).fetchCount(db)
}
return locsCountInFloor
}
} catch {
return 0
}
Any help would be greatly appreciated!
As is often the case when you have a problem with a generic type in Swift, the error message is not helpful.
Here’s the real problem:
DB.write is generic in its argument and return type. It has a type parameter T. The closure argument’s return type is T, and the write method itself returns T.
The closure you’re passing is more than a single expression. It is a multi-statement closure. Swift does not deduce the type of a multi-statement closure from the statements in the closure. This is just a limitation of the compiler, for practical reasons.
Your program doesn’t specify the type T explicitly or otherwise provide constraints that would let Swift deduce the concrete type.
These characteristics of your program mean Swift doesn’t know concrete type to use for T. So the compiler’s type checker/deducer fails. You would expect to get an error message about this problem. (Possibly an inscrutable message, but presumably at least relevant).
But that’s not what you get, because you declared DB as DatabaseQueue!.
Since DB is an implicitly-unwrapped optional, the type checker handles it specially by (as you might guess) automatically unwrapping it if doing so makes the statement type-check when the statement would otherwise not type-check. In all other ways, the type of DB is just plain DatabaseQueue?, a regular Optional.
In this case, the statement won’t type-check even with automatic unwrapping, because of the error I described above: Swift can’t deduce the concrete type to substitute for T. Since the statement doesn’t type-check either way, Swift doesn’t insert the unwrapping for you. Then it carries on as if DB were declared DatabaseQueue?.
Since DatabaseQueue? doesn’t have a write method (because Optional doesn’t have a write method), the call DB.write is erroneous. So Swift wants to print an error message. But it “helpfully” sees that the wrapped type, DatabaseQueue, does have a write method. And by this point it has completely forgotten that DB was declared implicitly-unwrapped. So it tells you to unwrap DB to get to the write method, even though it would have done that automatically if it hadn’t encountered another error in this statement.
So anyway, you need to tell Swift what type to use for T. I suspect you meant to say this:
var locsCountInFloor: Int
do {
locsCountInFloor = try DB.write { db in
...
Assigning the result of the DB.write call to the outer locsCountInFloor is sufficient to fix the error, because you already explicitly defined the type of locsCountInFloor. From that, Swift can deduce the return type of this call to DB.write, and from that the type of the closure.

Why we don't need to provide initial value for local variables?

While I was learning suddenly I wondered myself:
why do we have to provide initial values for global(even beyond a class scope) variable but we do not have to do same step with local variables like this? Is there any reason?
if importRequired {
let deleteObjectCount: Int
}
It is allowed, because deleteObjectCount is never been used in your code. And - and this is the difference to global variables - this fact can be checked by the compiler.
You could even do something like:
let importRequired = true
if importRequired {
let deleteObjectCount: Int
deleteObjectCount = 5
print (deleteObjectCount)
}
(e.g. kind-of modify a constant let variable) because the compiler checks that the constant is written only once, and this is done before reading it's value.
In contrast, global variables must be initialized directly, because otherwise the compiler cannot guarantee that they have been so before being initialized (because the could be accessed from anywhere in your program).

Cannot invoke initializer for type with no arguments [duplicate]

In Swift 2.2, I often declared variables using a concise syntax similar to let x = UIView?(). This gave x the UIView? type, and initialised it with nil. (Of course, you could use any type instead of UIView in these examples)
However, when I do the same in Swift 3.0, I get an error:
Cannot invoke initializer for type 'UIView?' with no arguments. It also says that Overloads for 'UIView?' exist with these partially matching parameter lists: (Wrapped), (nilLiteral: ()). Somehow, I don't think UIView?(nilLiteral: ()) is quite what I'm after.
Naturally, there are other alternative methods to do the same thing, such as let x: UIView? = nil and let x = nil as UIView(), but they're more verbose than the method I was using previously. Was the UIView?() constructor removed in Swift 3.0, or has it been replaced in a form that I've not yet discovered?
The following does initialise x to nil, the brackets are entirely superfluous.
var x: UIView?
return x == nil
Will return true
Check out the developer docs for more information.
https://developer.apple.com/library/content//documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html
If you define an optional variable without providing a default value,
the variable is automatically set to nil for you:
var surveyAnswer: String?
// surveyAnswer is automatically set to nil
#Hamish posted a comment to clarify the reason why UIView?() no longer works:
“The syntax UIView? is just syntactic sugar for Optional, therefore UIView?() is just syntactic sugar for Optional.init(). In Swift 2, Optional's init() constructs a new optional value set to .None. So yes, it was working correctly. In Swift 3 however, this initialiser has been removed, which is why UIView?() no longer works”

Error FS0037 sometimes, very confusing

If I write the following F# code, the compiler issues an error.
let a = 123
let a = 123
The error produced is:
error FS0037: Duplicate definition of value 'a'
If I write the same code in a function like this:
let fctn =
let a =123
let a =123
a
it doesn't produce any error.
I don't understand the difference. Can anyone please explain?
Edit : first code I write in module level.
I agree this is confusing. The problem is that let behaves differently when it is used as a local variable (within a function) and when it is used as a global definition (within a module).
Global definitions (in a module) are compiled as static members of a static class and so a name can be used only once. This means that top-level use of:
let a = 10
let a = 11
... is an error, because F# would have to produce two static members of the same name.
Local definitions (inside a function or some other nested scope) are compiled to Common IL and the variable name essentially disappears (the IL uses the stack instead). In this case, F# allows you to shadow variables, that is, you can hide a previous variable by re-using an existing name. This can be inside a function, or even just a do block inside a module, type or other function:
do
let a = 10
let a = 11
()
This is a bit confusing, because variable shadowing only works inside local scopes but not at the top level. It makes sense when you know how things are compiled though.
As an aside, while IL allows overloads of members by the same name, such overloads cannot be defined at module level in F#. Instead, you'd need to define them specifically as static member on a class (type in F#).
on scope and shadowing
as CaringDev mentioned (but not explained) you will probably see what the shadowing is about when you make the scope a bit more obvious (using the let ... in ... construct #light let you shorten a bit - but you still can use it even without #light off)
Try this:
> let a = 233 in let a = 555 in a;;
val it : int = 555
as you can see the expression evaluates to the shadowed value of a - but the original is not lost:
> let a = 233 in (let a = 555 in a), a;;
val it : int * int = (555, 233)
it's just not in scope in the inner let ... in ...
btw: you can rewrite your example to:
let fctn =
let a = 123 in
(let a =123 in a)
(I added the parentheses just to make this more obvious)
the other on the module level really defines a value for the scope of the module and is not really an expression but a definition
The first snippet defines two public values with the same name.
The second hides (shadows) a value.
With the first you would have externally visible change of state (a behaves like mutable) whereas with the second you can't (you have two as in different scopes).
If you write your statements in #light off ML syntax it becomes obvious immediately.

swift declare variable of subtype with reference of super type

I'm trying to use polymorphism in swift but I'm doing something wrong; I have the following scenario:
class Character : NSNode {
let characterName = "name"
}
class World : NSNode {
let character : NSNode = Character()
// getting error here, NSNode does not have a property "characterName"
character.characterName = "new name"
}
what's the correct way of declaring a variable of type Character() but has reference type NSNode()?
(character as Character).characterName = "new name"
This feature of Swift is not unusual. You have typed character as NSNode so that is all the compiler knows about it. To tell the compiler what sort of object it really is (e.g. what subclass of NSNode), you cast down.
There is nothing wrong with your declaration, provided you are willing to cast down every time you want to access a Character feature. But did you have some reason for not just typing character as a Character to begin with? After all, Swift knows perfectly that this means it is also an NSNode. It seems to me that you may have your notion of polymorphism somewhat topsy-turvy.

Resources