web-dev-qa-db-fra.com

Closure Tuple ne prend pas en charge la déstructuration dans Xcode 9 Swift 4

Après le projet de gloss pour Swift 4 dans Xcode 9

Je reçois une erreur suivante que je n'ai aucune idée

Le paramètre Tuple de fermeture '(clé: _, valeur: _)' ne prend pas en charge la déstructuration

Code:

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)
        }))
    }
}

Une erreur survient à ce stade try flatMap({ (key, value)in

28
Mihir Mehta

Commençons par la définition de flatMap pour un dictionnaire qui est le suivant:

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

Vous voyez que la fermeture transform ne prend que n paramètre de type ElementElement n'est qu'un typealias pour un tuple:

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

Ainsi, le premier et uniquement argument de la fermeture doit être un Tuple de deux éléments (key de type Key et value de type Value).


Maintenant, si vous regardez votre code (qui se compile en Swift 3), vous verrez que ce n'est pas le cas, et vous devriez vous demander pourquoi cela fonctionne-t-il même dans Swift 3.

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

Votre fermeture prend 2 arguments au lieu d'un (key de type Key et value de type Value). Cela fonctionne en Swift 3 grâce à une fonctionnalité appelée destructuring où le compilateur transformera automatiquement un Tuple de 2 éléments en 2 arguments.

Mais cette fonctionnalité est étrange, rarement utilisée et donne la plupart du temps des résultats inattendus, elle a donc été supprimée dans Swift 4.
Edit: Comme indiqué par OOPer, cette fonctionnalité a été temporairement supprimée dans Swift 4 beta mais doit être rajoutée avant que la version finale soit en dehors.

Au lieu de cela, vous devriez écrire:

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

Et votre fonction flatMap devient:

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)
    }))
}
24
deadbeef

C'est un effet secondaire de cette proposition pour Swift 4:

SE-0110 Distinguer entre les types de fonction à un seul tuple et à plusieurs arguments .

Mais certaines fonctionnalités incluses dans cette proposition ont provoqué une régression qui est abordée dans ce post de la liste de diffusion évolution-annonce :

[Swift-evolution-announce] [Core team] Résolution de la régression de l'utilisabilité SE-0110 dans Swift 4

Donc, vous pouvez vous attendre dans la future version bêta ou GM version de Xcode 9, votre code se compilera bien à nouveau. Jusque-là, vous pouvez utiliser ce type de solution:

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)
    }))
}

Par ailleurs, dans Swift 4, Dictionary a quelques nouveaux initialiseurs qui prennent Sequence de (Key, Value) paire. Par exemple:

init (uniqueKeysWithValues: S)

7
OOPer

Je viens de rencontrer cette erreur suite à l'utilisation de enumerated().map():

Le paramètre Closure Tuple ne prend pas en charge la déstructuration

J'ai tapé le code:

["foo"].enumerated().map(

Et puis j'ai continué à appuyer Enter jusqu'à ce que Xcode termine automatiquement le passe-partout de fermeture.

La saisie semi-automatique a apparemment un bogue qui provoque l'erreur ci-dessus. La saisie semi-automatique produit une double parenthèse ((offset: Int, element: String)) plutôt que des parenthèses simples (offset: Int, element: String).

Je l'ai corrigé manuellement et j'ai pu continuer:

// 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"
}

Peut-être le résultat de l'utilisation de Xcode 10.0 beta (10L176w)

3
pkamb