Gutes Beispiel für implizite Parameter in Scala? [geschlossen]

75

Bisher sehen implizite Parameter in Scala für mich nicht gut aus - es ist zu nahe an globalen Variablen, aber da Scala wie eine ziemlich strenge Sprache erscheint, bezweifle ich meiner Meinung nach :-).

Frage: Können Sie ein reales (oder nahes) gutes Beispiel zeigen, wenn implizite Parameter wirklich funktionieren? IOW: etwas Ernsthafteres als das showPrompt, das ein solches Sprachdesign rechtfertigen würde.

Oder im Gegenteil - könnten Sie ein zuverlässiges Sprachdesign zeigen (kann imaginär sein), das implizit nicht notwendig macht? Ich denke, dass selbst kein Mechanismus besser ist als implizite, weil der Code klarer ist und es keine Vermutungen gibt.

Bitte beachten Sie, ich frage nach Parametern, nicht nach impliziten Funktionen (Konvertierungen)!

Aktualisierung

Globale Variablen

Vielen Dank für alle tollen Antworten. Vielleicht kläre ich meinen Einwand "globale Variablen". Betrachten Sie eine solche Funktion:

max(x : Int,y : Int) : Int

du nennst es

max(5,6);

Sie könnten es (!) so machen:

max(x:5,y:6);

aber in meinen Augen implicitsfunktioniert das so:

x = 5;
y = 6;
max()

es unterscheidet sich nicht sehr von einem solchen Konstrukt (PHP-ähnlich)

max() : Int
{
  global x : Int;
  global y : Int;
  ...
}

Dereks Antwort

Dies ist ein großartiges Beispiel. Wenn Sie sich jedoch vorstellen können, dass das Senden von Nachrichten, die nicht verwendet werden, flexibel ist, veröffentlichen implicitSie bitte ein Gegenbeispiel. Ich bin sehr gespannt auf Reinheit im Sprachdesign ;-).

Greenoldman
quelle
Wenn Sie ein globales Implizit erstellen (und dies nicht können - das Beste, was Sie tun können, ist ein implizites Paket), gilt Ihre Aussage möglicherweise, aber nur, wenn Sie sich für so etwas entschieden haben ... nicht. Unter dem Strich beruht die Flexibilität dieser API auf der Verwendung von Implikits. Wenn Sie sie nicht verwenden, können Sie nicht die gleiche Flexibilität erhalten. Sie möchten also die Funktion entfernen, die sie großartig macht, und sie trotzdem großartig machen. Sehr merkwürdige Anfrage.
Derek Wyatt
@Derek Wyatt, der letzte Kommentar ist etwas seltsam - suchst du nicht nach Optimierung im Leben? Ich mache. Nun zu globalen Variablen - ich sage nicht, dass Sie globale Variablen haben müssen, um Implizite zu verwenden, ich sage, dass sie in der Verwendung ähnlich sind. Weil sie implizit durch den Namen des Angerufenen gebunden sind und aus dem Bereich des Anrufers herausgenommen werden, nicht aus dem tatsächlichen Anruf.
Greenoldman

Antworten:

99

In gewissem Sinne repräsentieren Implizite den globalen Zustand. Sie sind jedoch nicht veränderlich, was das wahre Problem bei globalen Variablen ist - Sie sehen keine Leute, die sich über globale Konstanten beschweren, oder? Tatsächlich schreiben Codierungsstandards normalerweise vor, dass Sie alle Konstanten in Ihrem Code in Konstanten oder Aufzählungen umwandeln, die normalerweise global sind.

Beachten Sie auch, dass sich Implizite nicht in einem flachen Namespace befinden, was auch bei Globals ein häufiges Problem ist. Sie sind explizit an Typen und damit an die Pakethierarchie dieser Typen gebunden.

Nehmen Sie also Ihre Globals, machen Sie sie unveränderlich und initialisieren Sie sie auf der Deklarationsseite und setzen Sie sie in Namespaces. Sehen sie immer noch wie Globale aus? Sehen sie immer noch problematisch aus?

