Dictionary.keys is neither Array nor ISequence


(Marko Havu) #1

While investigating why Dictionary.keys.contains gives E: No member "contains" on type "RemObjects.Elements.System.INSFastEnumeration<NSString>!" I noticed that Dictionary.keys is neither Array (as it is on Apple’s implementation) nor ISequence (as it says it is in Dictionary.swift of SwiftBaseLibrary):

let range = [Int]((1 ... 256).GetSequence())
print(range is ISequence<Int>)  // 1
let dict: [String: Int] = ["a": 1, "b": 2]
print(dict.keys is ISequence<String>)  // 0
let keys: ISequence<String> = dict.keys
print(keys is ISequence<String>)  // 0, what?

(marc hoffman) #2

Hmm:

public var keys: ISequence<Key> { ... }

(marc hoffman) #3

It looks like it’s a sequence, just fine, just the “is” check fails. ie

for k in keys { // works
    print(k)
}

works fine. logging.


(RemObjects) #4

Thanks, logged as bugs://81822


(Marko Havu) #5

It does look like it’s a sequence, but is disagrees and contains is not found. BTW, shouldn’t range in fact be Array<Int>, not ISequence<Int>?


(marc hoffman) #6

Well, I don’t believe ISequence defines .contains right now, but that is a separate issue. its is an ISequence. In fact, it’s an NSArray, which implements ISequence.

Why?


(Marko Havu) #7

For CLR it maps to self.Contains. Other platforms should have contains, right? Isn’t it mapped to NSArray.containsObject in Cocoa?

Because the sequence is wrapped in an [Int]().


(marc hoffman) #8

Well. ISequence on .NET is an alias for IEnumerable, so it gets all the LINQ operators. We provide those same operators on Cocoa, but they match the .NET names, eg .Contains should work, if RemObjects.Elements.RTL.Linq is in the import clause.

I could see an argument for adding the Swift standard parameters as an extension class in Swift Base Library. you fill like contributing that as a pull request?


(marc hoffman) #9

Yeah, but a sequence can get generated lazily, an array not. how would (0...MAX_INT) work? you’d get out of memory if it generates an array, but a sequence you can lazily loop over all billions of values.


(Marko Havu) #10

For sequence, certainly, but since I’m calling an array constructor with that sequence, I’m expecting an array to be instantiated. Am I wrong to expect that? What is the use of the [Int]() if it doesn’t create an array?


(Marko Havu) #11

Sure, let’s discuss this in chat.


(Marko Havu) #12

As a clarification: I first thought that the reason Dictionary.keys.contains did not work was because ISequence.contains wasn’t implemented for platforms other than .NET, but then I found this issue. Based on that, I tested with ISequence<Int>.contains, and it does indeed work, so the issue seems to be with Dictionary.keys.


(marc hoffman) #13

It’s curious. I get this:

let keys: ISequence<String> = dict.keys
let keys2 = dict.keys
let a = dict.keys.Contains("x")
let b = keys.Contains("x")
let c = keys2.Contains("x")
let d = dict.keys.contains("x") // E464 Case for identifier "contains" does not match original case "Contains"
let e = keys.contains("x") // E464 Case for identifier "contains" does not match original case "Contains"
let f = keys2.contains("x") // E464 Case for identifier "contains" does not match original case "Contains"

Upper-case Contains works on all three, lowercase gives a case mismatch; somehow the compiler sees the LINQ operator, but not the extension from SwiftBaseLibrary. Which makes sense given:

    #if CLR //|| ISLAND
    public func contains(_ item: T) -> Bool {
        return self.Contains(item)
    }
    #endif

(not sure why, I’ll fix that).

can you elaborate/give me a concrete example of what you mean here?


(marc hoffman) #14

Yeah, nevermind my last comment re that, I misunderstood. That said, I still don’t quite understand what the question/issue is:

range is an Array.


(Marko Havu) #15

Ah, it’s both. My bad.


(Marko Havu) #16

It might have been that my sequence was also an array, so it was actually Array.contains that was called.


(marc hoffman) #17

it’s actually not (on Cocoa), due to a limitation on the Cocoa platform (which we’ll fix with Toffee V2, that makes it so that structs cannot implement interfaces (only classes can). Its what makes some extra and annoying .GetSequence calls necessary, right now (but hopefully thats a short-lived issue, once V2 is ready in a month or two).


(Marko Havu) #18

I get this on Cocoa:

let range = [Int]((1 ... 256).GetSequence())
print(range is Array<Int>)  // 1
print(range is ISequence<Int>)  // 1

(Although this might be an issue with is, as you stated above.)


(marc hoffman) #19

That’s odd, that last one should fail, on Cocoa, until we move to Toffee V2… strange. You’re sure you have latest SwiftBaseLibrart where Array is a struct? what does print(typeOf(range)) print?


(Marko Havu) #20

I use the latest SBL from GitHub, and print(typeOf(range)) gives me E: Cannot get typeOf() for value types on Cocoa. (That’s why I used is in the above examples.)