# Constraints

For those of you looking for help on autolayout, this is not the place. Constraints, the constraints defined in Swift Standard Library, are an advanced topic that builds on other advanced topics, so beware.&#x20;

Everything in this article center around the `where` keyword. The `where` keyword allows us to make new components conditional or add extra conditions onto switches!

### Type Constraints

When working with parametric polymorphism it makes sense to add functionality in certain cases.&#x20;

For the following examples, we will use this implementation of a binary tree:

```swift
enum BinaryTree<T> {
    case leaf
    indirect case node(left: BinaryTree, value: T, right: BinaryTree)
}
```

Let's say we want to add a function to tell if our binary tree contains a value. To do this, we need `==` to be implemented. Thus, we make a function that will exist if and only if `T` conforms to `Equatable`:

```swift
extension BinaryTree where T: Equatable {
    
    func contains(_ element: T) -> Bool {
        switch self {
            case .leaf: return false
            case let .node(left, value, right):
                if value == element {
                    return true
                }
                return left.contains(element) || right.contains(element)
        }
    }
    
}
```

For those of you acquainted with binary trees, you may look at this and want to throw up–sorry! This is quite inefficient. The point of a binary tree is that we can reduce runtime by organizing it. And to be fair, that fact kind of nullifies this whole conditional function business, but we can think of this as more of an exercise than an actual use case.

But, I digress. Let's fix our inefficiency. There is another `protocol` we can use `Comparable`. `Comparable` guarantees `==`, `<`, and `>`. With these, it also guarantees `<=` and `>=`. Perfect! Let's implement it.

```swift
extension BinaryTree where T: Comparable {
    func contains(_ element: T) -> Bool {
        switch self {
            case .leaf: return false
            case let .node(left, value, right):
                if value == element {
                    return true
                } else if element < value {
                    return left.contains(element)
                } else {
                    return right.contains(element)
                }
        }
    }
}
```

You may not like having two extensions because it just gets kind of messy, but do not fear, we can combine these by putting the constraints on the functions instead of the extensions:

```swift
extension BinaryTree {

    func contains(_ element: T) -> Bool where T: Equatable {
        switch self {
            case .leaf: return false
            case let .node(left, value, right):
                if value == element {
                    return true
                }
                return left.contains(element) || right.contains(element)
        }
    }
    
    func contains(_ element: T) -> Bool where T: Comparable {
        switch self {
            case .leaf: return false
            case let .node(left, value, right):
                if value == element {
                    return true
                } else if element < value {
                    return left.contains(element)
                } else {
                    return right.contains(element)
                }
        }
    }

}
```

### Switch Constraints

Some of you may still be grossed out. I promised you that `switch` statements were this wonderful feature that would figure out all of the cases necessary to process something, but here I need extra if statements 😤! And you'd be right, except for that last part... We can cover these cases as well.

We can add conditionality to our cases with the `where` keyword.

```swift
func contains(_ element: T) -> Bool {
    switch self {
        case .leaf: return false
        case let .node(_, value, _) where value == element: return true
        case let .node(left, value, _) where value < element: return left.contains(element)
        case let .node(_, _, right): return right.contains(element)
    }
}
```

&#x20;Note that since the last case will always be triggered for nodes, it must be last. I wanted to add a `where` clause to fix that, but Swift would not recognize that as covering all of the cases. This was the cleanest solution we found. Credits to Hanzheng Li '23 for fixing that one.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ios-course.cornellappdev.com/resources/swift-foundations/constraints.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