Aber lasst uns hier nicht aufhören. Implizite sind an Typen gebunden und genauso "global" wie Typen. Stört Sie die Tatsache, dass Typen global sind?

Es gibt viele Anwendungsfälle, aber wir können anhand ihrer Vorgeschichte einen kurzen Überblick geben. Ursprünglich hatte Scala keine Implikationen. Was Scala hatte, waren Ansichtstypen, eine Funktion, die viele andere Sprachen hatten. Wir können das heute noch sehen, wenn Sie so etwas schreiben T <% Ordered[T], was bedeutet, dass der Typ Tals Typ angesehen werden kann Ordered[T]. Ansichtstypen sind eine Möglichkeit, automatische Umwandlungen für Typparameter (Generika) verfügbar zu machen.

Scala verallgemeinerte dieses Merkmal dann mit Impliziten. Automatische Umwandlungen sind nicht mehr vorhanden. Stattdessen haben Sie implizite Konvertierungen - dies sind nur Function1Werte und können daher als Parameter übergeben werden. Von da an T <% Ordered[T]bedeutete dies , dass ein Wert für eine implizite Konvertierung als Parameter übergeben wurde. Da die Umwandlung automatisch erfolgt, muss der Aufrufer der Funktion den Parameter nicht explizit übergeben. Daher wurden diese Parameter zu impliziten Parametern .

Beachten Sie, dass es zwei Konzepte gibt - implizite Konvertierungen und implizite Parameter -, die sehr nahe beieinander liegen, sich jedoch nicht vollständig überschneiden.

Auf jeden Fall wurden Ansichtstypen zu syntaktischem Zucker für implizite Konvertierungen, die implizit übergeben wurden. Sie würden folgendermaßen umgeschrieben:

def max[T <% Ordered[T]](a: T, b: T): T = if (a < b) b else a
def max[T](a: T, b: T)(implicit $ev1: Function1[T, Ordered[T]]): T = if ($ev1(a) < b) b else a

Die impliziten Parameter sind lediglich eine Verallgemeinerung dieses Musters, die es ermöglicht, jede Art von impliziten Parametern anstelle von nur zu übergeben Function1. Die tatsächliche Verwendung für sie folgte dann, und syntaktischer Zucker für diese Verwendungen kam letztere.

Eine davon sind Kontextgrenzen , die zum Implementieren des Typklassenmusters verwendet werden (Muster, da es sich nicht um eine integrierte Funktion handelt, sondern nur um die Verwendung der Sprache, die ähnliche Funktionen wie die Typklasse von Haskell bietet). Eine Kontextbindung wird verwendet, um einen Adapter bereitzustellen, der Funktionen implementiert, die einer Klasse inhärent sind, aber nicht von dieser deklariert werden. Es bietet die Vorteile von Vererbung und Schnittstellen ohne deren Nachteile. Zum Beispiel:

def max[T](a: T, b: T)(implicit $ev1: Ordering[T]): T = if ($ev1.lt(a, b)) b else a
// latter followed by the syntactic sugar
def max[T: Ordering](a: T, b: T): T = if (implicitly[Ordering[T]].lt(a, b)) b else a

Sie haben das wahrscheinlich schon verwendet - es gibt einen häufigen Anwendungsfall, den die Leute normalerweise nicht bemerken. Es ist das:

new Array[Int](size)

Dabei wird ein an Klassenmanifeste gebundener Kontext verwendet, um eine solche Array-Initialisierung zu ermöglichen. Wir können das an diesem Beispiel sehen:

def f[T](size: Int) = new Array[T](size) // won't compile!

Sie können es so schreiben:

def f[T: ClassManifest](size: Int) = new Array[T](size)

In der Standardbibliothek werden am häufigsten folgende Kontextgrenzen verwendet:

Manifest      // Provides reflection on a type
ClassManifest // Provides reflection on a type after erasure
Ordering      // Total ordering of elements
Numeric       // Basic arithmetic of elements
CanBuildFrom  // Collection creation

Die letzteren drei sind meist mit einer Sammlung, mit Methoden wie max, sumund map. Eine Bibliothek, die Kontextgrenzen in großem Umfang nutzt, ist Scalaz.

