35

The whole reason for optionals is to prevent runtime crashes resulting from hitting a variable assigned to nil/null/none. Therefore, variables can't be nil; instead they can be wrapped in an Optional type that represents them as Some or None, and unwrapped to get the specific contents of Some, or nil.

But if you unwrap them all over the place with !, or with Implicitly Unwrapped Optionals, you just introduce the possibility, since you're an imperfect coder, of runtime crashes. If you use if let to safely unwrap them, you avoid crashes, but you are stuck inside the scope of the if let statement to work with the value inside Some, and you still have to handle the potential nil case. If you use ? to unwrap temporarily for calling a method, it will be rewrapped when complete, introducing the messy possibility of multiply layered optional wrapping.

So: It appears to me that the thing to do is avoid optionals except when necessary (e.g. when calling framework methods that return them). But if I'm not using optionals, that means my object references need to be non-nil, and I can't figure out how to handle cases where, for one reason or another, there shouldn't be, or isn't, a value assigned to an object reference.

My question is: How do I avoid the need for nil? It seems like it requires a different programming approach. (Or am I supposed to just use optionals, and if that's what I'm doing, how is it any better than simply having null assignments to object references like other languages have?)

I know this is potentially a subjective question, but where else am I supposed to ask it? I'm not trying to troll or stir up debate, I'd really like to know what the correct approach is as I write more Swift code.

Luca Angeletti
  • 58,465
  • 13
  • 121
  • 148
Ivan X
  • 2,165
  • 16
  • 25
  • 6
    Ever heard of railway oriented programming? Here is a post that explains that concept in f# http://fsharpforfunandprofit.com/posts/recipe-part2/ – mustafa Dec 23 '14 at 15:14
  • great question, look into Functional programming and (possibly) Functional Swift – Nick Dec 23 '14 at 20:58
  • 7
    If you are about to cast the last vote to close this question as too subjective, could you comment as to what is specifically wrong with it? The question really is a how-do-I-do-this, and the answer really does answer it. If you really think it should be closed, what could I have done to improve it? – Ivan X Dec 24 '14 at 00:34

4 Answers4

86

You’re right, optionals can be a pain, which is why you shouldn't overuse them. But they're more than just something you have to deal with when using frameworks. They're a solution to an incredibly common problem: how to handle a call that returns a result that might be OK, or might not.

For example, take the Array.first member. This is a handy utility that gives you the first element of an array. Why is it useful to be able to call a.first, when you could just call a[0]? Because at runtime the array might be empty, in which case a[0] will explode. Of course you can check a.count beforehand – but then again

a. you might forget,

and

b. that results in quite ugly code.

Array.first deals with this by returning an optional. So you are forced to unwrap the optional before you can use the value that is the first element of the array.

Now, to your issue about the unwrapped value only existing inside the block with if let. Imagine the parallel code, checking the array count. It would be the same, right?

if a.count > 0 {
    // use a[0]
}
// outside the block, no guarantee
// a[0] is valid

if let firstElement = a.first {
    // use firstElement
}
// outside the block, you _can't_
// use firstElement

Sure, you could do something like perform an early return from the function if the count is zero. This works but is a bit error-prone – what if you forgot to do it, or put it in a conditional statement that didn't happen to run? Essentially you can do the same with array.first: check the count early in the function, and then later on do array.first!. But that ! is like a signal to you – beware, you are doing something dangerous and you will be sorry if your code isn't completely correct.

Optionals also help make the alternatives slightly prettier. Suppose you wanted to default the value if the array was empty. Instead of this:

array.count > 0 ? a[0] : somedefault

you can write this:

array.first ?? somedefault

This is nicer in several ways. It puts the important thing up front: the value you want is how the expression starts, followed by the default. Unlike the ternary expression, which hits you first with the checking expression, followed by the value you actually want, then finally the default. It's also more foolproof – much easier to avoid making a typo, and impossible for that typo to result in a runtime explosion.

