Swift Equatable Generic type comparison inside generic function

4.1k views Asked by At

I have a Node class for a binary tree like so:

class Node<T: Equatable> {
    let value: T
    let left: Node<T>?
    let right: Node<T>?

    init(value: T, left: Node<T>? = nil, right: Node<T>? = nil) {
        self.value = value
        self.left = left
        self.right = right
    }
}

The values need to be equatable.

I can test out the equitability like this:

let a = Node(value: 8)
let b = Node(value: 7)

let c = a.value > b.value

Which works fine, c: true

But when I write a generic function that uses the equitability of the nodes I get errors:

func isBinaryTree<T>(node: Node<T>) -> Bool {
    if let leftNode = node.left {
        guard leftNode.value < node.value else {
            return false
        }
        guard isBinaryTree(node: leftNode) else {
            return false
        }
    }
    if let rightNode = node.right {
        guard rightNode.value >= node.value else {
            return false
        }
        guard isBinaryTree(node: rightNode) else {
            return false
        }
    }

    return true
}

let result = isBinaryTree(node: root)

Errors:

error: binary operator '<' cannot be applied to two 'T' operands guard leftNode.value < node.value ||`

I'm not sure why the compiler doesn't seem to know why the T values are Equatable or why it doesn't think that the T on the leftNode is the same type as the T on the node.

The code:

let d = Node(value: Float(3), left: Node(value: Int(8)) , right: nil)

Gives an error as expected.

Looking further into this, it isn't related to the function because when I try the code:

let x = Node(value: 3, left: Node(value: 8) , right: nil)
let y = x.value < x.left!.value

I get the same error

2

There are 2 answers

0
Alexander On BEST ANSWER

In the general case, two Node objects aren't comparable. It depends on the kind of tree they're found in. It would make sense, for example, if nodes were only constrained to be valid members of a binary tree, but this isn't the case.

Luckily, you don't need Node to be Comparable, you can just need for its value to be Comparable:

class Node<T: Comparable> {
    let value: T
    let left: Node<T>?
    let right: Node<T>?

    init(value: T, left: Node<T>? = nil, right: Node<T>? = nil) {
        self.value = value
        self.left = left
        self.right = right
    }
}

extension Node: Equatable {
    static func == (lhs: Node, rhs: Node) -> Bool {
        return lhs.value == rhs.value
            && lhs.left == rhs.left
            && lhs.right == rhs.right
    }
}

extension Node {
    func isBinarySubTree() -> Bool {
        return left.map { $0.value < self.value } ?? true
            && right.map { self.value < $0.value } ?? true
            && left?.isBinaryTree() ?? true
            && right?.isBinaryTree() ?? true
    }
}
0
richy On

Thanks to Alexander, I had my Equatable and Comparable mixed up! the Node should be

class Node<T: Comparable> {
    //...
}

the code:

let a = Node(value: 8)
let b = Node(value: 7)

let c = a.value > b.value

Must be working because the compiler knows the values are Ints. But in the function, the input values aren't known.