2017-07-21 1 views
0

Consultez le code suivant:Dans Swift 3, pourquoi la valeur de retour vide des fonctions, ->(), est-elle parfois seulement déduite, mais d'autres fois, doit être déclarée?

typealias bar =()->() 

let foo:bar = { baz ->() in 
    print("foobar") 
    return() 
} 

let baz = foo() 

print("\(baz)") 

qui délivre en sortie:

foobar

()

Maintenant, si nous changeons cette ligne:

let foo:bar = { baz ->() in 

à ceci:

let foo:bar = { baz in 

... rien ne se passe. Par exemple, ->() est déduit par le compilateur. (Remarque: le compilateur est heureux aussi si nous omettons return().)

Cependant, quant à lui, si nous changeons cette ligne:

typealias bar =()->() 

à ceci:

typealias bar =() 

... alors la compilateur panique et se suicide. Pourquoi?

Cela semble en conflit avec la documentation de Swift, qui définit () (alias Void) comme:

Le type de retour des fonctions qui ne spécifie pas explicitement un type de retour

Selon cette déclaration, puisque typealias bar =()->() spécifie explicitement un type de retour, donc par définition, il ne peut pas retourner () -et pourtant il le fait! C'est complètement illogique et contradictoire.

Est-ce que quelqu'un voudrait bien m'expliquer ce qu'il pense de cela? Quelle est la justification ici? Si ->() est supposé être toujours inféré alors pourquoi l'avoir? Pourquoi ne pas simplement dire que () est toujours une fonction, telle que ()() retourne toujours ()?

Répondre

2

Si je ne me trompe pas, c'est le cas, vous parlez:

typealias bar =() 

let foo: bar = { baz ->() in 
    print("foobar") 
    return() 
} 

La main droite d'une affectation est une fermeture de type () ->(), qui est non évaluée. foo devrait avoir un bar de type (()), mais ce ne serait le cas si la fermeture a été évaluée, par exemple .:

let foo: bar = { baz ->() in 
    print("foobar") 
    return() 
}() 
+0

Oui mais selon la définition de '()', '()' et '() ->()' doit avoir un sens équivalent, puisque '()' est le type retourné par les fonctions qui ne spécifient pas de type de retour. Cependant, pour déclarer une typealias pour une fonction, vous devez toujours spécifier un type de retour, d'après ce que je peux dire, alors peut-être que leur documentation est mal formulée. – CommaToast

+0

'Cependant, pour déclarer une typealias pour une fonction, vous devez toujours spécifier un type de retour, d'après ce que je peux dire. – Alexander

+0

'() et() ->() devraient être équivalents en ce sens qu'ils ne le sont pas. Il y a une partie clé dans cette définition que vous avez citée, que je pense que vous avez manquée: void est le type de retour *** *** qui ne spécifie pas explicitement un type de retour – Alexander

1

J'aime Alexander's answer, mais voici un autre angle qui pourrait se révéler utile.

Avec une déclaration typealias bar = ..., vous définissez un type. Cette définition doit être complète et sans ambiguïté.Sinon, le système de type ne pourra pas vérifier si les choses que vous déclarez plus tard sont des membres du type. Cette déclaration fournit la signature complète du type - pour les types de fonction/fermeture, c'est-à-dire son ensemble de types de paramètres et son type de retour, même si les deux sont Void (aka ()).

Avec un let foo: bar = ... vous déclarez une valeur et prétendez qu'il doit être membre du type. Parce que sont déjà connus des parties de la définition du type (grâce aux typealias), vous n'avez pas besoin de les répéter lorsque vous déclarez un membre du type:

  • Votre fermeture n'a pas besoin de déclarer son type de retour parce que cela fait partie du type de fonction auquel votre fermeture se conforme. Vous pouvez return n'importe quelle valeur du type attendu dans votre fermeture. (Ou, puisque votre type de fonction définit un type de retour Void, vous pouvez return rien.)
  • Votre fermeture n'a pas besoin de déclarer les types de ses paramètres car les typealias l'ont déjà fait. (Ou, étant donné que le type de votre seul paramètre est Void, vous ne avez pas besoin de déclarer des paramètres du tout.)

Laissant de bits de la syntaxe de fermeture lors de la définition d'une fermeture dont le type est connu est l'un des types de Swift inférence caractéristiques. Vous pouvez considérer cela comme équivalent à comment, si vous avez un enum Foo { case one, two, three } et un func bar(_ foo: Foo), vous êtes autorisé à passer seulement .one dans un appel (bar(.one)). Foo.one est le nom qualifié complet pour cette constante, mais seulement .one suffit car Swift peut déduire le type Foo.