Eine andere übliche Verwendung besteht darin, die Kesselplatte bei Vorgängen zu verringern, die einen gemeinsamen Parameter haben müssen. Zum Beispiel Transaktionen:

def withTransaction(f: Transaction => Unit) = {
  val txn = new Transaction

  try { f(txn); txn.commit() }
  catch { case ex => txn.rollback(); throw ex }
}

withTransaction { txn =>
  op1(data)(txn)
  op2(data)(txn)
  op3(data)(txn)
}

Was dann so vereinfacht wird:

withTransaction { implicit txn =>
  op1(data)
  op2(data)
  op3(data)
}

Dieses Muster wird mit dem Transaktionsspeicher verwendet, und ich denke (bin mir aber nicht sicher), dass die Scala-E / A-Bibliothek es auch verwendet.

Die dritte häufig verwendete Verwendung besteht darin, Beweise für die übergebenen Typen zu erstellen, die es ermöglichen, zur Kompilierungszeit Dinge zu erkennen, die andernfalls zu Laufzeitausnahmen führen würden. Siehe diese Definition beispielsweise unter Option:

def flatten[B](implicit ev: A <:< Option[B]): Option[B]

Das macht es möglich:

scala> Option(Option(2)).flatten // compiles
res0: Option[Int] = Some(2)

scala> Option(2).flatten // does not compile!
<console>:8: error: Cannot prove that Int <:< Option[B].
              Option(2).flatten // does not compile!
                        ^

Eine Bibliothek, die diese Funktion in großem Umfang nutzt, ist Shapeless.

Ich denke nicht, dass das Beispiel der Akka-Bibliothek in eine dieser vier Kategorien passt, aber das ist der springende Punkt bei allgemeinen Funktionen: Die Leute können es auf alle möglichen Arten verwenden, anstatt auf die vom Sprachdesigner vorgeschriebenen Arten.

Wenn Sie gerne verschrieben werden (wie zum Beispiel Python), dann ist Scala einfach nichts für Sie.

Daniel C. Sobral
quelle
7
Das Buch, das Sie schreiben, sollte definitiv auf Englisch sein! :-) Danke für den tollen Beitrag.
Greenoldman
2
Warum gibt SO keine Sternoption für eine solche Antwort? Wirklich toller Beitrag!
Chen OT
23

Sicher. Akka hat ein großartiges Beispiel dafür in Bezug auf seine Schauspieler. Wenn Sie sich in der receiveMethode eines Schauspielers befinden , möchten Sie möglicherweise eine Nachricht an einen anderen Schauspieler senden. Wenn Sie dies tun, bündelt Akka (standardmäßig) den aktuellen Akteur wie folgt senderaus der Nachricht:

trait ScalaActorRef { this: ActorRef =>
  ...

  def !(message: Any)(implicit sender: ActorRef = null): Unit

  ...
}

Das senderist implizit. Im Schauspieler gibt es eine Definition, die wie folgt aussieht:

trait Actor {
  ...

  implicit val self = context.self

  ...
}

Dies schafft den impliziten Wert im Rahmen Ihres eigenen Codes und ermöglicht es Ihnen, einfache Dinge wie diese zu tun:

someOtherActor ! SomeMessage

Jetzt können Sie dies auch tun, wenn Sie möchten:

someOtherActor.!(SomeMessage)(self)

oder

someOtherActor.!(SomeMessage)(null)

oder

someOtherActor.!(SomeMessage)(anotherActorAltogether)

Aber normalerweise nicht. Sie behalten nur die natürliche Verwendung bei, die durch die implizite Wertedefinition im Actor-Merkmal ermöglicht wird. Es gibt ungefähr eine Million andere Beispiele. Die Sammlungsklassen sind riesig. Wenn Sie in einer nicht trivialen Scala-Bibliothek herumwandern, finden Sie eine Lastwagenladung.

