Por que uma chamada de function exige o nome do parâmetro no Swift?

Eu tenho essa function em uma class:

func multiply(factor1:Int, factor2:Int) -> Int{ return factor1 * factor2 } 

Eu tento chamar a function usando isso:

 var multResult = calculator.multiply(9834, 2321) 

O problema é que o compilador quer parecer mais com isso:

 var multResult = calculator.multiply(9834, factor2: 2321) 

Por que o primeiro causa um erro?

Atualização para o Swift 2.0 : Agora as funções se comportam de maneira idêntica aos methods e, para ambos, por padrão:

  • o primeiro parâmetro não possui nome externo; e
  • os outros parâmetros têm um nome externo idêntico ao nome interno.

Além disso, as regras abaixo ainda se aplicam, exceto que a syntax # abreviada desapareceu.


Aqui está uma resposta mais geral: as funções se comportam de maneira diferente quando definidas como funções verdadeiras fora de uma class e quando definidas como methods. Além disso, os methods init têm uma regra especial.


Funções

Suponha que você defina isso:

 func multiply1(f1: Double, f2: Double) -> Double { return f1 * f2 } 

Nomes de parâmetros são aqui apenas locais para a function e não podem ser usados ​​ao chamar a function:

 multiply1(10.0, 10.0) 

