Kwok Wen Jian Kwok Wen Jian - 5 months ago 12
Swift Question

why does this variable have to be unwrapped?

I'm on the second lecture of Stanford's IOS 9 developer course. The lecturer is trying to build a calculator. I can't figure out two parts.
One:

if pending != nil {
accumulator = pending!.binaryFunction(pending!.firstOperand, accumulator)
pending = nil


i understand that pending is an optional. but why is it that i still have to force unwrap it when I nested it in an "IF" statement that checks if it is nil or not?

The second part is:

private struct PendingBinaryOperationInfo {
var binaryFunction: (Double, Double) -> Double
var firstOperand: Double
}


why do the two variables need not have an initialiser? is it just because it is under "structure"?

The whole code is below. The two parts where i don't understand are found at the bottom of the chunk.

thanks a lot!

import Foundation

class CalculatorBrain
{
private var accumulator = 0.0

func setOperand(operand: Double) {
accumulator = operand
}

private var operations: Dictionary<String,Operation> = [
"e" : Operation.Constant(M_E),
"√" : Operation.UnaryOperation(sqrt),
"cos" : Operation.UnaryOperation(cos),
"×" : Operation.BinaryOperation({ $0 * $1 }),
"÷" : Operation.BinaryOperation({ $0 / $1 }),
"+" : Operation.BinaryOperation({ $0 + $1 }),
"−" : Operation.BinaryOperation({ $0 - $1 }),
"=" : Operation.Equals
]

private enum Operation {
case Constant(Double)
case UnaryOperation ((Double) -> Double)
case BinaryOperation ((Double, Double) -> Double)
case Equals
}

func performOperation(symbol: String) {
if let operation = operations[symbol] {
switch operation {
case .Constant(let value):
accumulator = value
case .UnaryOperation(let function):
accumulator = function(accumulator)
case .BinaryOperation(let function):
executePendingBinaryOperation()
pending = PendingBinaryOperationInfo(binaryFunction: function, firstOperand: accumulator)
case .Equals:
executePendingBinaryOperation()
}
}
}

private func executePendingBinaryOperation()
{
if pending != nil {
accumulator = pending!.binaryFunction(pending!.firstOperand, accumulator)
pending = nil
}
}

private var pending: PendingBinaryOperationInfo?

private struct PendingBinaryOperationInfo {
var binaryFunction: (Double, Double) -> Double
var firstOperand: Double
}

var result: Double {
get {
return accumulator
}
}
}

Answer
if pending != nil {
            accumulator = pending!.binaryFunction(pending!.firstOperand, accumulator)
            pending = nil

why is it that i still have to force unwrap it when I nested it in an "IF" statement that checks if it is nil or not?

Swift is a strongly typed language. In this case, pending is of type PendingBinaryOperationInfo?. Just because you checked it to make sure it isn't nil doesn't change the fact that pending is of type PendingBinaryOperationInfo?. You still need to unwrap it to access the PendingBinaryOperationInfo that is wrapped by the Optional.

The check for nil merely ensures that you can safely unwrap pending with ! because you now know it won't crash due to pending being nil.

Sometimes, programmers will write this like this:

if let unwrappedPending = pending {
    accumulator = unwrappedPending.binaryFunction(unwrappedPending.firstOperand, accumulator)
    pending = nil

This uses optional binding to unwrap pending and assign it to a new variable unwrappedPending that is of type PendingBinaryOperationInfo (which is not Optional).

Sometimes, you might also see this:

if let pending = pending {
    accumulator = pending.binaryFunction(pending.firstOperand, accumulator)
    pending = nil  // Oh, but this won't work because pending isn't optional

In this case, a new variable pending which is of type PendingBinaryOperationInfo is created which hides the original pending variable. I personally don't like this form because it confuses the issue with 2 different variables of two different types with the same name. In this case, it wouldn't work anyway, because the author also wants to set pending to nil, and you no longer have access to the original optional pending inside the if.


The second part is:

private struct PendingBinaryOperationInfo {
    var binaryFunction: (Double, Double) -> Double
    var firstOperand: Double
}

why do the two variables need not have an initialiser? is it just because it is under "structure"?"

Yes. For structures, Swift automatically generates an initializer for you that initializes all properties. You can see this initializer with Xcode's autocomplete feature by typing:

PendingBinaryOperationInfo(

As soon as you type the (, the initializer will pop up as the suggested completion. Here it is in a Playground:

autocompletion example in a Playground