Null coalescing operator with bool
Nil-coalescing operator has nothing to do with boolean values.
Use the full form ternary operator:
viewController.view.alpha = viewDisabled ? 0 : 1
Why is Nil coalescing operator Right Associative?
I think it's an optimization. Left or right association doesn't change the result.
This:
(b ?? c) ?? d
evaluates b ?? c
, and its result is used as the left side of x ?? d
. So even if b
is not null, the coalescing operator is executed 2 times.
In this case instead
b ?? (c ?? d)
if b
is not nil, the expression at the right side is not evaluated, hence not executed
Addendum
To prove that, I made a simple test: I (re)defined the nil coalescing operator:
infix operator !!! {
associativity left
precedence 110
}
func !!!<T>(optional: T?, defaultValue: @autoclosure () -> T?) -> T? {
if let value = optional {
println(optional)
return value
}
let def = defaultValue()
println(def)
return def
}
With this test data:
let a: String? = "a"
let b: String? = "b"
let c: String? = "c"
let d = a !!! b !!! c
Using associativity left
, this is what's printed to the console:
Optional("a")
Optional("a")
whereas changing the associativity to right
, the output is:
Optional("a")
That means when using right associativity the right side of the operator is ignored if the left is not nil.
Shorthand for null-coalescing operator and assignment for re-generatable property?
Similar notation in Swift would be:
private var _myProp: MyInstance?
public var myProp: MyInstance {
return _myProp ?? {_myProp = MyInstance(); return _myProp!}()
}
But I don't know that it's really more Swift-like or common to see that...
If you use this pattern a lot, it's probably best to just create a global function to help, like this:
func retrieve<T>(_ property:inout T?, withDefault value:T)->T {
if property == nil {
property = value
}
return property!
}
Then your code becomes:
private var _myProp: MyInstance?
public var myProp: MyInstance {
return retrieve(&_myProp, withDefault:MyInstance())
}
In my experience, functions are generally easier for other people to understand, read and debug than custom operators or overloads.
It does not trigger any errors when apply the Nil-Coalescing operator on a none optional variable
The nil-coalescing operator
public func ??<T>(optional: T?, @autoclosure defaultValue: () throws -> T) rethrows -> T
takes an optional a the first operand. So a ?? b
and a != nil ? a! : b
are equivalent provided that a
is an optional.
That is not the case in your example
let a: Int = 3, b: Int = 4
a ?? b
The first operand a
is not an optional. However, the compiler can
"wrap" a non-optional value T
into an optional T?
in order
to match a function or operator. For example, in
func foo(x: Int?) { }
foo(3)
the argument 3
is wrapped into an Int?
.
In your case, the expression is equivalent to
Optional<Int>.Some(a) ?? b
which is equivalent to
Optional<Int>.Some(a) != nil ? Optional<Int>.Some(a)! : b
However, the compiler is not so smart to recognize that Optional<Int>.Some(a)
cannot be nil
.
Custom operator to simplify If-Let
The problem (as you've correctly identified) is that because the left hand side argument is of type T
, when you pass an optional into it T
will be inferred to be Optional<Whatever>
. Because the right hand side argument is T?
(and because types can be freely promoted to optionals), it will infer the type to be Optional<Optional<Whatever>>
, leading to the confusing double wrapping you're observing.
The solution is to add an overload to deal with the situation when the left hand side argument is also an optional.
infix operator ?= {}
func ?= <T>(inout left: T, right: T?) {
if let right = right {
left = right
}
}
// overload to deal with an optional left handed side
func ?= <T>(inout left: T?, right: T?) {
if let right = right {
left = right
}
}
(Note that in Swift 3, inout
should appear before the parameter type)
Now if you use this operator with an optional as the left handed argument, Swift will use the overloaded version instead of the original version, as it'll always favour the more type-specific signature. This means that the right hand side won't get wrapped in a double optional, as it's now of the exact same type as the left hand side argument.
var name: String? = "Bob"
var firstName: String? = nil
name ?= firstName
print(name) // prints: Optional("Bob")
Note that this is similar to what the ??
does, it has two definitions to deal with one side being optional, one side being non-optional & both sides being optional in order to avoid the generation of double wrapped optionals:
@warn_unused_result
public func ??<T>(optional: T?, @autoclosure defaultValue: () throws -> T) rethrows -> T
@warn_unused_result
public func ??<T>(optional: T?, @autoclosure defaultValue: () throws -> T?) rethrows -> T?
Shorter Alternative to ternary to generate empty string if nil?
I don't see a simple way to simplify your code. An idea is to create a Double extension like this:
extension Optional where Wrapped == Double {
var asString: String {
self == nil ? "" : String(self!)
}
}
And then instead of that if condition you just use:
variable.asString
Related Topics
Process Array in Parallel Using Gcd
@Objc Keyword Extension Subclass Behaviour
Convert Character to Integer in Swift
Can the Conversion of a String to Data with Utf-8 Encoding Ever Fail
Skphysicsbody Avoid Collision Swift/Spritekit
Center Item Inside Horizontal Stack
How to Avoid Nested Navigation Bars in Swiftui
How to Change Font Size and Font Name of Uisegmentedcontrol Programmatically on Swift
Can't Create a Range in Swift 3
Swift - Seeding Arc4Random_Uniform? or Alternative
How Set Rootviewcontroller in Scene Delegate iOS 13
How to Handle Closure Recursivity
Why Can't the Swift Compiler Infer This Closure's Type
How to Use Keywords as Parameter Names in Swift
Swift Combine: How to Create a Single Publisher from a List of Publishers