Закрывающий кортеж не поддерживает деструктуризацию в Xcode 9 Swift 4

После глянцевого проекта для Swift 4 в Xcode 9

Я получаю следующую ошибку, о которой я понятия не имею

Параметр закрывающего кортежа '(ключ: _, значение: _) "не поддерживает деструктурирующий

Код:

extension Dictionary
{
    init(elements: [Element]) {
        self.init()
        for (key, value) in elements {
            self[key] = value
        }
    }

    func flatMap<KeyPrime, ValuePrime>(_ transform: (Key, Value) throws -> (KeyPrime, ValuePrime)?) rethrows -> [KeyPrime:ValuePrime] {
        return Dictionary<KeyPrime, ValuePrime>(elements: try flatMap({ (key, value) in
            return try transform(key, value)
        }))
    }
}

На этом этапе возникает ошибка try flatMap({ (key, value)in

Ответ 1

Начнем с определения flatMap для словаря, который выглядит следующим образом:

func flatMap(_ transform: (Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult]

Вы видите, что замыкание transform принимает только один параметр типа Element, где Element является всего лишь typealias для кортежа:

public typealias Element = (key: Key, value: Value)

Итак, первый аргумент и только закрытия должен быть кортежем из двух элементов (key типа key и value типа value).


Теперь, если вы посмотрите на свой код (который компилируется в Swift 3), вы увидите, что это не так, и вы должны спрашивать, почему это работает даже в Swift 3.

try flatMap({ (key, value) in
    return try transform(key, value)
})

Ваше закрытие принимает 2 аргумента вместо одного (key типа key и value типа value). Это работает в Swift 3 благодаря функции, называемой деструктуризацией, где компилятор автоматически преобразует кортеж из 2 элементов в 2 аргумента.

Но эта функция является странной, редко используется и дает большие результаты в большинстве случаев, поэтому она была удалена в Swift 4.
Изменить. Как указано OOPer, эта функция была временно удалена в бета-версии Swift 4, но должна быть добавлена ​​до окончательной версии.

Вместо этого вы должны писать:

try flatMap({ tupleArgument in
    return try transform(tupleArgument.key, tupleArgument.value)
})

И ваша функция flatMap будет выглядеть следующим образом:

func flatMap<KeyPrime, ValuePrime>(_ transform: (Key, Value) throws -> (KeyPrime, ValuePrime)?) rethrows -> [KeyPrime:ValuePrime] {
    return Dictionary<KeyPrime, ValuePrime>(elements: try flatMap({ element in
        return try transform(element.key, element.value)
    }))
}

Ответ 2

Это побочный эффект этого предложения для Swift 4:

SE-0110 Различают типы функций с одним кортежем и несколькими аргументами.

Но некоторые функции, включенные в это предложение, вызвали некоторую регрессию, о которой говорится в этом сообщении списка рассылки эволюции-объявления:

[swift-evolution-announce] [Основная команда] Решение проблемы юзабилити SE-0110 в Swift 4

Таким образом, вы можете ожидать, что в будущей бета-версии или GM-версии Xcode 9 ваш код снова будет хорошо компилироваться. До этого вы можете использовать этот способ обхода:

internal func flatMap<KeyPrime , ValuePrime>(_ transform: (Key, Value) throws -> (KeyPrime, ValuePrime)?) rethrows -> [KeyPrime : ValuePrime] {
    return Dictionary<KeyPrime,ValuePrime>(elements: try flatMap({ pair in
        let (key, value) = pair
        return try transform(key, value)
    }))
}

Кстати, в Swift 4 в Dictionary есть несколько новых инициализаторов, которые принимают Sequence из пары (Key, Value). Например:

init (uniqueKeysWithValues: S)

Ответ 3

Я только что столкнулся с этой ошибкой в результате использования enumerated().map():

Параметр закрывающего кортежа не поддерживает деструктуризацию

Я набрал код:

["foo"].enumerated().map

А затем нажимали Enter 3 раза, пока XCode не завершит автозаполнение шаблона закрытия.

Автозаполнение, похоже, имеет ошибку, которая вызывает вышеуказанную ошибку. Автозаполнение приводит к двойным круглым скобкам ((offset: Int, element: String)), а не к одиночным круглым (offset: Int, element: String).

Я исправил это вручную и смог продолжить:

// Xcode autocomplete suggests:
let fail = ["foo"].enumerated().map { ((offset: Int, element: String)) -> String in
    return "ERROR: Closure tuple parameter does not support destructuring"
}

// Works if you manually replace the "(( _ ))" with "( _ )"
let pass = ["foo"].enumerated().map { (offset: Int, element: String) -> String in
    return "works"
}

Возможно, результат использования бета-версии Xcode 10.0 (10L176w)