2

When implemented for a concrete type, my binary search tree node works fine in a playground:

class Node {
    var data: Int
    var leftChild: Node?
    var rightChild: Node?

    init(data: Int){
        self.data = data
    }
}

let n = Node(data: 42)      // {data 42 nil nil}

When I try to make it generic, Xcode crashes and burns:

class GenericNode<T: Comparable> {
    var data: T
    var leftChild: GenericNode?
    var rightChild: GenericNode?

    init(data: T){
        self.data = data
    }
}

let g = GenericNode<Int>(data: 42)

When entered line-by-line, Xcode survives until I enter the actual assignment in init. I tried specifying <AnyObject: Comparable>, but apparently adding constraints to AnyObject is not allowed. How does one make suitably constrained generic containers in Swift?

pjs
  • 18,696
  • 4
  • 27
  • 56

2 Answers2

1

I think this is more likely a bug. If you try to do this in Swift terminal, it will throw an error:

LVM ERROR: unimplemented IRGen feature! non-fixed class layout

One workaround is to let T conform to NSObject.

Because NSObject is a protocol has required methods like isEqual(), which I think is kind of equivalent to an implementation of Comparable, for now.

So, the code would be like:

class GenericNode<T: NSObject> {
    var data: T
    var leftChild: GenericNode?
    var rightChild: GenericNode?

    init(data: T){
        self.data = data
    }
}

let g = GenericNode<NSNumber>(data: 42)

Then you can compare two GenericNode like this:

let f = GenericNode<NSNumber>(data: 53)
g.data.isGreaterThan(f.data) // is false in this case.

If you just try to write basic BST stuff, this should be good enough to go.

Aaron He
  • 5,509
  • 3
  • 34
  • 44
  • This will allow me to create `g`, but not to create another `Node` `g2` and check for things like `g.data < g2.data`. Your proposed solution lacks the `Comparable` protocol, which to me seems essential for building a BST. – pjs Jun 18 '14 at 03:52
  • @pjs no, because `T` conforms to `NSObject`, `T` must has implemented `isEqual` method. Then, let's say if you have another `let f = GenericNode(data: 42)`, they can be compared by `g.data.isEqual(f.data)`. – Aaron He Jun 18 '14 at 03:58
  • Checking whether two objects are equal is not sufficient to build a binary search tree. You really need to know which is the larger and which is the smaller if they're not equal. – pjs Jun 18 '14 at 04:01
  • @pjs Aha! Sorry! But it has `isGreaterThan()` and `isLessThan()` methods. – Aaron He Jun 18 '14 at 04:13
  • I was truly hoping for an "all-Swift" solution. Perhaps in a future generation of the compiler. Meanwhile, use of the `is...Than()` methods checks out for both `NSNumber` and `NSString` so thanks for a workaround. – pjs Jun 18 '14 at 04:23
  • @pjs Me too. It's more likely a compiler bug. I hope it will be fixed soon. – Aaron He Jun 18 '14 at 04:26
1

Definitely some bugs here. This code doesn't crash:

class GenericRootClass {
}

class GenericNode<T: GenericRootClass where T : Comparable> {

    let data: T
    var leftChild: GenericNode<T>?
    var rightChild: GenericNode<T>?

    init(data: T) {
        self.data = data
    }
}

but as soon as I remove the GenericRootClass in the type constraint or change it to Any or AnyObject, Xcode crashes.

As an aside: you'll note a couple other changes in my code:

  1. The data property should probably be immutable.
  2. The child nodes should be of type GenericNode<T>, so that you can actually compare against their data. (The compiler would have pointed that one out, if it could survive long enough.)

Something as simple as

class GenericRootClass<T> {
    var data: T?
}

is able to crash Xcode 6 beta 2. It appears that generic classes aren't working at the moment.

Wes Campaigne
  • 4,060
  • 3
  • 22
  • 17
  • Good catch on the declarations for the children `Node`s, thanks. In the long run I was going to make `data` immutable, but in the short run wanted to be able to play around with individual `Node`s in the playground. I don't see how a `GenericRootClass` helps solve the basic problem, though. Swift classes such as `Int` or `String` aren't subclasses of it, and I want a `Node` to be able to store _any_ payload which implements `Comparable` (including objects from classes that I haven't yet created). – pjs Jun 18 '14 at 06:02
  • 1
    Sorry, no, it doesn't solve the problem. It was just something I arrived at in testing to see what the scope of "things that will crash" was; this was evidence that constraining it the type parameter to a specific class hierarchy -- any class hierarchy -- works. I'd suggest substituting NSObject for it (while leaving the Comparable constraint in place) but it appears, at least for NSNumber, that Apple's bridging to Cocoa types does not extend to marking them as Comparable when they should be. It'd be simple enough to add that extension yourself, but you shouldn't HAVE to do any of this. – Wes Campaigne Jun 18 '14 at 14:37
  • It's sad - I see a lot of potential for the language, but right now many things that should be simple soak up way too much time. – pjs Jun 18 '14 at 14:52
  • Since nobody has come up with an all-Swift solution, I'm marking your answer as the solution. – pjs Jun 20 '14 at 02:50