Derek Wyatt
quelle
Ich denke, dies ist ein noch besseres Beispiel als Traversable.maxTypklassen und dergleichen.
Debilski
Dies ist ein gutes Beispiel. Bis zu einem gewissen Grad denke ich, dass implizite Variablen eine Möglichkeit sind, globale Variablen und "Gott-Singletons" zu vermeiden (mangels eines besseren Wortes), aber Ihren Code dennoch besser lesbar zu halten, da Sie einige grundlegende Installationen (die oben genannten) nicht explizit übergeben müssen Singletons). Und andererseits können Sie sie beispielsweise beim Testen immer noch explizit übergeben. Daher denke ich, dass sie in vielen Fällen eine lockerere Kopplung und einen saubereren Code ermöglichen.
Vertti
@vertti, nicht genau. Ich denke, die Art und Weise, wie C ++ funktioniert, ist hier besser - dh Parameter pro ganze Klasse und / oder Standardargumente. Für mich ist die Vorstellung, dass die Funktion ein Argument aufnimmt, das von irgendwoher stammt, sehr seltsam.
Greenoldman
1
@Derek Wyatt, du nimmst das zu persönlich. "funktioniert hier besser" - Ich hoffe, ich habe mir klar gemacht, was meine Metriken sind, sie sind nicht die gleichen wie deine, also ist mein "besser" nicht das gleiche wie dein "besser". Sie sind froh implicits, dass Sie es getan haben , ich nicht - Ihr Beispiel ist großartig als Rätsel (und ich bin dafür dankbar), wie Sie das Problem aus einer anderen Perspektive angehen können. Das ist mein POV, also ersparen Sie mir bitte die Ratschläge "Lernen Sie die Sprache, in der Sie programmieren" (wahr, aber nicht sehr höflich - Bevormundung ist keine Diskussion willkommen).
Greenoldman
1
@macias Oh Mist! Es tut mir wirklich leid, aber mein Kommentar dort oben hätte lauten sollen ... Ich bevormunden Sie NICHT ... Ich bin es wirklich nicht. Ugh ... Entschuldigung.
Derek Wyatt
9

Ein Beispiel wären die Vergleichsoperationen an Traversable[A]. ZB maxoder sort:

def max[B >: A](implicit cmp: Ordering[B]) : A

Diese können nur dann sinnvoll definiert werden , wenn eine Operation <auf A. Ohne Implikationen müssten wir also Ordering[B]jedes Mal den Kontext angeben, wenn wir diese Funktion verwenden möchten. (Oder geben Sie die statische Typprüfung im Inneren auf maxund riskieren Sie einen Laufzeitfehler.)

Wenn jedoch eine implizite Vergleich Typklasse ist in Umfang, zB einige Ordering[Int], können wir es einfach sofort verwenden oder einfach die Vergleichsmethode ändern , indem Sie einen anderen Wert für den impliziten Parameter liefert.

Natürlich können Implizite abgeschattet werden, und daher kann es Situationen geben, in denen das tatsächliche Implizit, das in den Geltungsbereich fällt, nicht klar genug ist. Für einfache Anwendungen von maxoder sortes könnte in der Tat ausreichend sein , eine feste Ordnung zu haben , traitauf Intund einige Syntax verwenden , um zu überprüfen , ob dieses Merkmal zur Verfügung. Dies würde jedoch bedeuten, dass es keine zusätzlichen Merkmale geben könnte und jeder Code die ursprünglich definierten Merkmale verwenden müsste.

Ergänzung:
Antwort auf den globalen Variablenvergleich .

Ich denke, Sie haben Recht, dass in einem Code wie abgeschnitten

implicit val num = 2
implicit val item = "Orange"
def shopping(implicit num: Int, item: String) = {
  "I’m buying "+num+" "+item+(if(num==1) "." else "s.")
}

scala> shopping
res: java.lang.String = I’m buying 2 Oranges.

es kann nach faulen und bösen globalen Variablen riechen. Der entscheidende Punkt ist jedoch, dass es möglicherweise nur eine implizite Variable pro Typ im Bereich gibt. Ihr Beispiel mit zwei Ints wird nicht funktionieren.

