0

I've searched in a lot of places and communities among the internet to find what the heck is happening in this programing syntax.

I'm seeking, desperately, for guidance in this code.

What is happening with the compiler in these specific declarations?

transitions[prev]?[transition]
transitions[state]?[transition] != nil

This is how the class is declared

public final class StateMachine<State: Hashable, Transition: Hashable> 

This is the variables

public var state: State


private var transitions = [State:[Transition:State]]()

And these are the examples:

  1. First situation - What is happening in the transitions[prev]?[transition]

    public final func advance(transition: Transition, observe: Observer? = nil) -> State {
    let prev = state
    if let next = transitions[prev]?[transition], next != prev {
        state = next
        observe?(prev, next)
    }
    
    return state
    
  2. Second situation - What is happening in the return transitions[state]?[transition] != nil

    public final func canAdvance(transition: Transition) -> Bool {
       return transitions[state]?[transition] != nil
    }
    

That's all i want to understand. What is happening in these moments?

  • 2
    Please read [Swift Language Guide: Optional Chaining](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/OptionalChaining.html#//apple_ref/doc/uid/TP40014097-CH21-ID245) – vadian Nov 18 '17 at 20:05
  • i've read. But i'm still having some troubles to understand transitions[state]?[transition] – Victor Kreniski Nov 18 '17 at 20:42

2 Answers2

1

The question mark operator signifies optionality in Swift.

You can declare many things a optional, meaning they could be nil or hold a value. This includes for example variable declarations, computed properties, return values of functions and callbacks/closures. Also certain operations like casting or retrieving values from dictionaries will yield optional values.

When you want to use the value contained you have to unwrap them, cause there might not be one and the may be pointing to nil. There are many ways and forms of unwrapping and optionality chaining.

Explaing your particular examples:


In a dictionary retrieving a stored value via a key as in myDict[myKey] returns an optional value. The value for the key stored within your specific dictionary is another dictionary. By declaring transitions[state]?[transition] you say basically "if there is a dictionary found for the key state, go ahead and continue with this dictionary and get the value for the key transition for that dictionary, otherwise use nil".

This code:

return transitions[state]?[transition] != nil

is basically a shorter way of writing this:

if let stateDict = transitions[state] {
    return stateDict[transition] != nil
} else {
    return false
}

Your other example is about an optional closure passed into a function. You can also pass optional closures into functions and call them via closure?(). The ? signifies that if nil is passed for the closure, nothing should be done, otherwise it should be executed.

This code:

observe?(prev, next)

is basically a shorter way of writing this:

if let observeClosure = observe {
    observeClosure(prev, next)
}

Some more optionality explanations:

If you work with an optional value from a declared variable you can safely unwrap it like so:

func square(myValue: Int?) → Int {
    guard let myValue = myValue else {
        return 0
    }
    return myValue * myValue
}

or

func square(myValue: Int?) → Int {
    if let myValue = myValue {
        return myValue * myValue
    } else {
        return 0
    }
}

or you could define a fallback with the ?? operator

func square(myValue: Int?) → Int {
    return myValue ?? 0 * myValue ?? 0
}

You could also use the ! operator to unwrap unsafely and if nil is found your app would crash. You should never do that unless you can guarantee that nil cannot be found like in:

func square(myValue: Int?) → Int {
    if myValue != nil {
        myValue! * mayValue! {
    } else {
        return 0
    }
}
erik_m_martens
  • 496
  • 3
  • 8
  • Thank you! You really helped. This is the explanation i needed to understand what is actually happening in this declaration transitions[state]?[transition] – Victor Kreniski Nov 18 '17 at 20:25
  • Glad I could help. I will modify my answer a bit more to add more explanations about optionality – erik_m_martens Nov 18 '17 at 20:31
0

In brief, transitions[state]?[transition] from time to time can be nil.

So, if let next = transitions[prev]?[transition] unwraps this variable moves the algorithm inside if-parenthesis

if let next = transitions[prev]?[transition] {
// this code executes here if 'transitions[prev]?[transition]' is not nil
    state = next
    observe?(prev, next) // this closure possibly can be nil. if nil Swift just skips this line
}
Vyacheslav
  • 26,359
  • 19
  • 112
  • 194
  • I think i did not made myself clear and i'm sorry. I understand how if let works but my problem was understanding what the compiler does when he reads something like transitions[state]?[transition]. What is he going to do? For example, find the first key-value in [state] and move to [transition] or if he unwraps a nil in [state] he will end or switch to [transition], you know? – Victor Kreniski Nov 18 '17 at 20:33
  • @VictorOliveira if the `transitions[prev]` is nil the whole expression is nil. Is it clear? – Vyacheslav Nov 18 '17 at 21:55
  • @Vyacheslav: Anything call by nil is always nil (in Swift) – Neelam Verma Mar 04 '19 at 20:39