Shorthand for assignment if nil in Swift? - swift

if x == nil {
x = y
}
I know the statement above can be rewritten as:
x = x ?? y
But x ??= y is not accepted by compiler. Is there a shorthand not to repeat x?

Check this out, put the following in global code space(In your Extensions.swift maybe). This creates a custom operator you can use throughout your entire project.
Swift 2
infix operator ??= {}
func ??= <T>(inout left: T?, right: T) {
left = left ?? right
}
Swift 3
infix operator ??=
func ??= <T>(left: inout T?, right: T) {
left = left ?? right
}
Usage:
var foo: String? = nil
var bar = "Bar"
foo ??= bar
print("Foo: \(bar)")
Hope this helps :)

Laffen’s answer is great. However, it is not exactly equivalent to x = x ?? y as the right hand side is always evaluated with their definition of ??= as opposed to the standard ?? operator.
If you want to have this behavior too, #autoclosure is here to help:
infix operator ??=
func ??=<T>(left: inout T?, right: #autoclosure () -> T) {
left = left ?? right()
}

This code works in playground:
var x: Int? = nil
let y = 5
x = x ?? y // x becomes 5
and
var x: Int? = 6
let y = 5
x = x ?? y // x stays 6
By the way here are some variants of checking for nil:
if x != nil {
// classic
}
if x != .None {
// nil in Swift is enum with value .None
}
if let _x = x {
// if you need it's value
}
if let _ = x {
// if you don't need it's value
}
UPD: code for project - copy and run it:
var x: Int? = nil
let y = 5
x = x ?? y
print (x)
x = 7
x = x ?? y
print (x)

Related

Swift compilation time with nil coalescing operator

After reading of the article about swift compiling time. I am interested in why usage of more than 2 sequence coalescing operator increase compilation time significantly.
Example:
Compilation time 3.65 sec.
func fn() -> Int {
let a: Int? = nil
let b: Int? = nil
let c: Int? = nil
return 999 + (a ?? 0) + (b ?? 0) + (c ?? 0)
}
Compilation time 0.09 sec.
func fn() -> Int {
let a: Int? = nil
let b: Int? = nil
let c: Int? = nil
var res: Int = 999
if let a = a {
res += a
}
if let b = b {
res += b
}
if let c = c {
res += c
}
return res
}
I'm almost certain that this has to do with type inference. When interpreting all of those + and ?? operators the compiler is doing a lot of work behind the scenes to infer the types of those arguments. There are around thirty overloads for the + operator alone and when you chain several of them together you are making the compiler's job much more complicated than you might think.

Why something like optional chaining is not applied to all expressions and function calls in swift?

If there is an ability of optional chaining in swift like a?.b, which returns optional of type of b, why there is no ability to unwrap optionals like this:
let x: Int? = 5, y: Int? = 6
let z: Int?
z = x? + y?
I mean: apply + (or any other function) if all the arguments have values, or return 'nil' otherwise. It looks very useful for me, so I am not sure, if I am not missing something understanding this.
The why is not really answerable, it would be rather opinion based - although there's a good rationale from the Swift team for not implementing implicit stuff in the Swift realm.
But you can easily define a new + operator for your optional Ints:
func +(lhs: Int?, rhs: Int?) -> Int? {
if let l = lhs, r = rhs {
return l + r
}
return nil
}
let x: Int? = 5, y: Int? = 6
let z = x + y
You can also use flatmap to remove optionality and reduce to sum like this,
let z = [x, y].flatMap { $0 }.reduce(0, combine: +)

Shorthand for no re-assignment with the nil coalescing operator (??)

Is there a shorthand for doing this:
var x: Int = 42;
var y: Int? = nil
x = y ?? x
E.g. re-assigning same value/not re-assigning, if y is nil
Edit: To clarify, in the example, I'd like to keep the value (42) of x, if y is nil. A better wording might be an "assign if not nil" operator.
Edit2: As #rintaro pointed out, reassignment may incur side-effects which I would like to avoid. I could resort to if let like this:
var x: Int = 42;
var y: Int? = nil
if let y = y {
x = y
}
but that is pretty verbose.
There is no builtin operator for that.
Use map instead:
map(y) { x = $0 }
// OR
y.map { x = $0 }
Note: this technique is useful when the variable is observed:
var x: Int = 42 {
didSet {
println("didSet")
}
}
var y: Int? = nil
x = y ?? x // `didSet` is called
map(y) { x = $0 } // `didSet` is not called

How do you overload an operator in swift?