Dies bedeutet auch, dass implizite Variablen praktisch nur verwendet werden, wenn es für einen Typ eine nicht unbedingt eindeutige, aber eindeutige Primärinstanz gibt. Die selfReferenz eines Schauspielers ist ein gutes Beispiel für so etwas. Das Typklassenbeispiel ist ein weiteres Beispiel. Es kann Dutzende algebraischer Vergleiche für jeden Typ geben, aber es gibt einen besonderen. (Auf einer anderen Ebene kann die tatsächliche Zeilennummer im Code selbst auch eine gute implizite Variable darstellen, solange sie einen sehr unterschiedlichen Typ verwendet.)

Normalerweise verwenden Sie implicits nicht für alltägliche Typen. Und bei speziellen Typen (wie Ordering[Int]) besteht kein allzu großes Risiko, sie zu beschatten.

Debilski
quelle
Vielen Dank, dies ist jedoch ein Gegenbeispiel - dies sollte ein "Merkmal" der Sammlungsinstanz sein. Und dann könnten Sie max () verwenden, um die Reihenfolge der Sammlung zu bestimmen, oder max (Vergleicher), um eine benutzerdefinierte zu verwenden.
Greenoldman
2
Sicher, das wäre möglich. Es würde aber auch bedeuten, dass man Intbei Bedarf kein anderes Merkmal hinzufügen könnte, z. B. oder einen anderen vordefinierten Typ. (Ein oft zitiertes Beispiel ist das einer Halbgruppe, die möglicherweise weder auf Int noch auf String ein Originalmerkmal ist - und es wäre auch nicht möglich, dieses Merkmal in fester Form hinzuzufügen.) Das Problem ist: Es gibt keinen Weg einen Typ über alle möglichen Merkmale zu verallgemeinern. Dies ist immer Code (Typanmerkungen), der ad-hoc angegeben werden muss, sonst verlieren Sie die Typensicherheit. Implizite Variablen reduzieren hierfür lediglich den Boilerplate-Code.
Debilski
Keine Int'sSammlung von Int's, wie Liste oder Array gegeben. Wenn Sie davon ausgehen, dass die Elemente vergleichbar sind und wie implicitoben beschrieben schreiben, können Sie auch die Reihenfolge oben in der Klasse definieren (wie in C ++). In C ++ wird der Namespace hier nicht mit beliebigen Namen wie "cmp" verschmutzt, da Sie den Wert übergeben.
Greenoldman
Vielen Dank für diesen Zusatz, ich kann Sie nicht mehr bewerten, sorry :-)
Greenoldman
Wenn Sie die Haskell-Terminologie verwenden, können Sie sie auch korrekt verwenden. Ein Wert vom Typ Ordering[Int]ist eine Typklasseninstanz , keine Typklasse .
Rotsor
6

Nach meiner Erfahrung gibt es kein wirklich gutes Beispiel für die Verwendung von impliziten Parametern oder impliziten Konvertierungen.

Der kleine Vorteil der Verwendung von Implikits (ohne dass ein Parameter oder ein Typ explizit geschrieben werden muss) ist im Vergleich zu den von ihnen verursachten Problemen überflüssig.

Ich bin seit 15 Jahren Entwickler und arbeite seit 1,5 Jahren mit Scala.

Ich habe oft Fehler gesehen, die vom Entwickler verursacht wurden, ohne zu wissen, dass Implizite verwendet werden und dass eine bestimmte Funktion tatsächlich einen anderen Typ zurückgibt als den angegebenen. Aufgrund impliziter Konvertierung.

Ich habe auch Aussagen gehört, die besagen, dass Sie Implikits nicht verwenden sollten, wenn Sie sie nicht mögen. Dies ist in der realen Welt nicht praktikabel, da häufig externe Bibliotheken verwendet werden und viele von ihnen implizite Elemente verwenden. Daher verwendet Ihr Code implizite Elemente, und Sie sind sich dessen möglicherweise nicht bewusst. Sie können einen Code schreiben, der entweder Folgendes enthält:

import org.some.common.library.{TypeA, TypeB}

oder:

import org.some.common.library._

