Quelle est la fonction d'application dans Scala?

311

Je ne l'ai jamais compris à partir des noms artificiels de démêlage et de verbe (une AddTwoclasse en a un applyqui en ajoute deux!).

Je comprends que c'est du sucre syntaxique, donc (je l'ai déduit du contexte) il doit avoir été conçu pour rendre le code plus intuitif.

Quel sens donne une classe avec une applyfonction? À quoi sert-il et à quelles fins améliore-t-il le code (démarshalling, verbes, etc.)?

comment cela aide-t-il lorsqu'il est utilisé dans un objet compagnon?

Jesvin Jose
la source
4
Même une recherche rapide sur Google apporte de nombreux articles intéressants. En voici un: jackcoughonsoftware.blogspot.com/2009/01/…
om-nom-nom
6
en fait, c'est la même chose qu'un constructeur en Java / C ++ mais peut renvoyer la valeur et a le nom 'apply'
ses
C'est une méthode, pas une fonction. La distinction entre méthodes et fonctions est très importante dans Scala.
droite
1
Pour développer Zoidberg, "les méthodes ne sont que des fonctions qui peuvent accéder à l'état de la classe" twitter.github.io/scala_school/basics.html En général, cela s'applique à plus que Scala stackoverflow.com/questions/155609/…
DharmaTurtle

Réponses:

641

Les mathématiciens ont leurs propres petites manières amusantes, donc au lieu de dire "alors nous appelons la fonction en la fpassant xcomme paramètre" comme diraient les programmeurs, ils parlent "d'appliquer la fonction fà son argument x".

En mathématiques et en informatique, Apply est une fonction qui applique des fonctions aux arguments.
Wikipédia

applysert à combler le fossé entre les paradigmes orientés objet et fonctionnels dans Scala. Chaque fonction dans Scala peut être représentée comme un objet. Chaque fonction a également un type OO: par exemple, une fonction qui prend un Intparamètre et renvoie un Intaura le type OO de 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

Puisque tout est un objet dans Scala, vous fpouvez maintenant le traiter comme une référence à un Function1[Int,Int]objet. Par exemple, nous pouvons appeler une toStringméthode héritée de Any, cela aurait été impossible pour une fonction pure, car les fonctions n'ont pas de méthodes:

  f.toString

Ou nous pourrions définir un autre Function1[Int,Int]objet en appelant la composeméthode fet en enchaînant deux fonctions différentes ensemble:

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

Maintenant, si nous voulons réellement exécuter la fonction, ou comme le dit le mathématicien "appliquer une fonction à ses arguments", nous appellerions la applyméthode sur l' Function1[Int,Int]objet:

 f2.apply(2)

Écrire f.apply(args)chaque fois que vous voulez exécuter une fonction représentée comme un objet est la méthode orientée objet, mais ajouterait beaucoup d'encombrement au code sans ajouter beaucoup d'informations supplémentaires et il serait bien de pouvoir utiliser une notation plus standard, telle comme f(args). C'est là que le compilateur Scala intervient et chaque fois que nous avons une référence fà un objet fonction et écrivons f (args)pour appliquer des arguments à la fonction représentée, le compilateur se développe silencieusement f (args)à l'appel de méthode objet f.apply (args).

Chaque fonction dans Scala peut être traitée comme un objet et cela fonctionne également dans l'autre sens - chaque objet peut être traité comme une fonction, à condition d'avoir la applyméthode. De tels objets peuvent être utilisés dans la notation de fonction:

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

Il existe de nombreux cas d'utilisation où nous voudrions traiter un objet comme une fonction. Le scénario le plus courant est un modèle d'usine . Au lieu d'ajouter de l'encombrement au code à l'aide d'une méthode d'usine, nous pouvons nous applyopposer à un ensemble d'arguments pour créer une nouvelle instance d'une classe associée:

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) 

La applyméthode n'est donc qu'un moyen pratique de combler l'écart entre les fonctions et les objets dans Scala.

Vlad Gudim
la source
1
comment ajouteriez-vous un type de variable personnalisé à un objet. AFAIK ce n'est pas possible. Voici un exemple: vous avez cette classe class Average[YType](yZeroValue:YType). Comment passez-vous YType à partir de son objet puisque les objets ne peuvent pas accepter les paramètres de type?
Adrian
Les types dans Scala peuvent généralement être déduits en fonction des arguments, mais sinon, vous pouvez les fournir via les crochets. donc lorsque vous instanciez un objet Average, vous pouvez dire "val avg = new Average [Int] (0)"
Angelo Genovese
4
Les classes de cas méritent une mention ici. Les classes de cas ajoutent de manière pratique, automatique et invisible applyet des unapplyméthodes à un objet compagnon de la classe (entre autres). Ainsi , la définition d' une classe avec une seule ligne comme celui - ci case class Car(make:String, model: String, year: Short, color: String)permet de produire de nouveaux objets de la classe Car par seulement: val myCar = Car("VW","Golf",1999,"Blue"). C'est un raccourci pourCar.apply(...)
Kjetil S.
1
every object can be treated as a function, provided it has the apply method- tellement de sens maintenant.
Arj
voir aussi slideshare.net/pjschwarz/…
Philip Schwarz
51

Cela vient de l'idée que vous voulez souvent appliquer quelque chose à un objet. L'exemple le plus précis est celui des usines. Lorsque vous avez une usine, vous souhaitez lui appliquer un paramètre pour créer un objet.

Les gars de Scala pensaient que, comme cela se produit dans de nombreuses situations, il pourrait être agréable d'avoir un raccourci pour appeler apply. Ainsi, lorsque vous donnez des paramètres directement à un objet, c'est comme si vous passiez ces paramètres à la fonction d'application de cet objet:

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

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

Il est souvent utilisé dans un objet compagnon, pour fournir une belle méthode d'usine pour une classe ou un trait, voici un exemple:

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

Dans la bibliothèque standard de scala, vous pouvez voir comment scala.collection.Seqest implémenté: Seqest un trait, donc new Seq(1, 2)ne compilera pas mais grâce à l'objet compagnon et appliquez, vous pouvez appeler Seq(1, 2)et l'implémentation est choisie par l'objet compagnon.

Nicolas
la source
Est-ce que l'équivalent de l'application peut également être obtenu en utilisant Implicits?
jayunit100
11

Voici un petit exemple pour ceux qui veulent parcourir rapidement

 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")


} 

production

Un greeter-1 est instancié avec un message bonjour

Un greeter-2 est instancié avec le monde des messages

sdinesh94
la source
2
Le message pour g2 est-il correct? Est-ce que cela se produit réellement à l'instanciation, ou est-ce en fait après l'instanciation (même si c'est instancié paresseusement)?
Tim Barrass