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 ?
Related
After experimenting with currying in Swift, I came up with the code below. I want to see if it's possible to simplify this enum Operate. Currently, I need to initialize like this:
let multiply = Operate.Multiply.op
I would prefer to have each case have an associated value that directly returns a closure without having to do this hacky switch block. Is this possible?
Here's some code that you can run in a Swift playground:
import Foundation
enum Operate {
case Plus
case Minus
case Multiply
case unsafeDivide
var op: (Double) -> (Double) -> Double {
get {
switch self {
case .Plus:
return { n in
return { n + $0}
}
case .Minus:
return { n in
return { n - $0}
}
case .Multiply:
return { n in
return { n * $0}
}
case .unsafeDivide:
return { n in
return { n / $0 }
}
}
}
}
}
let multiply = Operate.Multiply.op
let plus = Operate.Plus.op
let unsafeDivide = Operate.unsafeDivide.op
// 3 + (16 * 2) -> 35
plus(3)(multiply(16)(2))
Bonus: How can I handle errors with unsafeDivide in a 'Swiftly' manner, that is, prevent this:
let unsafeDivide = Operate.unsafeDivide.op
unsafeDivide(2)(0)
What you seem to be doing is currying. You remove a lot of duplicated code by extracting a curry function:
func curry<A,B,C>(_ f: #escaping (A, B) -> C) -> (A) -> (B) -> C {
return { a in { b in f(a, b) } }
}
// ...
var op: (Double) -> (Double) -> Double {
switch self {
case .plus: // please follow Swift naming conventions, enum cases start with a lowercase
return curry(+)
case .minus:
return curry(-)
case .multiply:
return curry(*)
case .unsafeDivide:
return curry(/)
}
}
That already looks a lot nicer. You seem to not like switch statements, so here's how you'd do it with a dictionary:
var op: (Double) -> (Double) -> Double {
let dict: [Operate: (Double, Double) -> Double] =
[.plus: (+), .minus: (-), .multiply: (*), .unsafeDivide: (/)]
return curry(dict[self]!)
}
In fact, you can use the new callAsFunction feature in Swift 5.2 to omit even the word op on the caller side:
func callAsFunction(_ a: Double) -> (Double) -> Double {
op(a)
}
This allows you to do:
Operator.multiply(2)(3)
Using associated values is another way:
enum Operate {
case plus(Double)
case minus(Double)
case multiply(Double)
case unsafeDivide(Double)
func callAsFunction(_ b: Double) -> Double {
switch self {
case .plus(let a):
return a + b
case .minus(let a):
return a - b
case .multiply(let a):
return a * b
case .unsafeDivide(let a):
return a / b
}
}
}
But I personally don't like it because having associated values means that you can't simply use == to compare enum values, among other restrictions.
Preventing dividing by 0 at compile time is impossible, because the values you pass in might not be compile time constants. If you just want to check for compile time constants, then you might need a static code analyser like SwiftLint. At runtime, division of the Double 0 is well-defined by the IEEE standard anyway. It won't crash or anything.
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
Suppose you have two closures of type (Int)->() in Swift 3 and test to see if they are the same as each other:
typealias Baz = (Int)->()
let closure1:Baz = { print("foo \($0)") }
let closure2:Baz = { print("bar \($0)") }
if(closure1 == closure2) {
print("equal")
}
This fails to compile, giving the message:
Binary operator '==' cannot be applied to two '(Int)->()' operands
OK, well, how then can we compare two closures of the same type, to see if they are the same?
I'm pretty sure there is no way to determine if two closures are equal.
Obviously, a logical equality check is out of the question. That would be equivalent to finding an answer to the halting problem. (Just test to see if your code is equivalent to a piece of code that loops forever. If it is, it doesn't halt. If it isn't, it does halt.)
In theory you might expect the === operator to test if two closures are the exact same piece of code, but that gives an error when I try it in Playground.
Playground execution failed: error: MyPlayground.playground:1:20: error: cannot check reference equality of functions; operands here have types '(Int) -> ()' and '(Int) -> ()'
let bar = closure1 === closure2
~~~~~~~~ ^ ~~~~~~~~
Having thought about it, I'm sure the reason why that doesn't work is because you can't be sure that the closures really are equal. A closure is not just the code, but also the context in which it was created including any captures. The reason you can't check for equality is that there is no meaningful way in which two closures are equal.
To understand why thew captures are important, look at the following code.
func giveMeClosure(aString: String) -> () -> String
{
return { "returning " + aString }
}
let closure1 = giveMeClosure(aString: "foo")
let closure2 = giveMeClosure(aString: "bar")
Are closure1 and closure2 equal? They both use the same block of code
print(closure1()) // prints "returning foo"
print(closure2()) // prints "returning bar"
So they are not equal. You could argue that you can check the code is the same and the captures are the same, but what about
func giveMeACount(aString: String) -> () -> Int
{
return { aString.characters.count }
}
let closure3 = giveMeACount(aString: "foo")
let closure4 = giveMeACount(aString: "bar")
print(closure3()) // prints 3
print(closure4()) // prints 3
Apparently these closures are equal. It's not possible to implement any reasonable definition of equality that will work in every case, so Apple has instead not even tried. This is safer than providing an incomplete implementation that is wrong in some cases.
In the case where you want to track your own closures, uses them as Dictionary keys, etc., you can use something like this:
struct TaggedClosure<P, R>: Equatable, Hashable {
let id: Int
let closure: (P) -> R
static func == (lhs: TaggedClosure, rhs: TaggedClosure) -> Bool {
return lhs.id == rhs.id
}
var hashValue: Int { return id }
}
let a = TaggedClosure(id: 1) { print("foo") }
let b = TaggedClosure(id: 1) { print("foo") }
let c = TaggedClosure(id: 2) { print("bar") }
print("a == b:", a == b) // => true
print("a == c:", a == c) // => false
print("b == c:", b == c) // => false
I have the following class with a generic factory method:
final class Something<T> {
let value: T
init(initial: T) {
value = initial
}
}
extension Something {
class func zip<A, B>(_ a: A, _ b: B) -> Something<(A, B)> {
let initial = (a, b)
return Something<(A, B)>(initial: initial)
}
}
How come I can’t call zip without explicitly specifying the return type?
// ERROR: Cannot invoke `zip` with an argument list of type `(Int, Int)`
let y = Something.zip(1, 2)
// OK: Works but it’s unacceptable to require this on caller's side
let x = Something<(Int, Int)>.zip(1, 2)
Thank you for your time!
The reason you're seeing this is that there's nothing in this call:
let y = Something.zip(1, 2)
That tells Swift what T should be.
Your call implicitly specifies what A and B should be, and specifies the method should return Something<A, B>. But that Something<A, B> is not connected to Something<T>.
In fact, nothing at all in your call is connected to T; T is left unspecified, so it could be anything. I mean that literally—you can actually put (nearly) any random type in the angle brackets after Something and it'll work exactly the same:
let y = Something<UICollectionViewDelegateFlowLayout>.zip(1, 2)
What you would really like to do is somehow specify that T has to be a tuple and the two parameters are of the same types as the tuple's elements. Unfortunately, Swift doesn't currently have the features needed to properly do that. If the language were more sophisticated, you could say something like this:
extension<A, B> Something where T == (A, B) {
class func zip(a: A, _ b: B) -> Something {
let initial = (a, b)
return Something(initial: initial)
}
}
But for now, you'll have to make do with this horrible hack, which works by meaninglessly reusing the T type parameter so that it's no longer at loose ends:
extension Something {
class func zip<B>(a: T, _ b: B) -> Something<(T, B)> {
let initial = (a, b)
return Something<(T, B)>(initial: initial)
}
}
In short explanation, you use generics not correct. It's not realtime feature, it's precompile thing. If you need to make abstract class from generic input values, see and do like this:
class Abstract<T> {
init(value: T) {
print("inputed value: \(value)")
}
}
class Something {
class func zip<A, B>(value: A, value2: B) -> Abstract<(A, B)> {
print("Something.zip", value, value2)
return Abstract<(A, B)>(value: (value, value2))
}
}
Something.zip(5, value2: 40) // "inputed value: (5, 40)"
T simply isn't related to A and B in that way and so can't be inferred.
Eg.
let z = Something<(String, String)>.zip(1, 2)
let z2 = Something<AnyObject>.zip(1, 2)
work just fine to return a Something<(Int, Int)>
You can introduce type inference for your case like this:
final class Something<T> {
let value: T
init(initial: T) {
value = initial
}
class func zip<A, B>(_ a: A, _ b: B) -> Something<T> where T == (A, B) {
let initial = (a, b)
return Something<(A, B)>(initial: initial)
}
}
let y = Something.zip(1, 2) //works
Instead of using multiple optional bindings, we can define a function to tear down optional pyramid of doom.
func if_let<T, U, V> (a: T?, _ b: U?, _ c: V?, fn:(T, U, V) -> () ){
if let a = a {
if let b = b {
if let c = c {
fn(a, b, c)
}
}
}
}
Then I can write like this:
var s1: String? = "s11"
var s2: String? = "s22"
var s3: String? = "s33"
if_let(s1, s2, s3) { s1, s2, s3 in
print(("\(s1) - \(s2) - \(s3)"))
}
However, the problem is how to make this if_let function more generic so that it can accept any number of arguments. My implementation is like this:
func if_let<T> (values: T?..., fn:(params: [T]) -> ()) {
for value in values {
guard value != nil else { return }
}
let unwrappedArray = values.map{ $0! }
fn(params: unwrappedArray)
}
I tried to map the array and get a new one with all elements unwrapped and then call the fn. But when I ran the test again, I got a compile error:
Cannot convert value of type String? to expected argument type '_?'
Can anyone explain and fix this error?
The problem is that your second implementation of if_let no longer takes as a final parameter a function of type (T,U,V)->(). It now needs a function of type ([T])->(). If you call it with one, it compiles:
if_let(s1, s2, s3) { args in // or: (args: [String])->() in
print("\(args[0]) - \(args[1]) - \(args[2])")
}
A relevant note, rather than an answer to the specific question: with Swift 2, you needn't enter the pyramid of doom no more
let a: String? = nil
let b: Int? = nil
let c: Double? = nil
// possible mutate...
if let a = a, b = b, c = c {
// do something with shadow vars
}