wake-up-neo.com

Verstehen implizit in Scala

Ich habe das Scala playframework Tutorial durchgearbeitet und bin auf dieses Codefragment gestoßen, das mich verwirrt hat:

def newTask = Action { implicit request =>
taskForm.bindFromRequest.fold(
        errors => BadRequest(views.html.index(Task.all(), errors)),
        label => {
          Task.create(label)
          Redirect(routes.Application.tasks())
        } 
  )
}

Also habe ich mich entschlossen zu recherchieren und bin auf diesen Beitrag gestoßen.

Ich verstehe es immer noch nicht.

Was ist der Unterschied zwischen diesen:

implicit def double2Int(d : Double) : Int = d.toInt

und

def double2IntNonImplicit(d : Double) : Int = d.toInt

abgesehen von der offensichtlichen Tatsache haben sie unterschiedliche Methodennamen.

Wann sollte ich implicit verwenden und warum?

287
Clive

Im Folgenden werden die wichtigsten Anwendungsfälle von Implikiten erläutert. Weitere Informationen finden Sie im relevantes Kapitel der Programmierung in Scala .

Implizite Parameter

Die letzte Parameterliste einer Methode kann mit implicit markiert werden, dh die Werte werden aus dem Kontext entnommen, in dem sie aufgerufen werden. Wenn der Gültigkeitsbereich keinen impliziten Wert des richtigen Typs enthält, wird er nicht kompiliert. Da der implizite Wert in einen einzelnen Wert aufgelöst werden muss und Konflikte vermieden werden sollen, ist es eine gute Idee, den Typ spezifisch für seinen Zweck zu machen, z. Benötigen Sie Ihre Methoden nicht, um ein implizites Int zu finden!

beispiel:

  // probably in a library
class Prefixer(val prefix: String)
def addPrefix(s: String)(implicit p: Prefixer) = p.prefix + s

  // then probably in your application
implicit val myImplicitPrefixer = new Prefixer("***")
addPrefix("abc")  // returns "***abc"

Implizite Konvertierungen

Wenn der Compiler einen Ausdruck mit dem falschen Typ für den Kontext findet, sucht er nach einem impliziten Wert Function eines Typs, mit dem er die Typprüfung durchführen kann. Wenn also ein A erforderlich ist und ein B gefunden wird, wird nach einem impliziten Wert vom Typ B => A Im Gültigkeitsbereich gesucht (es werden auch einige andere Stellen wie in B und A Begleitobjekte (falls vorhanden). Da def s zu Function - Objekten "eta-expandiert" werden können, kann auch eine implicit def xyz(arg: B): A verwendet werden.

Der Unterschied zwischen Ihren Methoden besteht also darin, dass der Compiler die mit implicit markierte Methode für Sie einfügt, wenn eine Double gefunden wird, aber eine Int erforderlich ist.

implicit def doubleToInt(d: Double) = d.toInt
val x: Int = 42.0

wird genauso funktionieren wie

def doubleToInt(d: Double) = d.toInt
val x: Int = doubleToInt(42.0)

In der zweiten haben wir die Konvertierung manuell eingefügt; im ersten hat der compiler das selbe automatisch gemacht. Die Konvertierung ist aufgrund der Typanmerkung auf der linken Seite erforderlich.


Bezüglich Ihres ersten Snippets aus dem Spiel:

Aktionen werden auf diese Seite in der Play-Dokumentation erklärt (siehe auch API-Dokumente ). Du benutzt

apply(block: (Request[AnyContent]) ⇒ Result): Action[AnyContent]

auf dem Objekt Action (das der Begleiter des gleichnamigen Merkmals ist).

Wir müssen also eine Funktion als Argument angeben, die als Literal in das Formular geschrieben werden kann

request => ...

In einem Funktionsliteral ist der Teil vor dem => Eine Wertdeklaration und kann wie in jeder anderen implicit - Deklaration mit val markiert werden. Hier muss request nicht mit implicit markiert sein, damit check eingegeben werden kann. Dadurch ist es jedoch als verfügbar impliziter Wert für alle Methoden, die ihn innerhalb der Funktion benötigen könnten (und natürlich auch explizit verwendet werden können). In diesem speziellen Fall wurde dies durchgeführt, weil für die Methode bindFromRequest in der Klasse Form ein implizites Argument Request erforderlich ist.

ACHTUNG: enthält Sarkasmus mit Bedacht! YMMV ...

Luigis Antwort ist vollständig und richtig. Dies ist nur, um es ein wenig mit einem Beispiel zu erweitern, wie Sie herrlich überbeanspruchen können implicits, wie es ziemlich oft in Scala) Projekten vorkommt. Eigentlich so oft , Sie können es wahrscheinlich sogar in einer der "Best Practice" Anleitungen finden.

