web-dev-qa-db-fra.com

Pourquoi le compilateur Swift ne peut-il pas déduire le type de cette fermeture?

J'écrivais donc du code pour différencier plusieurs versions de mon application:

static var jsonURLNL =  {
    if ProcessInfo.processInfo.environment["CONSUMER"] != nil {
        return URL(string: "consumerURL")!
    }
    return URL(string: "professionalURL")!
}()

Mais j'ai une erreur de compilation:

Impossible de déduire le type de retour de fermeture complexe; ajouter un type explicite pour lever l'ambiguïté

Pourquoi le compilateur Swift ne sait-il pas que cela renverra un URL? Je pense que c'est assez évident dans ce cas.

Mon objectif avec cette question n'est pas de faire une critique sur Xcode ou Swift, c'est d'augmenter ma connaissance de la façon dont le compilateur déduit les types dans Swift.

13
vrwim

Le type de retour d'une fermeture n'est déduit automatiquement que si la fermeture se compose d'une expression unique, par exemple:

static var jsonURLNL =  { return URL(string: "professionalURL")! }()

ou si le type peut être déduit du contexte d'appel :

static var jsonURLNL: URL =  {
    if ProcessInfo.processInfo.environment["CONSUMER"] != nil {
        return URL(string: "consumerURL")!
    }
    return URL(string: "professionalURL")!
}()

ou

static var jsonURLNL = {
    if ProcessInfo.processInfo.environment["CONSUMER"] != nil {
        return URL(string: "consumerURL")!
    }
    return URL(string: "professionalURL")!
}() as URL

Exemples simplifiés: Cette fermeture à une seule expression compile:

let cl1 = { return 42 }

mais cette fermeture multi-expression ne:

let cl2 = { print("Hello"); return 42 }
// error: unable to infer complex closure return type; add explicit type to disambiguate

Les lignes suivantes se compilent car le type est déduit du contexte:

let cl3 = { print("Hello"); return 42 } as () -> Int

let y1: Int = { print("Hello"); return 42 }()

let y2 = { print("Hello"); return 42 }() as Int

Voir aussi la citation de Jordan Rose dans cette discussion de liste de diffusion :

L'inférence de type de Swift est actuellement orientée sur les instructions, il n'y a donc pas de moyen facile de faire l'inférence [de fermeture d'instructions multiples]. C'est au moins en partie une préoccupation au moment de la compilation: le système de types de Swift permet beaucoup plus de conversions possibles que, par exemple, Haskell ou OCaml, donc résoudre les types pour une fonction multi-instructions entière n'est pas un problème trivial, peut-être pas un problème traitable.

et le rapport de bogue SR-157 .

(Les liens et le devis sont copiés de Comment le contrat API flatMap transforme l'entrée facultative en résultat non facultatif? ).

15
Martin R

L'inférence du type de retour de la fonction anonyme peut sembler facile à vous , mais il s'avère qu'il serait extrêmement difficile de l'intégrer dans le compilateur.

Par conséquent, il n'est généralement pas autorisé d'écrire un initialiseur définir et appeler sans spécifier le type dans le cadre de la déclaration.

Mais ce n'est pas un gros problème. Il vous suffit de spécifier le type!

static var jsonURLNL : URL = ...

(Ce que je fais dans ma tête est de traiter l'inclusion du type comme faisant partie de la syntaxe des initialiseurs de définition et d'appel. Donc je l'inclus toujours. Donc je ne rencontre jamais ce message d'erreur!)


Après coup: Considérez ce qui suit:

static var jsonURLNL =  {
    if ProcessInfo.processInfo.environment["CONSUMER"] != nil {
        return "Howdy"
    }
    return URL(string: "professionalURL")!
}()

Voyez-vous le problème? Maintenant rien ne peut être déduit, même par un être humain, car vous avez accidentellement été incohérent dans vos types de retour. Mais si vous écrivez : URL, maintenant le compilateur sait ce que vous supposiez renvoyer et sait que "Howdy" n'est pas le bon type.

3
matt