Reputation: 3819
Apple supplies an example of succinct optional chaining
class Person {
var residence: Residence?
}
class Residence {
var numberOfRooms = 1
}
let john = Person()
if let roomCount = john.residence?.numberOfRooms {
println("John's residence has \(roomCount) room(s).")
} else {
println("Unable to retrieve the number of rooms.")
}
Imagine trying to adjust the condition with some arithmetic operations. This results in a compiler error as the modulo operator doesn't support optionals.
if john.residence?.numberOfRooms % 2 == 0 {
// compiler error: Value of optional type Int? not unwrapped
println("John has an even number of rooms")
} else {
println("John has an odd number of rooms")
}
Of course you could always do something like the following, but it lacks the simplicity and succinctness of optional chaining.
if let residence = john.residence {
if residence.numberOfRooms % 2 == 0 {
println("John has an even number of rooms")
}else{
println("John has an odd number of rooms")
}
} else {
println("John has an odd number of rooms")
}
Are there any Swift language features which might provide a better solution?
Upvotes: 5
Views: 756
Reputation: 570
To make the following line work (i.e. make the operator directly accessing the value behind the optional left operand):
if john.residence?.numberOfRooms % 2 == 0
the operator must be in a precedence-group with assignment: true
. The modulo operator is in the MultiplicationPrecedence
precedence-group:
precedencegroup MultiplicationPrecedence {
associativity:left
higherThan: AdditionPrecedence
}
You can make your own prededence-group with assignment: true
precedencegroup AssignmentTruePrecedence {
assignment: true
associativity:left
higherThan:AdditionPrecedence
lowerThan:MultiplicationPrecedence
}
Now you can declare your own operator, assign it to the above precedence-group, and define the associated function to do the modulo operation.
infix operator %| : AssignmentTruePrecedence
extension Int {
static func %| (left: Int, right: Int)->Int {
return left % right
}
}
The line will work without error with our new modulo operator:
if john.residence?.numberOfRooms %| 2 == 0
It turns out that you don't even have to declare a new operator, as redeclaring the modulo operator and assigning it to our new precedence-group seems to work!
infix operator % : AssignmentTruePrecedence
Upvotes: 0
Reputation: 411
I've been trying to do the same thing for the last 10 minutes, using both optional chaining and the nil coalescing operator; the compiler doesn't like either of them. Can be done with an tf/then to get it working, but it feels ugly.
Should be able to do with an extension to Double, though I agree it feels like swift could (should?) support optional chaining in the standard operators. The following works in a playground:
extension Double {
func divide(by: Double) -> Double {
return (self/by)
}
}
var optDouble: Double? = nil
var result = optDouble?.divide(by: 100.0) // nil
optDouble = 20.25
result = optDouble?.divide(by: 100.0) // 0.2025
Upvotes: 0
Reputation: 28819
A possible solution would be to use Guard
func testOddEven(){
guard let number = john.residence?.numberOfRooms else {print("Doesn't even have a residence loool"); return }
switch number % 2 == 0 {
case true:
print("event")
break;
case false:
print("odd")
break;
}
}
Upvotes: 0
Reputation: 4170
Pattern matching is quite powerful, and can work in situations like this:
switch john.residence?.numberOfRooms {
case .Some(let x) where x % 2 == 0:
println("Even number of rooms")
default:
println("Odd number of rooms")
}
Upvotes: 2
Reputation: 3819
Best I could come up with is an extension on Optionals called omap (optional map).
extension Optional{
func omap<K:Any>(optionalBlock:(T)->(K?))->K?{
if self{
return optionalBlock(self!)
}
return nil
}
}
Similar to map, omap simply returns an instance of the optional to a supplied closure, but handles the nil case appropriately.
This allows you to chain an optional with arbitrary operations like so:
if (john.residence.omap{r in r.numberOfRooms % 2 == 0}){
println("John has an even number of rooms")
} else {
println("John has an odd number of rooms")
}
See: https://gist.github.com/Grantismo/3e1ba0412e911dfd7cfe
Upvotes: 0
Reputation: 160
I think what you are looking for is generally called a monad in functional programming.
It is not directly available in swift, but by using some of the language features, you can implement monads yourself in a generic way. (And also define a nice looking infix operator that makes it look like the monads in Haskell)
A quick google search for "monad swift" turned up some promising looking code at https://gist.github.com/cobbal/7562875ab5bfc6f0aed6
Upvotes: 3
Reputation: 89232
If I understand, you want it to say it's even if it's definitely even and odd if it's either odd or any of the optional values aren't set. If so:
if john.residence?.numberOfRooms && (john.residence!.numberOfRooms! % 2) == 0 {
println("John has an even number of rooms")
} else {
println("John has an odd number of rooms")
}
is the best I could do without a helper function. The issue is that you need methods to call on optional values to chain.
There are either bugs with if and Bool when optional chaining is being used, or I don't understand it well enough, but this works for me:
extension Int{
func isEven() -> Bool { return self % 2 == 0 }
}
if (john.residence?.numberOfRooms?.isEven() == true){
println("John has an even number of rooms")
} else {
println("John has an odd number of rooms")
}
If you write
if (john.residence?.numberOfRooms?.isEven())
That resolves as true as long as numberOfRooms has some value, even if isEven() returns false. As if the expression was Bool?, but it's not, it's Bool.
Upvotes: 0