Beide Codes werden kompiliert und ausgeführt. Sie führen jedoch nicht immer zu denselben Ergebnissen, da der Import der zweiten Version eine Konvertierung impliziert, die dazu führt, dass sich der Code anders verhält.

Der dadurch verursachte 'Fehler' kann sehr lange nach dem Schreiben des Codes auftreten, falls einige Werte, die von dieser Konvertierung betroffen sind, ursprünglich nicht verwendet wurden.

Sobald Sie auf den Fehler stoßen, ist es keine leichte Aufgabe, die Ursache zu finden. Sie müssen einige gründliche Untersuchungen durchführen.

Obwohl Sie sich wie ein Experte für Scala fühlen, nachdem Sie den Fehler gefunden und durch Ändern einer Importanweisung behoben haben, haben Sie tatsächlich viel wertvolle Zeit verschwendet.

Weitere Gründe, warum ich generell gegen Implikits bin, sind:

  • Sie machen den Code schwer verständlich (es gibt weniger Code, aber Sie wissen nicht, was er tut)
  • Kompilierungszeit. Scala-Code wird viel langsamer kompiliert, wenn implizite Elemente verwendet werden.
  • In der Praxis wird die Sprache von statisch typisiert in dynamisch typisiert geändert. Es ist wahr, dass Sie solche Situationen vermeiden können, wenn Sie einmal sehr strengen Codierungsrichtlinien folgen, aber in der realen Welt ist dies nicht immer der Fall. Selbst wenn Sie die IDE "Nicht verwendete Importe entfernen" verwenden, kann Ihr Code weiterhin kompiliert und ausgeführt werden, jedoch nicht wie zuvor, als Sie "nicht verwendete" Importe entfernt haben.

Es gibt keine Option zum Kompilieren von Scala ohne Implikationen (falls vorhanden, bitte korrigieren Sie mich), und wenn es eine Option gäbe, hätte keine der gängigen Community-Scala-Bibliotheken kompiliert.

Aus all den oben genannten Gründen denke ich, dass Implizite eine der schlimmsten Praktiken sind, die die Scala-Sprache anwendet.

Scala hat viele großartige Funktionen und viele nicht so großartige.

Bei der Auswahl einer Sprache für ein neues Projekt sind Implikationen einer der Gründe gegen Scala, nicht dafür. Meiner Meinung nach.

noam
quelle
Es ist erwähnenswert, dass Kotlin Implizite beseitigt hat
Akavall
4

Eine andere gute allgemeine Verwendung impliziter Parameter besteht darin, den Rückgabetyp einer Methode vom Typ einiger an sie übergebener Parameter abhängig zu machen. Ein gutes Beispiel, das von Jens erwähnt wird, ist das Sammlungsframework und Methoden wie map, deren vollständige Signatur normalerweise lautet:

def map[B, That](f: (A) ⇒ B)(implicit bf: CanBuildFrom[GenSeq[A], B, That]): That

Beachten Sie, dass der Rückgabetyp Thatdurch die beste Anpassung bestimmt wird CanBuildFrom, die der Compiler finden kann.

Ein weiteres Beispiel hierfür finden Sie in dieser Antwort . Dort wird der Rückgabetyp der Methode Arithmetic.applyanhand eines bestimmten impliziten Parametertyps ( BiConverter) bestimmt.

Jean-Philippe Pellet
quelle
Vielleicht vermisse ich etwas. Sie können hier nicht für Typ That raten, also müssen Sie es angeben, richtig? Wäre es nicht dasselbe, wenn Sie den Typ That weglassen und das Ergebnis einfach von Hand konvertieren: map (it => it.foo) .toBar () anstelle von map [B, List [Bars]] (it => it.foo)?
Greenoldman
@macias: Letzteres erstellt keine Zwischensammlung. Wenn Sie toBar explizit aufrufen, muss zuerst ein Foo erstellt werden, das dann in einen Balken konvertiert wird. Wenn es einen Typparameter gibt, kann eine Leiste direkt erstellt werden.
Kiritsuku
3
@macias: Wenn Sie es von Hand konvertieren, machen Sie es in einem zweiten Schritt danach. Möglicherweise erhalten Sie eine ListGegenleistung und müssen diese dann erneut durchlaufen, um eine zu erhalten Set. Durch die Verwendung der impliziten 'Annotation' kann die mapMethode vermeiden, die falsche Sammlung zu initialisieren und zu füllen.
Debilski
1
@macias: Sie müssen die Typparameter nicht für die Kartenmethode buchstabieren - sie können abgeleitet werden. val lf: List [Foo] =…; val sb: Setze [Bar] = lf map (_.toBar) // keine Zwischenliste [Bar]
romusz
4

