¿Cuál es la función de aplicación en Scala?

311

Nunca lo entendí a partir de los nombres inventados de desorden y verbo (¡una AddTwoclase tiene un applyque agrega dos!) Ejemplos.

Entiendo que es azúcar sintáctico, por lo que (deduje del contexto) debe haber sido diseñado para hacer que un código sea más intuitivo.

¿Qué significado da una clase con una applyfunción? ¿Para qué se utiliza y con qué fines mejora el código (desordenar, sustantivos verbales, etc.)?

¿Cómo ayuda cuando se usa en un objeto complementario?

Jesvin Jose
fuente
44
Incluso una búsqueda rápida en Google trae muchos buenos artículos. Aquí hay uno: jackcoughonsoftware.blogspot.com/2009/01/…
om-nom-nom
66
en realidad es lo mismo que un constructor en Java / C ++ pero puede devolver el valor y tiene el nombre 'aplicar'
ses
Es un método, no una función. La distinción entre métodos y funciones es muy importante en Scala.
derecha el
1
Para dar más detalles sobre Zoidberg, "Los métodos son solo funciones que pueden acceder al estado de la clase" twitter.github.io/scala_school/basics.html En general, esto se aplica a más de Scala stackoverflow.com/questions/155609/…
DharmaTurtle

Respuestas:

641

Los matemáticos tienen sus propias formas divertidas, así que en lugar de decir "entonces llamamos a la función fpasándola xcomo parámetro", como diríamos los programadores, hablan de "aplicar la función fa su argumento x".

En matemáticas e informática, Apply es una función que aplica funciones a argumentos.
Wikipedia

applysirve para cerrar la brecha entre paradigmas orientados a objetos y funcionales en Scala. Cada función en Scala se puede representar como un objeto. Cada función también tiene un tipo OO: por ejemplo, una función que toma un Intparámetro y devuelve un Inttipo OO tendrá Function1[Int,Int].

 // define a function in scala
 (x:Int) => x + 1

 // assign an object representing the function to a variable
 val f = (x:Int) => x + 1

Dado que todo es un objeto en Scala fahora se puede tratar como una referencia al Function1[Int,Int]objeto. Por ejemplo, podemos llamar al toStringmétodo heredado de Any, eso habría sido imposible para una función pura, porque las funciones no tienen métodos:

  f.toString

O podríamos definir otro Function1[Int,Int]objeto llamando al composemétodo fy encadenando dos funciones diferentes juntas:

 val f2 = f.compose((x:Int) => x - 1)

Ahora, si queremos ejecutar realmente la función, o como dice el matemático "aplicar una función a sus argumentos", llamaríamos al applymétodo en el Function1[Int,Int]objeto:

 f2.apply(2)

Escribir f.apply(args)cada vez que desee ejecutar una función representada como un objeto es la forma Orientada a objetos, pero agregaría un montón de desorden al código sin agregar mucha información adicional y sería bueno poder usar una notación más estándar, como como f(args). Ahí es donde interviene el compilador Scala y cada vez que tenemos una referencia fa un objeto de función y escribimos f (args)para aplicar argumentos a la función representada, el compilador se expande silenciosamente f (args)a la llamada al método del objeto f.apply (args).

Cada función en Scala puede tratarse como un objeto y también funciona a la inversa: cada objeto puede tratarse como una función, siempre que tenga el applymétodo. Tales objetos se pueden usar en la notación de función:

// we will be able to use this object as a function, as well as an object
object Foo {
  var y = 5
  def apply (x: Int) = x + y
}


Foo (1) // using Foo object in function notation 

Hay muchos casos de uso en los que nos gustaría tratar un objeto como una función. El escenario más común es un patrón de fábrica . En lugar de agregar desorden al código usando un método de fábrica, podemos applyobjetar un conjunto de argumentos para crear una nueva instancia de una clase asociada:

List(1,2,3) // same as List.apply(1,2,3) but less clutter, functional notation

// the way the factory method invocation would have looked
// in other languages with OO notation - needless clutter
List.instanceOf(1,2,3) 

Entonces, el applymétodo es solo una forma práctica de cerrar la brecha entre funciones y objetos en Scala.

Vlad Gudim
fuente
1
¿Cómo agregaría un tipo de variable personalizado a un objeto? AFAIK no es posible. Aquí hay un ejemplo: tienes esta clase class Average[YType](yZeroValue:YType). ¿Cómo se pasa YType desde su objeto ya que los objetos no pueden tomar parámetros de tipo?
Adrian
Los tipos en Scala generalmente se pueden inferir en función de los argumentos, pero si no, puede proporcionarlos a través de corchetes. así que cuando crea una instancia de un objeto Promedio, podría decir "val avg = new Promedio [Int] (0)"
Angelo Genovese
44
Las clases de casos merecen una mención aquí. Las clases de casos agregan de manera conveniente, automática e invisible applyy unapplymétodos a un objeto complementario de la clase (entre otras cosas). Así la definición de una clase con sólo una línea como ésta case class Car(make:String, model: String, year: Short, color: String)hace posible la producción de nuevos objetos de la clase de coche por solo: val myCar = Car("VW","Golf",1999,"Blue"). Esta es la abreviatura deCar.apply(...)
Kjetil S.
1
every object can be treated as a function, provided it has the apply method- Tanto ahora tiene sentido.
Arj
ver también slideshare.net/pjschwarz/…
Philip Schwarz
51

Viene de la idea de que a menudo quieres aplicar algo a un objeto. El ejemplo más preciso es el de las fábricas. Cuando tiene una fábrica, desea aplicarle parámetros para crear un objeto.

Los chicos de Scala pensaron que, como ocurre en muchas situaciones, podría ser bueno tener un atajo para llamar apply. Por lo tanto, cuando le da parámetros directamente a un objeto, se elimina como si pasara estos parámetros a la función de aplicación de ese objeto:

class MyAdder(x: Int) {
  def apply(y: Int) = x + y
}

val adder = new MyAdder(2)
val result = adder(4) // equivalent to x.apply(4)

A menudo se usa en objetos complementarios, para proporcionar un buen método de fábrica para una clase o un rasgo, aquí hay un ejemplo:

trait A {
  val x: Int
  def myComplexStrategy: Int
}

object A {
  def apply(x: Int): A = new MyA(x)

  private class MyA(val x: Int) extends A {
    val myComplexStrategy = 42
  }
}

Desde la biblioteca estándar de scala, puede ver cómo scala.collection.Seqse implementa: Seqes un rasgo, por new Seq(1, 2)lo tanto , no se compilará, pero gracias al objeto complementario y la aplicación, puede llamar Seq(1, 2)y la implementación es elegida por el objeto complementario.

Nicolas
fuente
¿Podría lograrse el equivalente de aplicar usando Implicits también?
jayunit100
11

Aquí hay un pequeño ejemplo para aquellos que quieren leer rápidamente

 object ApplyExample01 extends App {


  class Greeter1(var message: String) {
    println("A greeter-1 is being instantiated with message " + message)


  }

  class Greeter2 {


    def apply(message: String) = {
      println("A greeter-2 is being instantiated with message " + message)
    }
  }

  val g1: Greeter1 = new Greeter1("hello")
  val g2: Greeter2 = new Greeter2()

  g2("world")


} 

salida

Se está instanciando un greeter-1 con un mensaje de saludo

Un greeter-2 está siendo instanciado con un mundo de mensajes

sdinesh94
fuente
2
¿Es correcto el mensaje para g2? ¿Está sucediendo eso realmente en la instanciación, o es de hecho después de la instanciación (incluso si es una instancia perezosa)?
Tim Barrass