Using if let... with many expressions
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")
}
How to write multiple condition in if let statement?
You can use a simple tuple:
if let (Some(6), Some(7)) = (a, b) {
// do something
}
How to read if let expressions?
You shouldn't expect the keywords in the syntax of any programming language to map directly to words in English, or any other natural language.
let
expressions in Rust always accept a pattern, which lets you do things like:
let foo = (1, (2, 3));
let (a, (b, _)) = foo;
However, this only works when the pattern is irrefutable, that is it will always match.
if let
extends this to accommodate types that may not always match.
How you are reading it is just fine: "if the pattern matches then...", or "if this let binding succeeds then...".
Using multiple let-as within a if-statement in Swift
Update for Swift 3:
The following will work in Swift 3:
if let latitudeDouble = latitude as? Double, let longitudeDouble = longitude as? Double {
// latitudeDouble and longitudeDouble are non-optional in here
}
Just be sure to remember that if one of the attempted optional bindings fail, the code inside the if-let
block won't be executed.
Note: the clauses don't all have to be 'let' clauses, you can have any series of boolean checks separated by commas.
For example:
if let latitudeDouble = latitude as? Double, importantThing == true {
// latitudeDouble is non-optional in here and importantThing is true
}
Swift 1.2:
Apple may have read your question, because your hoped-for code compiles properly in Swift 1.2 (in beta today):
if let latitudeDouble = latitude as? Double, longitudeDouble = longitude as? Double {
// do stuff here
}
Swift 1.1 and earlier:
Here's the good news - you can totally do this. A switch statement on a tuple of your two values can use pattern-matching to cast both of them to Double
at the same time:
var latitude: Any! = imageDictionary["latitude"]
var longitude: Any! = imageDictionary["longitude"]
switch (latitude, longitude) {
case let (lat as Double, long as Double):
println("lat: \(lat), long: \(long)")
default:
println("Couldn't understand latitude or longitude as Double")
}
Update: This version of the code now works properly.
Using Let inside of a if statement
Your let
forms have no body in which the variable abs
is used.
Using two let
expressions is also probably not what you want. You should use one, and then use if
when calculating the value to bind the variable to.
(define abs
(lambda (l)
(let ((val (if (>= (car l) 0)
(car l)
(- (car l)))))
val)
)
)
Using the Swift if let with logical AND operator &&
As of Swift 1.2, this is now possible. The Swift 1.2 and Xcode 6.3 beta release notes state:
More powerful optional unwrapping with if let — The if let construct
can now unwrap multiple optionals at once, as well as include
intervening boolean conditions. This lets you express conditional
control flow without unnecessary nesting.
With the statement above, the syntax would then be:
if let tabBarController = window!.rootViewController as? UITabBarController where tabBarController.viewControllers.count > 0 {
println("do stuff")
}
This uses the where
clause.
Another example, this time casting AnyObject
to Int
, unwrapping the optional, and checking that the unwrapped optional meets the condition:
if let w = width as? Int where w < 500
{
println("success!")
}
For those now using Swift 3, "where" has been replaced by a comma. The equivalent would therefore be:
if let w = width as? Int, w < 500
{
println("success!")
}
swift: if-let with parallel assignment
Before deciding whether it is possible or not, consider why if - let ...
conditionals work with a single optional value: the reason this code compiles
if let constVar = testVar {
...
}
is that all optional types conform to the LogicalValue
protocol, which handles the null checking of the optional value.
This explains why your trick with an optional tuple did not work either: the implementation of the LogicalValue
checked if the tuple itself is non-null, ignoring its components. The logic behind Apple's decision is clear: rather than making an exception for tuples when all their element types are optional, they took the uniform approach, and treated the tuple in the same way that they treat other optional types.
Of course, implementing the logic that you are trying to implement is easy with an additional line of code:
if a != nil && b != nil {
let (m, n) = (a!, b!)
println("m: \(m), n: \(n)")
} else {
println("too bad")
}
Avoid consecutive if let declarations in Swift
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.
How can I use if statement in let statement?
An if
statement in ML is an expression. That means it has to have a value. Remember, value declaration in SML looks like this:
val <name> = <expression>
Since an if ... then ... else
statement is an expression, you can put in on the right side of the =
.
fun function i =
let
val h = if i = 1 then ...
else ...
in
...
end
Another thing. Remember that valOf
can go wrong. Using it on input is pretty much asking for your program to crash, since the user could type in anything.
Related Topics
Failing Cast in Swift from Any? to Protocol
Why Constant Constraints the Property from a Structure Instance But Not the Class Instance
Cannot Assign Property in Method of Struct
How to Encode a String to Base64 in Swift
How to Generate a Random Number in a Range (10...20) Using Swift
Firebase Getting Data in Order
How Flatmap API Contract Transforms Optional Input to Non Optional Result
How to See Which Version of Swift I'M Using
Swift @Escaping and Completion Handler
How to Improve People Occlusion in Arkit 3.0
Changing Text Color of Datepicker