Diferencia entre método y función en Scala

Leí Scala Functions (parte de Another tour of Scala ). En esa publicación, declaró:

Los métodos y las funciones no son lo mismo

Pero él no explicó nada al respecto. ¿Qué estaba tratando de decir?

Jim tiene esto bastante cubierto en su publicación de blog , pero estoy publicando una sesión informativa aquí para referencia.

Primero, veamos qué nos dicen las especificaciones de Scala. El Capítulo 3 (tipos) nos informa sobre Tipos de funciones (3.2.9) y Tipos de métodos (3.3.1). El Capítulo 4 (declaraciones básicas) habla de Declaración de Valor y Definiciones (4.1), Declaración de Variables y Definiciones (4.2) y Declaraciones y Definiciones de Funciones (4.6). El Capítulo 6 (expresiones) habla de Funciones Anónimas (6.23) y Valores de Método (6.7). Curiosamente, los valores de función se hablan una vez en 3.2.9, y en ningún otro lado.

Un tipo de función es (aproximadamente) un tipo de la forma (T1, …, Tn) => U , que es una forma abreviada de la FunctionN N de la biblioteca estándar. Las funciones anónimas y los valores de los métodos tienen tipos de funciones, y los tipos de funciones se pueden usar como parte de declaraciones y definiciones de valores, variables y funciones. De hecho, puede ser parte de un tipo de método.

Un tipo de método es un tipo sin valor . Eso significa que no hay ningún valor, ningún objeto, ninguna instancia, con un tipo de método. Como se mencionó anteriormente, un valor de método en realidad tiene un tipo de función . Un tipo de método es una statement de def , todo sobre una def excepto su cuerpo.

Las declaraciones y definiciones de valor y las declaraciones y definiciones de variable son declaraciones val y var , que incluyen tanto el tipo como el valor , que pueden ser, respectivamente, tipo de función y funciones anónimas o valores de método . Tenga en cuenta que, en la JVM, estos (valores de método) se implementan con lo que Java llama “métodos”.

Una statement de función es una statement def , que incluye tipo y cuerpo . La parte de tipo es el Tipo de método, y el cuerpo es una expresión o un bloque . Esto también se implementa en la JVM con lo que Java llama “métodos”.

Finalmente, una función anónima es una instancia de un tipo de función (es decir, una instancia del rasgo FunctionN ), y un valor de método es la misma cosa. La distinción es que un Método de Valor se crea a partir de métodos, ya sea mediante la postfijación de un guión bajo ( m _ es un valor de método correspondiente a la “statement de función” ( def ) m ), o mediante un proceso llamado expansión eta , que es como un cambio automático de método a función.

Eso es lo que dicen las especificaciones, así que permítanme exponerlo: ¡no usamos esa terminología! Conduce a demasiada confusión entre la llamada “statement de función” , que es una parte del progtwig (capítulo 4 – declaraciones básicas) y “función anónima” , que es una expresión, y “tipo de función” , que es, bien un tipo – un rasgo.

La terminología a continuación, y utilizada por experimentados progtwigdores de Scala, hace un cambio de la terminología de la especificación: en lugar de decir statement de función , decimos método . O incluso statement de método. Además, observamos que las declaraciones de valores y las declaraciones de variables también son métodos para fines prácticos.

Entonces, dado el cambio de terminología anterior, aquí hay una explicación práctica de la distinción.

Una función es un objeto que incluye uno de los rasgos de FunctionX , como Function0 , Function1 , Function2 , etc. También podría incluir PartialFunction , que realmente extiende Function1 .

Veamos la firma tipo para uno de estos rasgos:

 trait Function2[-T1, -T2, +R] extends AnyRef 

Este rasgo tiene un método abstracto (también tiene algunos métodos concretos):

 def apply(v1: T1, v2: T2): R 

Y eso nos dice todo lo que hay que saber al respecto. Una función tiene un método de apply que recibe N parámetros de los tipos T1 , T2 , …, TN , y devuelve algo del tipo R Es contravariante en los parámetros que recibe, y covariante en el resultado.

Esa variación significa que una Function1[Seq[T], String] es un subtipo de Function1[List[T], AnyRef] . Ser un subtipo significa que se puede usar en lugar de él. Uno puede ver fácilmente que si voy a llamar a f(List(1, 2, 3)) y espero que AnyRef vuelva, cualquiera de los dos tipos anteriores funcionaría.

Ahora, ¿cuál es la similitud de un método y una función? Bueno, si f es una función y m es un método local para el scope, ambos pueden llamarse así:

 val o1 = f(List(1, 2, 3)) val o2 = m(List(1, 2, 3)) 

Estas llamadas son realmente diferentes, porque la primera es solo un azúcar sintáctico. Scala lo expande a:

 val o1 = f.apply(List(1, 2, 3)) 

Que, por supuesto, es una llamada de método en el objeto f . Las funciones también tienen otros azúcares sintácticos en su beneficio: literales de función (dos de ellos, en realidad) y (T1, T2) => R firmas de tipo (T1, T2) => R Por ejemplo:

 val f = (l: List[Int]) => l mkString "" val g: (AnyVal) => String = { case i: Int => "Int" case d: Double => "Double" case o => "Other" } 

Otra similitud entre un método y una función es que el primero se puede convertir fácilmente en el segundo:

 val f = m _ 

