Pattern Variable Binding Cannot Appear in an Expression

Pattern variable binding cannot appear in an expression

A "pattern variable binding" is the thing you're doing, i.e. using let in the middle of some code, not at the top level of a file or enum or struct or class as a way of declaring a constant variable.

What makes it an expression is the parentheses. You've cut the "let" expression off from its surroundings and asked for evaluation of it as an expression separately. But you can't do that: you can't say "let" just anywhere.

Another way of looking at it is simply this: if let is a fixed meaningful pattern, where the condition is an Optional being evaluated into a constant for use inside the if-code. The parenthesis broke up the pattern.

The pattern is called a binding because you're defining this name very temporarily and locally, i.e. solely down into the if-code. I think it goes back to LISP (at least, that's where I've used "let" this way in the past).

Swift: Switch case with multiple patterns cannot bind to variable

It means that case labels with multiple patterns cannot declare variables.

This is allowed:

let somePoint = (1, 1)
switch somePoint {
// Case with multiple patterns without binding
case (0, _),
(_, 0):
println("(\(somePoint.0), \(somePoint.1)) is on an axis")
default:
println("(\(somePoint.0), \(somePoint.1)) is not of an axis")
}

This is allowed, too:

let somePoint = (1, 1)
switch somePoint {
// Case with single patterns with binding
case (0, let y):
println("(0, \(y)) is on an axis")
case (let x, 0):
println("(\(x), 0) is on an axis")
default:
println("(\(somePoint.0), \(somePoint.1)) is not of an axis")
}

However, this is prohibited:

let somePoint = (1, 1)
switch somePoint {
// Case with multiple patterns that have bindings
case (0, let y),
(let x, 0):
println("(\(x), \(y)) is on an axis")
default:
println("(\(somePoint.0), \(somePoint.1)) is not of an axis")
}

The above produces an error:

error: 'case' labels with multiple patterns cannot declare variables

Looking to clean up this switch statement off an `Any` value from `valueForKeyPath`

There is a way to pattern match and cast all at once, and I think it's actually quite clean:

switch nodeValue {
case let number as NSNumber:
switch number {
case 0: value = "No"
case 1: value = "Yes"
default: break
}
case let string as String:
value = string
default: break
}

Can you continue a loop if optional downcasting fails in Swift?

This is a somewhat common pattern, but it is not a good pattern. I've seen it inject bugs into ObjC projects. It assumes too much about the view hierarchy, and winds up being fragile when that changes (such as when someone injects an extra view you weren't expecting in order to manage rotations; true story). The better pattern is to maintain a property that points to your SpecificView (or views) that you want to track. Downcasting in general is something to be avoided, not optimized.

That said, it is not a terrible pattern, and sometimes it is a very useful pattern. So how might you handle it?

let specifics = self.subviews
.filter { $0 is SpecificView }
.map { $0 as SpecificView }

for view in specifics { ... }

That's kind of a common pattern, so maybe we can genericize it?

extension Array {
func filterByClass<T>(c: T.Type) -> [T] {
return self.filter { $0 is T }.map { $0 as T }
}
}

for view in self.subviews.filterByClass(SpecificView) { ... }

That said, I think this approach should be avoided wherever possible rather than excessively simplified.

infinite type inferred due to pattern variable

Using the value from a pattern match that way may be functionally equivalent, but the types are not - the type system doesn't have the evidence that the type variable for the Right can be ignored.

Using the types in your second example - when you pattern match e@(Left err), the type of e will be Either String DomeinFile - but you want an Either String PropDefs. The pattern match does not "free up" the type variable on the right, which is why you have to reconstruct the Left with the error again.

Swift: What do brackets around an if condition change?

According to the document:

The syntax of if statement is

if [condition] {
[statements]
}

And [condition] here is defined as:

if-conditionexpression­ | value-binding-pattern­


Here, let ... is a "Value-Binding Pattern".

value-binding-pattern → var ­pattern |­ let ­pattern­

As for ( ... ), it's not a value-binding-pattern because it doesn't start with let nor var, hence it must be a expression. In the specifications of expressions, the only we can find that starts with ( and end with ) is called "Parenthesized Expression".

parenthesized-expression → ( ­expression-element-list­opt ­)­

