Accidental invention of structural comparison?

It was a regular day full of my messing with swift lang, and in pursuit of a rummage, I had written this code:

import Foundation

protocol TypedObject {
    var type: Any.Type { get }
}
extension TypedObject {
    var type: Self { self.self }
}
extension Array: TypedObject {var type: Any.Type {Array.self}}
print(
    [1,2].type == [1,2,3].self,
    [1,2].type == [1,2].self
)

For some unknown to me reason this code prints false true. The only reason why this happens is that it compares structures of operands. This behaviour seems strange to me. Do you think it is a bug?

Your [1, 2].type is not using this property (which therefore adds nothing to your example and can be removed from it):

extension Array: TypedObject {var type: Any.Type {Array.self}}

it's using this property (by type inference):

extension TypedObject {
    var type: Self { self.self }
}

and self.self.self… is the same as self.self which is the same as self, thus your example compares the arrays, not the types.

I assume you meant the latter code block to be:

extension TypedObject {
    var type: Self.Type { Self.self }
}

And that you might have mean't to write:

print(
    [1,2].type == type(of: [1,2,3]),
    [1,2].type == type(of: [1,2])
)

But still, it's not clear as to what you want to achieve.


Given the above, why shouldn't it?
(It's comparing arrays, not types, and [1, 2] is not equal to [1, 2, 3]
and [1, 2] is equal to [1, 2].)

[1, 2].self.self… is the same as
[1, 2].self which is the same as
[1, 2].

The weird thing about this code is that I cannot remove anything from it. If i do so it fails to compile. :thinking:
To add more, both type and [].self are gonna return a metatype Array.Type, but without it being explicitly specialized - they are even not declared Array.Type - I am confused about why =='s output seems to take into account their structure as part a type.

No, please read my reply above again.
[1, 2].self is not a type (or metatype), it's simply [1, 2], ie the array itself.

let a = [1, 2]
let b = [1, 2].self.self.self.self.self // Note: zero or more .self here doesn't matter!
print(a) // [1, 2]
print(b) // [1, 2]
print(a.count, b.count) // 2 2
print(a == b) // true

See?


We can take this in two steps:

protocol TypedObject {
    var type: Any.Type { get }
}
extension TypedObject {
    var type: Self.Type { Self.self } // <-- I've changed this to what I assume you meant.
}
extension Array: TypedObject { } // <-- And this.
print(
    [1,2].type == type(of: [1,2,3]),
    [1,2].type == type(of: [1,2])
)

The reason that this does not compile is that the default implementation of type is not satisfying the requirement (because Self.Type is not Any.Type) so you either have to do this:

protocol TypedObject {
    var type: Any.Type { get }
}
extension TypedObject {
    var type: Any.Type { Self.self }
}
extension Array: TypedObject { }
print(
    [1,2].type == type(of: [1,2,3]),
    [1,2].type == type(of: [1,2])
)
// Will print "true true".

or this:

protocol TypedObject {
    associatedtype A
    var type: A { get }
}
extension TypedObject {
    var type: Self.Type { Self.self }
}
extension Array: TypedObject { }
print(
    [1,2].type == type(of: [1,2,3]),
    [1,2].type == type(of: [1,2])
)
// Will also print "true true".

I don't really see the point of this since it's essentially just a complicated way of saying
(for the first example):

let foo: Array<Int> = [1, 2]
let bar: Any.Type = type(of: foo)
print(bar == type(of: foo)) // true

or (the second example):

let foo: Array<Int> = [1, 2]
let bar = type(of: foo)
print(bar == type(of: foo)) // true
1 Like

Yeah, probably long isolation has made me blear. Thank you.

1 Like