object HelloWorld {
  case class Text(content: String)
  case class Prefix(text: String)

  implicit def String2Text(content: String)(implicit prefix: Prefix) = {
    Text(prefix.text + " " + content)
  }

  def printText(text: Text): Unit = {
    println(text.content)
  }

  def main(args: Array[String]): Unit = {
    printText("World!")
  }

  // Best to hide this line somewhere below a pile of completely unrelated code.
  // Better yet, import its package from another distant place.
  implicit val prefixLOL = Prefix("Hello")
}
34
Daniel Dinnyes

Warum und wann sollten Sie den Parameter request als implicit markieren:

Einige Methoden, die Sie im Hauptteil Ihrer Aktion verwenden, haben eine implizite Parameterliste, wie z. B. Form.scala eine Methode definiert:

def bindFromRequest()(implicit request: play.api.mvc.Request[_]): Form[T] = { ... }

Sie bemerken dies nicht unbedingt, da Sie nur myForm.bindFromRequest() aufrufen würden. Sie müssen die impliziten Argumente nicht explizit angeben. Nein, Sie überlassen es dem Compiler , bei jedem Methodenaufruf, der eine Instanz der Anforderung erfordert, nach einem gültigen Kandidatenobjekt zu suchen, das übergeben werden soll. Da Ihnen eine Anfrage zur Verfügung steht , müssen Sie sie nur als implicit markieren.

Sie markieren es explizit als verfügbar für implizite Verwendung.

Sie weisen den Compiler an, dass es "OK" ist, das vom Play-Framework gesendete Anforderungsobjekt zu verwenden (wir haben den Namen "request" angegeben, hätten aber auch nur "r" oder "req" verwenden können), wo immer dies erforderlich ist, "on the sly". .

myForm.bindFromRequest()

siehst du es? es ist nicht da, aber es ist da!

Es passiert einfach, ohne dass Sie es manuell an jeder Stelle einfügen müssen, die es benötigt (aber Sie können es explizit weitergeben, wenn Sie dies wünschen, egal ob es ist mit implicit markiert oder nicht):

myForm.bindFromRequest()(request)

Ohne es als implizit zu markieren, würden Sie müssen das oben Genannte tun. Wenn Sie es als implizit markieren, müssen Sie es nicht.

Wann sollten Sie die Anfrage als implicit markieren? Dies ist nur dann wirklich erforderlich, wenn Sie Methoden verwenden, die eine implizite Parameterliste deklarieren, die eine Instanz der Anforderung erwartet . Aber um es einfach zu halten, könnten Sie es sich zur Gewohnheit machen, die Anfrage implicitimmer zu markieren. Auf diese Weise können Sie einfach schönen, knappen Code schreiben.

6
Peter Perháč

Im obigen Fall sollte es auch eine implizite Funktion only one Geben, deren Typ double => Int Ist. Andernfalls wird der Compiler verwirrt und nicht richtig kompiliert.

//this won't compile

implicit def doubleToInt(d: Double) = d.toInt
implicit def doubleToIntSecond(d: Double) = d.toInt
val x: Int = 42.0
4
rileyss

In scala implizit funktioniert als:

Konverter

Parameterwert Injektor

Es gibt 3 Arten der Verwendung von Implicit

  1. Implizit Typkonvertierung: Konvertiert die fehlererzeugende Zuweisung in den vorgesehenen Typ

    val x: String = "1"

    wert y: Int = x