expression-element-list is basically a comma delimited list of expressions. And expression does not include value-binding-pattern. That's why you cannot put brackets around the let ....

cannot bind by-move into a pattern guard

In this case, you can bind by reference:

let res = match Some("hi".to_string()) {
Some(ref s) if s.len() == 0 => 1,
_ => 3
};

The general problem here is that binding by move must disallow further uses of the original variable, as moving out invalidates the data. If the guard is false, then the original variable needs to be used to match against the later patterns, which is illegal due to the move.

For example:

fn f(x: Option<String>) {
match x {
Some(a) if { drop(a); false } => println!("impossible"),
Some(b) => println!("whoops, {}", b),
None => println!("none"),
}
}

If x is Some, the internal String is moved out and deallocated when deciding if a arm should be taken, but that same String is immediately used again for the b arm once the a arm is rejected.

swift if case and optional binding

The first three code snippets you showed are the same. Out of the three, the first (optional binding) is definitely the recommended way to bind an optional in an if statement, as recommended by the Swift Guide:

You use optional binding to find out whether an optional contains a value, and if so, to make that value available as a temporary constant or variable.

The other two ways, I feel, are a bit more roundabout. Why would you write x + 2 - 1 when you can just write x + 1?

The enum case pattern works because Optional being implemented as an enum.

According to the Swift Reference, the optional pattern is just a syntactic sugar for the enum case pattern for the special case of Optional:

An optional pattern matches values wrapped in a some(Wrapped) case of an Optional<Wrapped> enumeration.

OTOH, there are times when you can't use optional binding, such as in a for loop or a switch statement. These are the times when optional patterns come in handy. An example from the Swift Reference:

let arrayOfOptionalInts: [Int?] = [nil, 2, 3, nil, 5]
// Match only non-nil values.
for case let number? in arrayOfOptionalInts {
print("Found a \(number)")
}

If you have a look at the syntax of the control flow statements and loops, you'll notice that patterns can be used in a lot more places than optional binding. Enum case patterns and optional patterns are patterns, but optional binding isn't a pattern.

The fourth code snippet prints nothing, because Optional.some(42) does not match the pattern x?. Recall what an optional pattern matches, it matches .some(x). x is nil (you haven't initialised it to anything), so you are matching .some(42) to .some(.none). Clearly they don't match, so the if statement does not run. Even if it did run, printing x would give you nil, as this is pattern matching, not variable assignment. If you want to do something similar to assigning variables...

You'd need to use a value binding pattern like let x? in cases like this. The optional pattern expands to let .some(x), and,

Identifiers patterns within a value-binding pattern bind new named variables or constants to their matching values.

Why doesn't using LET as an optional throw an error, as it is an implicit comparison to zero - inconsistency in Swift Intro book?

You are kind of right, it is not consistent, it should not mention Boolean Expression but rather a Logic Value. Here is what the Language Reference says about if:

The value of any condition in an if statement must have a type that conforms to the LogicValue protocol. The condition can also be an optional binding declaration, as discussed in Optional Binding.

Please note that optional binding here is not treated as an expression. It is not an assignment in if like in C. It is just a special case of an if statement that succeeds when the bound value is not nil.

None of this is valid in Swift:

x = y = 10 // () is not convertible to Int
if z = optional { } // type () does not conform to LogicValue
if let z = optional && z > 5 { } // bound value must be of Optional type
if (let z = optional) && z > 5 { } // pattern variable binding cannot appear in expression

Edit:
At first my answer stated that "assignment is not an expression in Swift", but technically that is incorrect. Assignment is an expression, but of type () a.k.a. Void. This is valid Swift:

var v = ()
var x = 0

v = x = 10 // x = 10; v = ()

Edit2:
Just to make this perfectly clear, when you use if let or if var it is not an assignment to an existing variable, a new variable/constant is introduced that is scoped to the inside of the if block:

var x = 0
var y : Int? = 10
if var x = y { // new variable x is introduced
println(x) // prints 10
x = 20 // neither affects y nor 'outer' x
}
if let x = y { // new constant x is introduced
println(x) // prints 10
}
if let y = y { // introduce new y shadowing the original
println(y) // prints 10
}
println(x) // prints 0

//if x = y { } // does not compile


Related Topics



Leave a reply



Submit