A syntax error I'm not understanding...

I'm defining a typealias for a set, and then creating an extension for the new typealias. When I do this, I'm getting an odd syntax error. Any/all guidance appreciated.

typealias IntSet = Set<Int>

extension IntSet {
    func aFunction() -> Set<String> {
        let array: [String] = self.map { "\($0)" }
        return Set(array)
    }
}

At the return line, I get the following syntax error: Cannot convert return expression of type 'Set<Int>' to return type 'Set<String>'

Even if I replace the return line with the following, I get the same compile error

        return Set("array")

Replies

I suppose the Set here may refers to Set<Int> because you are adding code in the Set<Int> extension, the compiler are using the minimum scope of the defination. So try to use .init(array) or Set<String>(array) would be fine.

Problem is that map does not work on Set: https://stackoverflow.com/questions/29107928/swift-map-extension-for-set

Even this does not compile:

extension IntSet {
    func aFunction() -> Set<String> {
        let array: [String] = self.map { "\($0)" }
        print(array)            // This prints correctly
        let aSet = Set(array)  // ERROR: No exact matches in call to initializer 
        return [] //Set(array)  // No error here
    }
}

If you work on array, no issue

typealias IntSet = [Int] //Set<Int>

extension IntSet {
    func aFunction() -> Set<String> {
        let array: [String] = self.map { "\($0)" }
        return Set(array)
    }
}

I tried what is proposed in the referenced link, to no avail.

extension Set {

    /// Map, but for a `Set`.
    /// - Parameter transform: The transform to apply to each element.
    func map<T>(_ transform: (Element) throws -> T) rethrows -> Set<T> {
        var tempSet = Set<T>()

        try forEach {
            tempSet.insert(try transform($0))
        }

        return tempSet
    }
}

I got it working by building the set manually

extension IntSet {
    func aFunction() -> Set<String> {
        var set3: Set<String> = []
        for s in self {
            set3.insert(String(s))
        }
        return set3
    }
}

But building from an array did not work…

extension IntSet {
    func aFunction() -> Set<String> {
        var array2: [String] = []
        for s in self {
            array2.append(String(s))
        }
        return Set(array2) // Cannot convert return expression of type 'Set<Int>' to return type 'Set<String>'
    }
}

Ihe issue here is that your type alias is effectively expanded to code like this:

extension Set where Element == Int {
    … your code here …
}

Within that code, a standalone Set is being inferred to be an alias for Self, that is, Set<Int>. To override that you need to explicitly specify the type:

extension Set where Element == Int {
    func aFunction() -> Set<String> {
        let array: [String] = self.map { "\($0)" }
        return Set<String>(array)
    }
}

Honestly, I’m not sure if that’s a bug or not. On the one hand, this inference makes sense in general. OTOH, you do have an explicit type available (because the return means that the type must be Set<String>).

If you just want to fix your code, the above will do it. If you want to know whether this is a bug or not, I recommend that you ask that over on Swift Forums.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

ps Claude31 wrote:

Problem is that map does not work on Set

It’s not that map(_:) doesn’t work on Set, it’s that it can’t work. When you map elements in a container, it’s clear that the number of output elements must match the number of input elements. However, that’s not possible to guarantee with Set because the mapping could produce duplicate elements in the new type.

Consider this mapping:

func isEven(_ i: Int) -> Bool {
    i.isMultiple(of: 2)
}

It makes sense for Array:

let aIn = [1, 2, 3, 4, 5]
let oOut = aIn.map(isEven(_:))
print(oOut) // [false, true, false, true, false]

but the logic falls apart for Set:

let sIn = Set([1, 2, 3, 4, 5])
let sOut = Set(sIn.map(isEven(_:)))
print(sOut) // [false, true]

That’s the same reason where Dictionary has a mapValues(_:) method but no mapKeys(_:) method, and that its map(…) method takes a uniquing functioning.