¿Por qué una llamada a función requiere el nombre del parámetro en Swift?

Tengo esta función en una clase:

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

Intento llamar a la función usando esto:

 var multResult = calculator.multiply(9834, 2321) 

El problema es que el comstackdor quiere que se vea más como esto:

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

¿Por qué el primero causa un error?

Actualización para Swift 2.0 : ahora las funciones se comportan de manera idéntica a los métodos, y para ambos, de forma predeterminada:

  • el primer parámetro no tiene un nombre externo; y
  • los otros parámetros tienen un nombre externo idéntico al nombre interno.

Aparte de eso, las reglas a continuación aún se aplican, excepto que la syntax de # taquigrafía ya no existe.


Aquí hay una respuesta más general: las funciones se comportan de manera diferente cuando se definen como verdaderas funciones fuera de una clase, y cuando se definen como métodos. Además, los métodos init tienen una regla especial.


Funciones

Supongamos que defines esto:

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

Los nombres de los parámetros están aquí solo localmente para la función, y no se pueden usar cuando se llama a la función:

 multiply1(10.0, 10.0) 

Si desea forzar el uso de parámetros con nombre al llamar a la función, puede hacerlo. Prefija cada statement de parámetro con su nombre externo . Aquí, el nombre externo de f1 es f1param , y para f2 , usamos la abreviatura donde lo prefijamos por # para indicar que el nombre local se usará también como el nombre externo:

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

Entonces, los parámetros nombrados deben ser utilizados:

 multiply2(f1param: 10.0, f2: 10.0) 

Métodos

Las cosas son diferentes para los métodos. Por defecto, todos menos el primer parámetro son nombrados, como descubriste. Supongamos que tenemos esto, y consideramos el método de 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 } } 

Luego, debe usar el nombre del segundo parámetro (y si es el caso):

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

Puede forzar el uso de un param nombrado para el primer argumento proporcionando un nombre externo para él, como para funciones (o prefijando su nombre local con # si desea usar el mismo nombre externo que su nombre local). Entonces, debes usarlo:

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

Finalmente, puede declarar un nombre externo de _ para los otros argumentos siguientes, lo que indica que desea llamar a su método sin usar parámetros con nombre, como este:

 calc.multiply3(10.0, 10.0) 

Nota de interoperabilidad: si @objc la class Calc con la anotación @objc , puedes utilizarla desde el código Objective-C, y es equivalente a esta statement (mira los nombres de los 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 Init

La regla difiere un poco para los métodos init , donde todos los parámetros tienen un nombre externo por defecto. Por ejemplo, esto funciona:

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

Aquí, debe especificar start: para la sobrecarga que acepta un Int , pero debe omitirlo para la sobrecarga que acepta un String .

Nota de interoperabilidad: esta clase se exportaría a Objective-C de esta manera:

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

Cierres

Supongamos que define un tipo de cierre como este:

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

Los nombres de los parámetros se comportarán de manera muy similar a los de un método. Tendrá que proporcionar los nombres de los parámetros cuando llame al cierre a menos que establezca explícitamente el nombre externo en _.

Por ejemplo, ejecutando el cierre:

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

Como regla general: incluso si no le gustan, probablemente debería intentar seguir usando los parámetros nombrados al menos cada vez que dos parámetros tengan el mismo tipo, para desambiguarlos. También diría que es bueno también nombrar al menos todos Boolean parámetros Int y Boolean .

Los nombres de los parámetros en la llamada a la función se llaman nombres de palabras clave, y están siguiendo sus raíces hasta el lenguaje Smalltalk.

Las clases y los objetos a menudo se vuelven a utilizar desde otro lugar, o forman parte de sistemas complejos muy grandes, y no tendrán una atención de mantenimiento activa durante períodos prolongados a la vez.

Mejorar la claridad y legibilidad del código es muy importante en estas situaciones, ya que el código a menudo termina siendo la única documentación, cuando los desarrolladores están bajo presión en el plazo.

Al otorgar a cada parámetro un nombre de palabra clave descriptivo, los mantenedores pueden ver rápidamente el propósito de una función al echar un vistazo a la llamada de función, en lugar de profundizar en el código de la función en sí. Hace que el significado implícito de los parámetros sea explícito.

El último lenguaje para adoptar nombres de palabras clave para los parámetros en las llamadas a funciones es Rust (enlace) , que se describe como “un lenguaje de progtwigción de sistemas que funciona increíblemente rápido, previene segfaults y garantiza la seguridad del hilo”.

Los sistemas de alta disponibilidad requieren una mayor calidad de código. Los nombres de las palabras clave les permiten a los equipos de desarrollo y mantenimiento muchas más oportunidades de evitar y detectar errores al enviar el parámetro incorrecto o al desordenar los parámetros.

Pueden ser prolijos o superficiales, pero los Smalltalkers prefieren prolijo y descriptivo a escueto y sin sentido. Pueden permitirse estar, porque su IDE les hará la mayor parte de ese tipo de tipeo.

ya que usaste calculator.multiply() en el código de ejemplo, asumo que esta función es un método del objeto de la calculator .

Swift hereda muchas cosas de Object-C y esta es una de ellas:

Cuando en objective-c lo haría (hipotéticamente):

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

el equivalente en Swift es:

 calculator.multiply(9834, factor2:2321); 

Debido a que su función “multiplicar” es un método, y al igual que Objective-c, los parámetros de los métodos forman parte del nombre.

Por ejemplo, puedes hacer esto.

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

Aquí hay dos métodos diferentes, con diferentes nombres, multiplicar (factor2) y multiplicar (factor2 factor3).

Esta regla solo se aplica a los métodos, si declara esto como una función fuera de una clase, entonces la llamada a la función no requiere el nombre del parámetro.

La razón es histórica. Así es como funcionó en Smalltalk y sobrevivió a sus descendientes. Squeak, Scratch , Blockly , Objective C y Swift.

Los lenguajes kiddy (Squeak, Scratch y Blockly) se aferran a él, porque los progtwigdores principiantes tienden a luchar con el orden arity y de parámetros. Esa fue la razón original por la que Smalltalk lo hizo de esa manera. No sé por qué ObjC y Swift decidieron adoptar la convención, pero lo hicieron.

Programa de ejemplo Scratch

Una nota sobre pasar un método como argumento que no devuelve ningún valor:

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