This idiom of Swift makes good sense
if let x = someDict[someKey] { ... }
However, what I really want is
if let x = someDict[someKey], y = someDict[someOtherKey] { ... }
As written this is not incorrect, but is this idea possible?
Update for Swift 1.2
Since Swift 1.2, if let allows unwrapping multiple optionals, so you can now just write this, as in your example:
if let x = someDict[someKey], y = someDict[someOtherKey] { … }
You can even interleave conditions such as:
if let x = someDict[someKey] where x == "value", y = someDict[someOtherKey] { … }
This used to be valid before Swift 1.2
Here's how you would do it without an ugly force-upwrapping:
switch (dict["a"], dict["b"]) {
case let (.Some(a), .Some(b)):
println("match")
default:
println("no match")
}
Still pretty verbose, actually.
This works because an optional type of the form Type? is actually shorthand for Optional<Type>, which is an enum that looks roughly like this:
enum Optional<T> {
case None
case Some(T)
}
You can then use pattern matching as for any other enum.
Edit: I've seen people write helper functions like this one (sorry for the lack of attribution, I don't remember where I saw it):
func unwrap<A, B>(a: A?, b: B?) -> (A, B)? {
switch (a, b) {
case let (.Some(a), .Some(b)):
return (a, b)
default:
return nil
}
}
Then you can keep using the if let construct, namely like this:
if let (a, b) = unwrap(dict["a"], dict["b"]) {
println("match: \(a), \(b)")
} else {
println("no match")
}
In Swift 1.2 (part of Xcode 6.3), the if let optional binding construct can bind multiple optionals, using the same syntax found in this question.
if let x = someDict[someKey], y = someDict[someOtherKey] { ... }
You can also extend this with conditions on the bound values:
if let a = foo(), b = bar(), a < b, let c = baz() { ... }
The switch pattern for handling multiple-optional binding (as seen in this answer) remains valid. Although with if let guard conditions you might find fewer use cases for it, it's still there in case you want to, say, handle cases of multiple optional binding with different behavior when different subsets of the optionals you're testing are nil.
(Note: Prior to Swift 3, if let a = ..., b = ..., a < b used where to split the binding from the conditional.)
In Swift1.2,you could use multiple optional bindings.
if let x = someDict[someKey], y = someDict[someOtherKey] { ... }
Notice that y is implicitly declared as constant,which is the same as x.The above code is equivalent to the following.
if let x = someDict[someKey],let y = someDict[someOtherKey] { ... }
If you want to explicitly declare y as mutable type,just type var before the variable name.
if let x = someDict[someKey],var y = someDict[someOtherKey] { ... }
Related
I'm coming from a C++ background, but I'm learning Swift 4 for MetalKit. Since I'm only used to C++, the whole focus on "optionals" comes a little foreign to me. While reading along with the Swift 4 book published by Apple, I came along the following problem:
Say I have two "String"s a and b:
let a = "12"
let b = "24"
I want to add these two together. It is bad practise AND illegal to write
let c = Int(a) + Int(b)
Because a and b are both Strings, their typecast into Int is an optional: the conversion may have failed. The solution seems to be
if let c = Int(a), let d = Int(b)
{
let e = c + d
}
But this is a bit of a hassle: I'm copying way more than I would in a C program, where I could simply add a and b and then test whether the result has a value. Is there a more efficient, better way to perform this addition?
As #rmaddy said in his comment, this is the whole point of optionals. It is supposed to make you work at it, resulting in safer code.
If you are willing to go to a bit of up-front work, you can create a custom operator that will throw an error if the result is converting to an Int is nil:
enum Err: Error {
case nilValue
}
func +(lhs: String, rhs: String)throws -> Int {
guard let c = Int(lhs), let d = Int(rhs) else {
throw Err.nilValue
}
return c + d
}
(You might have to add infix operator + to this snippet.)
You can then use it like this:
do {
let i: Int = try a + b
print(i)
} catch {
// Catch error here
}
Or use try?. This will return nil if an error is thrown:
let i: Int? = try? a + b
If you don't want to use the type annotations, you can give the operator a different name, i.e.:
infix operator +?: AdditionPrecedence
func +?(lhs: String, rhs: String)throws -> Int
If you're looking for a way to write this in one line, you can take advantage of the map and flatMap variants for optionals:
let a = "12"
let b = "24"
let c = Int(a).flatMap { aʹ in Int(b).map { bʹ in aʹ + bʹ }}
c is of type Optional<Int> and will be nil when either Int(a) or Int(b) fails. The outer map operation must be a flatMap to get the correct result type. If you replace flatMap with map, the type of c would be Optional<Optional<Int>>, or Int??.
Whether you consider this readable is at least partly a matter of familiarity with the concept of mapping over optionals. In my experience, most Swift developers prefer unwrapping with if let, even if that results in more than one line.
Another alternative: wrap this pattern of unwrapping two optionals and applying a function to the unwrapped values in a generic function:
func unwrapAndApply<A, B, Result>(_ a: A?, _ b: B?, _ f: (A, B) -> Result) -> Result? {
guard let a = a, let b = b else { return nil }
return f(a, b)
}
This function works on all inputs, regardless of the underlying types. Now you can write this to perform the addition:
let d = unwrapAndApply(Int(a), Int(b), +)
The unwrapAndApply function only works on two input arguments. If you need the same functionality for three, four, five, … inputs, you’ll have to write additional overloads of unwrapAndApply that take the corresponding number of arguments.
You have many ways in which you can do this. I will show you one that it is appropriate if you have to do this often:
extension Int {
static func addStrings(_ firstString: String, _ secondString: String) -> Int? {
guard let firstNumber = Int(firstString) else { return nil }
guard let secondNumber = Int(secondString) else { return nil }
return firstNumber + secondNumber
}
}
you should use it like this:
let number = Int.addStrings("1", "2")
I am using one awesome feature of Swift - extensions. With them you can add methods and computed variables to every class you want. Optionals and extensions are very important things when it comes to Swift development. You should read Apple docs carefully.
You can define your own operator to add two optionals together:
let a = "12"
let b = "24"
infix operator ?+: AdditionPrecedence
func ?+ (left: Int?, right: Int?) -> Int? {
guard let left = left,
let right = right else {
return nil
}
return left + right
}
let c = Int(a) ?+ Int(b)
The result is an optional. If you don't want the result to be optional you need to provide a suitable default value. For instance if you think 0 is appropriate:
infix operator ?+: AdditionPrecedence
func ?+ (left: Int?, right: Int?) -> Int {
guard let left = left,
let right = right else {
return 0
}
return left + right
}
There are a couple of ways to handle optionals besides the if let method you showed.
One is to place a guard let statement at the beginning of the block of code in which you are using the optionals. This allows you to avoid having tons of nested if let statements:
guard let c = Int(a), let d = Int(b) else { return }
// use `c` and `d` as you please
// ...
You can also use the nil coalescing operator to define a default value (e.g. 0):
let c = (Int(a) ?? 0) + (Int(b) ?? 0)
In this situation, if either Int(a) or Int(b) fails, they will be replaced with 0, respectively. Now c is an Int instead of an Int? and can be used freely without unwrapping. This may or may not be appropriate depending on what can happen if you use a default value rather than the intended one.
Further reading: What is an optional value in Swift?
Alternatively, you can create a custom operator to allow you to numerically "add" two strings:
infix operator +++: AdditionPrecedence
func +++(_ a: String, _ b: String) -> Int? {
if let intA = Int(a), let intB = Int(b) {
return intA + intB
} else {
return nil
}
}
// use it like so:
let c = "12" +++ "24"
// now c is Int? and you can check if the result is optional
if let d = c {
}
More about custom operators in the Swift Documentation: https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AdvancedOperators.html
I'm trying without success to unwrap and cast multiple optionals via tuples. The strange thing is that unwrapping individual items works.
It has been suggested that I'm misusing the downcast as? operator,
and that's very likely, but I just can't see it.
Can anyone explain why this doesn't work?
class fooba { }
func unwrapPair(a: AnyObject?, _ b:AnyObject?) {
if let a = a as? fooba {
// true
}
if let b = b as? fooba {
// true
}
if let (a, b) = (a, b) as? (fooba, fooba) {
// false!?!?
}
}
unwrapPair(fooba(), fooba())
I don't think this is a duplicate of tuple "upcasting",
because aren't I downcasting? In any case, explaining exactly how
they're duplicates would be illuminating and an answer in itself.
Instead of separately trying to match each component with if-let combinations, you can just do the same in a more swift-y way:
switch (a, b) {
case (let a as String, _):
// Do whatever you need to do with a
return true
case (_, let b as String):
// Do whatever you need to do with b
return true
case (let a as NSNumber, let b as NSNumber):
// Do whatever you need to do with a and b
return false
default:
return true
}
Note also that the type of downcast you are trying to do is currently impossible - look at this swift bug: tuple 'as?' downcast broken
For the simple cases like if let or guard I don't see the advantage,
if case let x? = someOptional where ... {
...
}
//I don't see the advantage over the original if let
if let x = someOptional where ... {
...
}
For the for-case-let case to simplify working with optional collections, I really hope swift can go one step further:
for case let x? in optionalArray {
...
}
//Wouldn't it be better if we could just write
for let x? in optionalArray {
...
}
After google it for a while the only case I find useful is this "Swift 2 Pattern Matching: Unwrapping Multiple Optionals" :
switch (username, password) {
case let (username?, password?):
print("Success!")
case let (username?, nil):
print("Password is missing")
...
So any other advantages of introducing optional patterns?
I believe you're conflating two different concepts. Admittedly, the syntax isn't immediately intuitive, but I hope their uses are clarified below.
(I recommend reading the page about Patterns in The Swift Programming Language.)
case conditions
The "case condition" refers to the ability to write:
if case «pattern» = «expr» { ... }
while case «pattern» = «expr» { ... }
for case «pattern» in «expr» { ... }
These are particularly useful because they let you extract enum values without using switch.
Your example, if case let x? = someOptional ..., is a valid example of this, however I believe it's most useful for enums besides Optional.
enum MyEnum {
case A
case B(Int)
case C(String)
}
func extractStringsFrom(values: [MyEnum]) -> String {
var result = ""
// Without case conditions, we have to use a switch
for value in values {
switch value {
case let .C(str):
result += str
default:
break
}
}
// With a case condition, it's much simpler:
for case let .C(str) in values {
result += str
}
return result
}
You can actually use case conditions with pretty much any pattern that you might normally use in a switch. It can be weird sometimes:
if case let str as String = value { ... } (equivalent to if let str = value as? String)
if case is String = value { ... } (equivalent to if value is String)
if case 1...3 = value { ... } (equivalent to if (1...3).contains(value) or if 1...3 ~= value)
The optional pattern, a.k.a. let x?
The optional pattern, on the other hand, is a pattern that lets you unwrap optionals in contexts besides a simple if let. It's particularly useful when used in a switch (similar to your username/password example):
func doSomething(value: Int?) {
switch value {
//case 2: // Not allowed
case 2?:
print("found two")
case nil:
print("found nil")
case let x:
print("found a different number: \(x)")
}
}
I have a Swift optional type and I want to chain a transformation to it (map) and eventually pass it to a closure that prints it.
I want avoid unwrapping (using !) the optional type because I don't want to execute the closure unless we have something inside the optional.
In Scala we use foreach as a map that returns Unit.
val oi = Option(2)
oi.map(_+1).foreach(println)
In Swift I get only until the increment (and the conversion to string)
let oi:Int? = 1
let oiplus = oi.map({$0 + 1}).map({String($0)})
// now we have "2"
Now how do I give this to print() only in the case the optional is not nil?
I would just map over it
let oi: Int? = 1
let oiplus = oi.map{ $0 + 1 }.map { String($0) }
oiplus.map { print($0) }
Arguably not nice to map for producing a side-effect, but there's no foreach for Optional in Swift.
This also produces an unused result warning in Swift 2.
If the semantic (and the warning) bug you, you can always define your own foreach for Optional. It's pretty straightforward:
extension Optional {
func forEach(f: Wrapped -> Void) {
switch self {
case .None: ()
case let .Some(w): f(w)
}
}
}
and then
let oi: Int? = 1
let oiplus = oi.map{ $0 + 1 }.map { String($0) }
oiplus.forEach { print($0) }
which resembles scala as much as possible.
While I usually do not recommend using map for side-effects, in this case I believe it's the cleanest Swift.
_ = oiplus.map{ print($0) }
There is now a SequenceType.forEach method that is explicitly for this problem, but Optional is not a SequenceType. You could of course extend Optional to add a forEach-like method (I'd probably call it apply in Swift).
It's worth noting that this just avoids the if-lit, which is barely any longer:
if let o = oiplus { print(o) }
But it admittedly doesn't chain as nicely as:
_ = oi
.map { $0 + 1 }
.map { print($0) }
See also http://www.openradar.me/23247992, which is basically Gabriele's answer.
This question already has answers here:
Using multiple let-as within a if-statement in Swift
(3 answers)
Closed 6 years ago.
In Swift I used if let declarations to check if my object is not nil
if let obj = optionalObj
{
}
But sometimes, I have to face with consecutive if let declarations
if let obj = optionalObj
{
if let a = obj.a
{
if let b = a.b
{
// do stuff
}
}
}
I'm looking for a way to avoid consecutive if let declarations.
I would try something like :
if let obj = optionalObj && if let a = obj.a && if let b = a.b
{
// do stuff
}
But the swift compiler do not allow this.
Any suggestion ?
Update
In swift 1.2 you can do
if let a = optA, let b = optB {
doStuff(a, b)
}
Original answer
In your specific case, you can use optional chaining:
if let b = optionaObj?.a?.b {
// do stuff
}
Now, if you instead need to do something like
if let a = optA {
if let b = optB {
doStuff(a, b)
}
}
you're out of luck, since you can't use optional chaining.
tl; dr
Would you prefer a cool one-liner instead?
doStuff <^> optA <*> optB
Keep reading. For how scaring it might look, this is really powerful and not so crazy to use as it seems.
Fortunately, this is a problem easily solved using a functional programming approach. You can use the Applicative abstraction and provide an apply method for composing multiple options together.
Here's an example, taken from http://robots.thoughtbot.com/functional-swift-for-dealing-with-optional-values
First we need a function to apply a function to an optional value only only when it contains something
// this function is usually called fmap, and it's represented by a <$> operator
// in many functional languages, but <$> is not allowed by swift syntax, so we'll
// use <^> instead
infix operator <^> { associativity left }
func <^><A, B>(f: A -> B, a: A?) -> B? {
switch a {
case .Some(let x): return f(x)
case .None: return .None
}
}
Then we can compose multiple options together using apply, which we'll call <*> because we're cool (and we know some Haskell)
// <*> is the commonly-accepted symbol for apply
infix operator <*> { associativity left }
func <*><A, B>(f: (A -> B)?, a: A?) -> B? {
switch f {
case .Some(let value): return value <^> a
case .None: return .None
}
}
Now we can rewrite our example
doStuff <^> optA <*> optB
This will work, provided that doStuff is in curried form (see below), i.e.
func doStuff(a: A)(b: B) -> C { ... }
The result of the whole thing is an optional value, either nil or the result of doStuff
Here's a complete example that you can try in the playground
func sum(a: Int)(b: Int) -> Int { return a + b }
let optA: Int? = 1
let optB: Int? = nil
let optC: Int? = 2
sum <^> optA <*> optB // nil
sum <^> optA <*> optC // Some 3
As a final note, it's really straightforward to convert a function to its curried form. For instance if you have a function taking two parameters:
func curry<A, B, C>(f: (A, B) -> C) -> A -> B -> C {
return { a in { b in f(a,b) } }
}
Now you can curry any two-parameter function, like + for example
curry(+) <^> optA <*> optC // Some 3
I wrote a little essay on the alternatives some time ago: https://gist.github.com/pyrtsa/77978129090f6114e9fb
One approach not yet mentioned in the other answers, which I kinda like, is to add a bunch of overloaded every functions:
func every<A, B>(a: A?, b: B?) -> (A, B)? {
switch (a, b) {
case let (.Some(a), .Some(b)): return .Some((a, b))
default: return .None
}
}
func every<A, B, C>(a: A?, b: B?, c: C?) -> (A, B, C)? {
switch (a, b, c) {
case let (.Some(a), .Some(b), .Some(c)): return .Some((a, b, c))
default: return .None
}
}
// and so on...
These can be used in if let statements, case expressions, as well as optional.map(...) chains:
// 1.
var foo: Foo?
if let (name, phone) = every(parsedName, parsedPhone) {
foo = ...
}
// 2.
switch every(parsedName, parsedPhone) {
case let (name, phone): foo = ...
default: foo = nil
}
// 3.
foo = every(parsedName, parsedPhone).map{name, phone in ...}
Having to add the overloads for every is boilerplate'y but only has to be done in a library once. Similarly, with the Applicative Functor approach (i.e. using the <^> and <*> operators), you'd need to create the curried functions somehow, which causes a bit of boilerplate somewhere too.
In some cases you can use optional chaining. For your simple example:
if let b = optionalObj?.a?.b {
// do stuff
}
To keep your nesting down and to give yourself the same variable assignments, you could also do this:
if optionalObj?.a?.b != nil {
let obj = optionalObj!
let a = obj.a!
let b = a.b!
}
After some lecture thanks to Martin R, I found an interesting workaround: https://stackoverflow.com/a/26012746/2754218
func unwrap<T, U>(a:T?, b:U?, handler:((T, U) -> ())?) -> Bool {
switch (a, b) {
case let (.Some(a), .Some(b)):
if handler != nil {
handler!(a, b)
}
return true
default:
return false
}
}
The solution is interesting, but it would be better if the method uses variadic parameters.
I naively started to create such a method:
extension Array
{
func find(includedElement: T -> Bool) -> Int?
{
for (idx, element) in enumerate(self)
{
if includedElement(element)
{
return idx
}
}
return nil
}
}
func unwrap<T>(handler:((T...) -> Void)?, a:T?...) -> Bool
{
let b : [T!] = a.map { $0 ?? nil}
if b.find({ $0 == nil }) == nil
{
handler(b)
}
}
But I've this error with the compiler: Cannot convert the expression's type '[T!]' to type '((T...) -> Void)?'
Any suggestion for a workaround ?