Se você quiser forçar o uso de parâmetros nomeados ao chamar a function, você pode. Prefixe cada declaração de parâmetro com seu nome externo . Aqui, o nome externo de f1 é f1param e, para f2 , usamos a abreviação onde f1param por # para indicar que o nome local também deve ser usado como o nome externo:

 func multiply2(f1param f1: Double, #f2: Double) -> Double { return f1 * f2 } 

Então, os parâmetros nomeados devem ser usados:

 multiply2(f1param: 10.0, f2: 10.0) 

Métodos

As coisas são diferentes para methods. Por padrão, todos, exceto o primeiro parâmetro, são nomeados, como você descobriu. Suponha que tenhamos isso e consideremos o método multiply1 :

 class Calc { func multiply1(f1: Double, f2: Double) -> Double { return f1 * f2 } func multiply2(f1param f1: Double, f2: Double) -> Double { return f1 * f2 } func multiply3(f1: Double, _ f2: Double) -> Double { return f1 * f2 } } 

Então, você tem que usar o nome do segundo (e seguinte, se houver) parâmetros:

 let calc = Calc() calc.multiply1(1.0, f2: 10.0) 

Você pode forçar o uso de um parâmetro nomeado para o primeiro argumento, fornecendo um nome externo para ele, como para funções (ou prefixando seu nome local com # se quiser usar o mesmo nome externo que seu nome local). Então você tem que usá-lo:

 calc.multiply2(f1param: 10.0, f2: 10.0) 

Finalmente, você pode declarar um nome externo de _ para os outros argumentos a seguir, indicando que você deseja chamar seu método sem usar parâmetros nomeados, como este:

 calc.multiply3(10.0, 10.0) 

Nota de interoperabilidade: Se você prefixar a class Calc com a anotação @objc , poderá usá-la a partir do código Objective-C e será equivalente a essa declaração (observe os nomes dos parâmetros):

 @interface Calc - (double)multiply1:(double)f1 f2:(double)f2; - (double)multiply2WithF1param:(double)f1 f2:(double)f2; - (double)multiply3:(double)f1 :(double)f2; @end 

Métodos de boot

A regra difere um pouco pelos methods init , em que todos os parâmetros têm um nome externo por padrão. Por exemplo, isso funciona:

 class Calc { init(start: Int) {} init(_ start: String) {} } let c1 = Calc(start: 6) let c2 = Calc("6") 

Aqui, você precisa especificar start: para a sobrecarga que aceita um Int , mas você deve omiti-lo para a sobrecarga que aceita um String .

Nota de interoperabilidade: esta class seria exportada para o Objective-C assim:

 @interface Calc - (instancetype)initWithStart:(NSInteger)start __attribute__((objc_designated_initializer)); - (instancetype)init:(NSString *)start __attribute__((objc_designated_initializer)); @end 

Fechamentos

Suponha que você defina um tipo de fechamento como este:

 typealias FancyFunction = (f1: Double, f2: Double) -> Double 

Os nomes dos parâmetros se comportarão de maneira muito semelhante àqueles em um método. Você terá que fornecer os nomes para os parâmetros ao chamar o encerramento, a menos que você defina explicitamente o nome externo como _.

Por exemplo, executando o fechamento:

 fund doSomethingInteresting(withFunction: FancyFunction) { withFunction(f1: 1.0, f2: 3.0) } 

Como regra geral: mesmo que você não goste deles, você provavelmente deve tentar usar parâmetros nomeados pelo menos sempre que dois parâmetros tiverem o mesmo tipo, a fim de desambiguá-los. Eu também diria que é bom nomear pelo menos todos os parâmetros Int e Boolean .

Os nomes de parâmetros na chamada de function são chamados de nomes de palavras-chave e eles são rastreados de volta para o idioma Smalltalk.

Classes e objects são frequentemente reutilizados de qualquer outro lugar, ou fazem parte de sistemas complexos muito grandes, e não terão atenção de manutenção ativa por longos períodos de cada vez.

Melhorar a clareza e a legibilidade do código é muito importante nessas situações, pois o código geralmente acaba sendo a única documentação, quando os desenvolvedores estão sob pressão de prazo.

Dar a cada parâmetro um nome de palavra-chave descritivo permite aos mantenedores ver rapidamente qual é o propósito de uma chamada de function olhando para a chamada de function, em vez de se aprofundar no próprio código de function. Isso torna implícito o significado implícito dos parâmetros.

A linguagem mais recente a adotar nomes de palavras-chave para parâmetros em chamadas de function é Rust (link) – descrita como “uma linguagem de programação de sistemas que opera de maneira incrivelmente rápida, previne os segfaults e garante a segurança do thread”.

Sistemas de alta disponibilidade requerem maior qualidade de código. Os nomes de palavras-chave permitem que as equipes de desenvolvimento e manutenção tenham muito mais oportunidades de evitar e detectar erros ao enviar o parâmetro incorreto ou ao chamar parâmetros fora de ordem.

Eles podem ser verbosos ou concisos, mas os Smalltalkers preferem verbos e descritivos para serem concisos e sem sentido. Eles podem se dar ao luxo de ser, porque seu IDE fará a maior parte dessa digitação para eles.

desde que você usou calculator.multiply() no código de exemplo, estou assumindo que esta function é um método do object da calculator .

O Swift herda muitas coisas do objective-c e este é um deles:

Quando no objective-c você faria (hipoteticamente):

 [calculator multiply:@9834 factor2:@2321]; 

o equivalente em Swift é:

 calculator.multiply(9834, factor2:2321); 

Como a function “multiplicar” é um método e, assim como o Objective-c, os parâmetros nos methods fazem parte do nome.

Por exemplo, você pode fazer isso.

 class Calculator { func multiply(factor1:Int, factor2:Int) -> Int{ return factor1 * factor2 } func multiply(factor1:Int, factor2:Int, factor3:Int) -> Int{ return factor1 * factor2 * factor3 } } 

Aqui há dois methods diferentes, com nomes diferentes, multiplicar (fator2) e multiplicar (fator2 fator3).

Esta regra só se aplica aos methods, se você declarar isso como uma function fora de uma class, então a chamada de function não requer o nome do parâmetro.

O motivo é histórico. Foi assim que funcionou em Smalltalk e sobreviveu em seus descendentes. Squeak, Scratch , Blockly , Objective C e Swift.

As linguagens kiddy (Squeak, Scratch e Blockly) mantiveram isso, porque os programadores iniciantes tendem a lutar com a ordem dos arianos e dos parâmetros. Essa foi a razão original pela qual a Smalltalk fez dessa maneira. Eu não sei porque ObjC e Swift decidiram adotar a convenção, mas eles fizeram.

Programa de exemplo de rascunho

Uma nota sobre passar um método como um argumento que não retorna nenhum valor:

 func refresh(obj:Obj, _ method: (Obj)->Void = setValue) { method(element) } func setValue(obj:Obj){ obj.value = "someValue" } refresh(someObj,setValue)