Es ist einfach, denken Sie daran:

  • um die zu übergebende Variable auch als implizit zu deklarieren
  • alle impliziten Parameter nach den nicht impliziten Parametern in einem separaten () zu deklarieren

z.B

def myFunction(): Int = {
  implicit val y: Int = 33
  implicit val z: Double = 3.3

  functionWithImplicit("foo") // calls functionWithImplicit("foo")(y, z)
}

def functionWithImplicit(foo: String)(implicit x: Int, d: Double) = // blar blar
samthebest
quelle
3

Implizite Parameter werden in der Erfassungs-API häufig verwendet. Viele Funktionen erhalten ein implizites CanBuildFrom, das sicherstellt, dass Sie die beste Implementierung der Ergebnissammlung erhalten.

Ohne Implikationen würden Sie entweder die ganze Zeit so etwas passieren, was die normale Nutzung umständlich machen würde. Oder verwenden Sie weniger spezialisierte Sammlungen, was ärgerlich wäre, da Sie dadurch an Leistung / Leistung verlieren würden.

Jens Schauder
quelle
0

Ich kommentiere diesen Beitrag etwas spät, aber ich habe in letzter Zeit angefangen, Scala zu lernen. Daniel und andere haben nette Hintergrundinformationen über implizite Schlüsselwörter gegeben. Ich würde mir aus praktischer Sicht zwei Cent für implizite Variablen zur Verfügung stellen.

Scala eignet sich am besten zum Schreiben von Apache Spark-Codes. In Spark haben wir einen Spark-Kontext und höchstwahrscheinlich die Konfigurationsklasse, die möglicherweise die Konfigurationsschlüssel / -werte aus einer Konfigurationsdatei abruft.

Nun, wenn ich eine abstrakte Klasse habe und ein Objekt der Konfiguration deklariere und den Kontext wie folgt funke: -

abstract class myImplicitClass {

implicit val config = new myConfigClass()

val conf = new SparkConf().setMaster().setAppName()
implicit val sc = new SparkContext(conf)

def overrideThisMethod(implicit sc: SparkContext, config: Config) : Unit
}

class MyClass extends myImplicitClass {

override def overrideThisMethod(implicit sc: SparkContext, config: Config){

/*I can provide here n number of methods where I can pass the sc and config 
objects, what are implicit*/
def firstFn(firstParam: Int) (implicit sc: SparkContext, config: Config){ 
    /*I can use "sc" and "config" as I wish: making rdd or getting data from cassandra, for e.g.*/
    val myRdd = sc.parallelize(List("abc","123"))
}
def secondFn(firstParam: Int) (implicit sc: SparkContext, config: Config){
 /*following are the ways we can use "sc" and "config" */

        val keyspace = config.getString("keyspace")
        val tableName = config.getString("table")
        val hostName = config.getString("host")
        val userName = config.getString("username")
        val pswd = config.getString("password")

    implicit val cassandraConnectorObj = CassandraConnector(....)
    val cassandraRdd = sc.cassandraTable(keyspace, tableName)
}

}
}

Wie wir den obigen Code sehen können, habe ich zwei implizite Objekte in meiner abstrakten Klasse und ich habe diese beiden impliziten Variablen als implizite Parameter für Funktion / Methode / Definition übergeben. Ich denke, dies ist der beste Anwendungsfall, den wir in Bezug auf die Verwendung impliziter Variablen darstellen können.

anshuman sharma
quelle