Take another example: the find function. This checks if a value is in a collection and returns the index of its position. But the value may not be present in the collection. Other languages might handle this by returning the end index (which doesn't point to a value but rather one past the last value). This is what's termed a "sentinel" value – a value that looks like a regular result, but actually has special meaning. Like the previous example, you’d have to check the result was not equal to the end index before using it. But you have to know to do this. You have to look up the docs for find and confirm that's how it works.

With find returning an optional it's just natural, when you understand the optional idiom, to realize that the reason it does is because the result might not be valid for the obvious reason. And all the same things about safety mentioned above also apply – you can’t accidentally forget, and use the result as an index, because you have to unwrap it first.

That said, you can over-use optionals as they are a burden to have to check. This is why array subscripts don't return optionals – it would create so much hassle to have to constantly check and unwrap them, especially when you know for a fact that the index you're using is valid (for example, you're in a for loop over a valid index range of the array) that people would be using ! constantly, and thus clutter their code without benefit. But then the helper methods like first and last are added to cover common cases where people do want to quickly do an operation without having to check the array size first, but want to do it safely.

(Swift Dictionaries, on the other hand, are expected to be regularly accessed via invalid subscripts, which is why their [key] method does return an optional)

Even better is if the possibility of failure can be avoided altogether. For example, when filter matches no elements, it doesn’t return a nil optional. It returns an empty array. “Well obviously it would”, you might say. But you’d be surprised how often you see someone making a return value like an array optional when actually they just ought to return an empty one. So you’re completely correct in saying you should avoid optionals except when they’re necessary – it’s just a question of what necessary means. In the examples above I’d say they’re necessary, and a better solution to the alternatives.

Siddharth
  • 9,349
  • 16
  • 86
  • 148
Airspeed Velocity
  • 40,491
  • 8
  • 113
  • 118
  • 11
    This is a really thoughtful, in-depth, on-point answer, and I appreciate your taking the time to spell it out with actual use cases as to when or when not optionals make sense. Thanks! – Ivan X Dec 23 '14 at 19:58
  • 4
    Fantastic stuff. You should probably write a swift blog or something ;) – jrturton Dec 24 '14 at 16:53
  • I actually writes a custom filter function that returns optional arrays. You busted me on that one. :D – Chen Li Yong Jun 09 '17 at 03:37
13

Or am I supposed to just use optionals, and if that's what I'm doing, how is it any better than simply having null assignments to object references like other languages have?

If your computation may need to return an “special” value, then yes, in Swift you are supposed to use optionals. They are better than nullable types because they are explicit. It’s easy to miss a case where a pointer might be nil, it’s much harder (but entirely possible) to screw up with optionals.

If you use “if let” to safely unwrap them, you avoid crashes, but you are stuck inside the scope of the “if let” statement to work with the value, and you still have to handle the potential nil case.

That’s a feature. I mean, that’s the whole point of an optional type: you have to handle both cases (nil and non-nil) and you have to be explicit about it.

See Haskell’s Maybe type and monad for an example of a similar concept. The Maybe type is the exact equivalent of optional types, the Maybe monad makes it very easy to “chain” operations with these optional values without having to manually check for empty values all the time.

zoul
  • 102,279
  • 44
  • 260
  • 354
  • 1
    This is helpful in the distinction about an optional being explicit versus a null reference not being so. Thanks. – Ivan X Dec 23 '14 at 20:00
  • Can you comment on the sentence "*If you use ? to unwrap temporarily for calling a method, it will be rewrapped when complete, introducing the messy possibility of multiply layered optional wrapping.*"? Are swift optionals not monadic, but behave only like functors? – Bergi Dec 24 '14 at 11:23
  • @Bergi, in Objective-C, you can send a message to a `nil` object and nothing bad happens. Optional chaining in Swift is just a generalized version of this feature. AFAIK you can’t use it for anything more complex than nested property access, chained method calls and such stuff. (Not sure if this answers your question about monads and functors, unfortunately I don’t know that much about functional programming.) – zoul Dec 25 '14 at 17:26
  • Aw, I thought you knew the Maybe *monad*. The OP seems to fear a type like `Optional>` that he would get when he chains a method that returns an optional result to an optional. Can this (automatically) be unwrapped to an `Optional`? – Bergi Dec 26 '14 at 12:08
  • In Swift, if you chain multiple optional accesses using `?`, you just get an “single-level” optional back, like `Maybe Int` if the rightmost call returns and `Int`. I know that the Maybe monad allows you to chain multiple expressions that use the Maybe type without having to check the return values by hand all the time, so I know the two features are similar and that the Haskell solution is more general, applicable to other situations. I don’t know much more. – zoul Dec 26 '14 at 14:45
1

Swift 2 and later also includes guard let which solves many of the problems mentioned in this question. You can even re-use the variable name if you want:

func compute(value: Double?) -> Double? {
    guard let value = value  else { return nil }

    // ... `value` is now a regular Double within this function
}

You can also use optional chaining (?) to short-circuit an expression when the first part returns nil:

let answer = compute(value: 5.5)?.rounded()

And nil coalescing (??) to provide a default value instead of nil:

let answer:Double = compute(value: 5.5) ?? 0

I agree with the other posts that optionals are not the right tool for every problem, but you don't need to avoid them. Use if let, guard let, ??, etc. to express how nil values are handled or passed along.

Robin Stewart
  • 3,147
  • 20
  • 29
1

Think about it this way: in any situation where you need an optional variable var value: Double?, you could equivalently use a pair of non-optional variables:

var value: Double
var valueExists: Bool

Whenever you want to use value, you would need to manually check if valueExists:

if valueExists { /* do something */ }
else { /* handle case where value is not valid */ }

It's usually a lot easier to use the language features designed for handling optionals than it is to pass dual variables around and do these checks manually. It's also much safer to have the compiler always remind you to handle the case where value is not valid. It's for reasons like these that optionals were invented!

Robin Stewart
  • 3,147
  • 20
  • 29