Say you have a class C. It has two instance variables, number and vector. vector is just an array of either ints or doubles.
I would like to do the following:
c1 = C()
c1.number = 2
c1.vector = [1,2,3]
c2 = C()
c2.number = 3
c2.vector = [4,6,7]
println(c1.number + c2.number) \\print to screen 5
println(c1.vector + c2.vector) \\ print [5,8,10]
Basically, I'm looking to overload the "+" operator so that it knows which "version" of the "+" to use depending of the type.
The + operator is already defined for the type Array. It does an array merge and tacks the values of the rvalue onto the lvalue. To do a sum of values by index you can do something like this:
protocol Numeric { }
extension Double: Numeric {}
extension Int: Numeric {}
func +<T: Numeric>(left: [T], right: [T]) -> [T]? {
var numElements: Int = 0
if count(left) != count(right) {
return nil
} else {
numElements = count(left)
}
var result = [T]()
for var i = 0; i < numElements; ++i {
if let lvalue = left[i] as? Int, rvalue = right[i] as? Int {
result.append(lvalue + rvalue as! T)
} else if let lvalue = left[i] as? Double, rvalue = right[i] as? Double {
result.append(lvalue + rvalue as! T)
}
}
return result
}
But generally, I wouldn't advise overriding a predefined operator because of the high potential to cause confusion and chaos later on down the road.

Swift: adding optionals Ints

I declare the following:
var x:Int?
var y:Int?
and I'd like a third variable z that contains the sum of x and y. Presumably, as x & y are optionals, z must also be an optional:
var z:Int? = x + y
but this gives a complier error "value of optional type 'Int?' not unwrapped; did you mean to use '!' or '?'"
If I unwrap x & y:
var z:Int? = x! + y!
I get a run-time error as x & y are nil, so can't be unwrapped.
I can achieve the desired result as follows:
var z:Int?
if let x1 = x {
if let y1 = y {
z = x1+y1
}
}
but this seems a bit verbose for adding together 2 integers! Is there a better way of achieving this?
Here is my take, I think it's cleaner:
let none:Int? = nil
let some:Int? = 2
func + (left: Int?, right:Int?) -> Int? {
return left != nil ? right != nil ? left! + right! : left : right
}
println(none + none)
println(none + some)
println(some + none)
println(some + some)
println(2 + 2)
With results of:
nil
Optional(2)
Optional(2)
Optional(4)
4
The best I can think of is:
if x && y {
z = x! + y!;
}
Since they are all Optionals... there's really no way to avoid:
checking that they aren't nil
Unwrapping them before you add them.
Best solution IMO:
z = x.map { x in y.map { $0 + x } }
Did you know the map operator on Optional<T>? It is very nice.
Documentation says:
If self == nil, returns nil. Otherwise, returns f(self!).
It depends exactly what you're trying to achieve, but you could do it with an optional tuple:
var xy: (Int, Int)?
var z: Int
if let (x1, y1) = xy {
z = x1 + y1 // Not executed
}
xy = (3, 4)
if let (x1, y1) = xy {
z = x1 + y1 // 7
}
Update
As #Jack Wu points out, this changes the semantics. If you were willing to be a bit more verbose, you could however do it as follows:
func optIntAdd(x: Int?, y: Int?) -> Int? {
if let x1 = x {
if let y1 = y {
return x1 + y1
}
}
return nil
}
operator infix +! { }
#infix func +! (x: Int?, y: Int?) -> Int?{
return optIntAdd(x, y)
}
var x: Int?
var y: Int?
var z: Int?
z = x +! y // nil
x = 1
z = x +! y // nil
y = 2
z = x +! y // 3
Not sure "+!" is a useful choice of name for the operator, but it would't let me choose "+?".
Someday, we'll have variadic generics. Until then, you need a pair of overloads for all arities.
var z = Optional(x, y).map(+)
public extension Optional {
/// Exchange two optionals for a single optional tuple.
/// - Returns: `nil` if either tuple element is `nil`.
init<Wrapped0, Wrapped1>(_ optional0: Wrapped0?, _ optional1: Wrapped1?)
where Wrapped == (Wrapped0, Wrapped1) {
self = .init((optional0, optional1))
}
/// Exchange two optionals for a single optional tuple.
/// - Returns: `nil` if either tuple element is `nil`.
init<Wrapped0, Wrapped1>(_ optionals: (Wrapped0?, Wrapped1?))
where Wrapped == (Wrapped0, Wrapped1) {
switch optionals {
case let (wrapped0?, wrapped1?):
self = (wrapped0, wrapped1)
default:
self = nil
}
}
}
z = (x ?? 0) + (y ?? 0)
* Worked on Xcode 9 *
Swift 5.5 Sum any optional values
infix operator +?: AdditionPrecedence
public func +? <T: AdditiveArithmetic>(lhs: T?, rhs: T?) -> T {
if let lhs = lhs, let rhs = rhs {
return lhs + rhs
} else if let lhs = lhs {
return lhs
} else if let rhs = rhs {
return rhs
} else {
return .zero
}
}