Stringist nicht der -Untertyp von Int, daher tritt der Fehler in Zeile 2 auf. Um den Fehler zu beheben, sucht der Compiler im Gültigkeitsbereich nach einer solchen Methode, die ein implizites Schlüsselwort enthält, und verwendet Stringals Argument und gibt ein Intzurück.

damit

implicit def z(a:String):Int = 2

val x :String = "1"

val y:Int = x // compiler will use z here like val y:Int=z(x)

println(y) // result 2  & no error!
  1. Implizit Empfängerumwandlung: Wir rufen im Allgemeinen die Eigenschaften eines Empfängerobjekts auf, z. B. Methoden oder Variablen. Um also eine Eigenschaft eines Empfängers aufzurufen, muss die Eigenschaft Mitglied der Klasse/des Objekts dieses Empfängers sein.

    class Mahadi{
    
    val haveCar:String ="BMW"
    
    }
    

    class Johnny{

    val haveTv:String = "Sony"

    }

   val mahadi = new Mahadi



   mahadi.haveTv // Error happening

Hier wird mahadi.haveTveinen Fehler erzeugen. Denn scala Der Compiler sucht zuerst nach der Eigenschaft haveTvfür mahadireceiver. Wird nicht gefunden. Zweitens Es wird nach einer Methode im Gültigkeitsbereich mit implizites Schlüsselwortgesucht, die Mahadi-Objektals Argument verwendet und Johnny-Objektzurückgibt. Dies ist jedoch nicht der Fall. Daher wird FehlerAber das Folgende ist in Ordnung.

class Mahadi{

val haveCar:String ="BMW"

}

class Johnny{

val haveTv:String = "Sony"

}

val mahadi = new Mahadi

implicit def z(a:Mahadi):Johnny = new Johnny

mahadi.haveTv // compiler will use z here like new Johnny().haveTv

println(mahadi.haveTv)// result Sony & no error
  1. Implizit Parameterinjektion: Wenn wir eine Methode aufrufen und ihren Parameterwert nicht übergeben, verursacht dies einen Fehler. Der scala= Compiler funktioniert wie folgt - zuerst wird versucht, Wert zu übergeben, es wird jedoch kein direkter Wert für den Parameter erhalten.

    def x(a:Int)= a
    
    x // ERROR happening
    

Zweitens, wenn der Parameter ein implizites Schlüsselwort hat, sucht er nach einem valim scope, das den gleichen Typvon Wert hat. Wenn nicht, wird er einen Fehler verursachen.

def x(implicit a:Int)= a

x // error happening here

Um dieses Problem zu lösen, sucht der Compiler nach einem impliziten Wertmit dem Typ von Int, da der Parameter aimplizites Schlüsselworthat.

def x(implicit a:Int)=a

implicit val z:Int =10

x // compiler will use implicit like this x(z)
println(x) // will result 10 & no error.

Ein weiteres Beispiel:

def l(implicit b:Int)

def x(implicit a:Int)= l(a)

wir können es auch so schreiben

def x(implicit a:Int)= l

Da leinen impliziten Parameterund im Gültigkeitsbereich von bodyder Methode x) einen impliziten lokalen Variablen( Parameter sind lokale Variablen ) adas ist der Parameter von x, also im Hauptteil von xmethoddie Methodensignatur das implizite Argument von l) valuewird durch die lokale implizite Variable (Parameter) a der Methode x) implizitabgelegt.

Damit

 def x(implicit a:Int)= l

wird so in Compiler sein

def x(implicit a:Int)= l(a)

Ein weiteres Beispiel:

def c(implicit k:Int):String = k.toString

def x(a:Int => String):String =a

x{
x => c
}

es wird Fehler verursachen, weil cin x {x => c}explizit die Übergabe von Werten in Argumenten oder impliziten Werten erfordert in scope.

Also können wir den Parameterexplizit implizitbeim Aufruf der Methode x

x{
implicit x => c // the compiler will set the parameter of c like this c(x)
}

Dies wurde in action methodvon Play-Framework verwendet

def index = Action{
implicit request =>

Ok(views.html.formpage)

}
0
MHJ