Scala expandirá eso , suponiendo que m type es (List[Int])AnyRef into (Scala 2.7):

 val f = new AnyRef with Function1[List[Int], AnyRef] { def apply(x$1: List[Int]) = this.m(x$1) } 

En Scala 2.8, en realidad usa una clase AbstractFunction1 para reducir el tamaño de las clases.

Tenga en cuenta que no se puede convertir a la inversa, de una función a un método.

Los métodos, sin embargo, tienen una gran ventaja (bueno, dos, pueden ser un poco más rápidos): pueden recibir parámetros de tipo . Por ejemplo, mientras que f arriba puede necesariamente especificar el tipo de List que recibe ( List[Int] en el ejemplo), m puede parametrizarlo:

 def m[T](l: List[T]): String = l mkString "" 

Creo que esto cubre casi todo, pero estaré encantado de complementar esto con las respuestas a cualquier pregunta que pueda quedar.

Una gran diferencia práctica entre un método y una función es lo que significa el return . solo devuelve retornos de un método. Por ejemplo:

 scala> val f = () => { return "test" } :4: error: return outside method definition val f = () => { return "test" } ^ 

El regreso de una función definida en un método produce un retorno no local:

 scala> def f: String = { | val g = () => { return "test" } | g() | "not this" | } f: String scala> f res4: String = test 

Mientras que regresar de un método local solo regresa de ese método.

 scala> def f2: String = { | def g(): String = { return "test" } | g() | "is this" | } f2: String scala> f2 res5: String = is this 

función Una función se puede invocar con una lista de argumentos para producir un resultado. Una función tiene una lista de parámetros, un cuerpo y un tipo de resultado. Las funciones que son miembros de una clase, rasgo u objeto singleton se llaman métodos . Las funciones definidas dentro de otras funciones se llaman funciones locales. Las funciones con el tipo de resultado de la Unidad se llaman procedimientos. Las funciones anónimas en el código fuente se llaman literales de funciones. En tiempo de ejecución, los literales de funciones se instancian en objetos llamados valores de función.

Progtwigción en Scala Second Edition. Martin Odersky – Lex Spoon – Bill Venners

Deje que diga que tiene una lista

 scala> val x =List.range(10,20) x: List[Int] = List(10, 11, 12, 13, 14, 15, 16, 17, 18, 19) 

Definir un método

 scala> def m1(i:Int)=i+2 m1: (i: Int)Int 

Definir una función

 scala> (i:Int)=>i+2 res0: Int => Int =  scala> x.map((x)=>x+2) res2: List[Int] = List(12, 13, 14, 15, 16, 17, 18, 19, 20, 21) 

Método de aceptación de argumento

 scala> m1(2) res3: Int = 4 

Definición de función con val

 scala> val p =(i:Int)=>i+2 p: Int => Int =  

Argumento para funcionar es opcional

  scala> p(2) res4: Int = 4 scala> p res5: Int => Int =  

La discusión sobre el Método es Obligatoria

 scala> m1 :9: error: missing arguments for method m1; follow this method with `_' if you want to treat it as a partially applied function 

Consulte el siguiente tutorial que explica el paso de otras diferencias con ejemplos como otros ejemplos de diferencias con el método Vs Función, Uso de funciones como variables, creación de funciones que devuelven funciones

Las funciones no son compatibles con los parámetros predeterminados. Los métodos lo hacen. La conversión de un método a una función pierde los parámetros predeterminados. (Scala 2.8.1)

Aquí hay un buen artículo del cual se toman la mayoría de mis descripciones. Solo una breve comparación de Funciones y Métodos con respecto a mi comprensión. Espero eso ayude:

Funciones : Básicamente son un objeto. Más precisamente, las funciones son objetos con un método de aplicación; Por lo tanto, son un poco más lentos que los métodos debido a su sobrecarga. Es similar a los métodos estáticos en el sentido de que son independientes de un objeto que se invocará. Un simple ejemplo de una función es como el siguiente:

 val f1 = (x: Int) => x + x f1(2) // 4 

La línea de arriba no es otra cosa que asignar un objeto a otro como object1 = object2. En realidad, el objeto2 en nuestro ejemplo es una función anónima y el lado izquierdo recibe el tipo de objeto debido a eso. Por lo tanto, ahora f1 es un objeto (Función). La función anónima es en realidad una instancia de la Función 1 [Int, Int] que significa una función con 1 parámetro de tipo Int y valor de retorno de tipo Int. Llamar a f1 sin los argumentos nos dará la firma de la función anónima (Int => Int =)

Métodos : no son objetos sino asignados a una instancia de una clase, es decir, un objeto. Exactamente lo mismo que el método en java o funciones miembro en c ++ (como señaló Raffi Khatchadourian en un comentario a esta pregunta ), etc. Un ejemplo simple de un método es como sigue:

 def m1(x: Int) = x + x m1(2) // 4 

La línea anterior no es una simple asignación de valores sino una definición de un método. Cuando invoque este método con el valor 2 como la segunda línea, la x se sustituye por 2 y se calculará el resultado y obtendrá 4 como salida. Aquí obtendrá un error si simplemente escribe m1 porque es un método y necesita el valor de entrada. Al usar _ puedes asignar un método a una función como bramido:

 val f2 = m1 _ // Int => Int =