2016-12-20 1 views
22

Der Unit bekommt beim Generieren von Byte-Code eine spezielle Behandlung durch den Compiler, weil er analog zu void auf dem jvm ist. Aber konzeptionell als ein Typ innerhalb des Scala-Typ-Systems scheint es, dass es auch in der Sprache selbst eine spezielle Behandlung erfährt (Beispiele unten).Welche Sonderregeln hat der Scala-Compiler für den Unit-Typ innerhalb des Typsystems?

Also meine Frage ist, dies zu klären und zu verstehen, welche Mechanismen verwendet werden, und wenn es wirklich eine spezielle Behandlung für die Unit Art gibt.


Beispiel 1:

Für "normale" scala Typen wie Seq, wenn eine Methode Seq zurückgibt, dann müssen Sie Seq zurückkehren (oder einen spezifischeren Typ, der Seq erweitert)

def foo1: Seq[Int] = List(1, 2, 3) 
def foo2: Seq[Int] = Vector(1, 2, 3) 
def foo3: Seq[Int] = "foo" // Fails 

Die ersten beiden Beispiele kompilieren, weil List[Int] und Vector[Int] Subtypen vonsind. Der dritte schlägt fehl, weil String nicht ist.

Aber wenn ich das dritte Beispiel ändern Unit obwohl zurückzukehren, es wird kompilieren und ohne Probleme ausgeführt werden, obwohl Stringist nicht ein Subtyp von Unit:

def foo3(): Unit = "foo" // Compiles (with a warning) 

Ich weiß nicht, von jedem anderen Typ, für den diese Ausnahme in scala erlaubt wäre. Hat der Compiler spezielle Regeln für den Typ Unit auf der Systemtypebene oder gibt es eine Art allgemeinerer Mechanismus bei der Arbeit, z. eine implizite Konvertierung


Beispiel 2:

Ich bin auch nicht klar, wie Einheit in Situationen, in Wechselwirkung tritt, wo Varianz Regeln normalerweise angewandt werden würden.

Zum Beispiel, wir schlagen manchmal diesen Fehler mit Future[Unit] wo wir versehentlich map anstelle von flatMap und erstellen Future[Future]:

def save(customer: Customer): Future[Unit] = ... // Save to database 

def foo: Future[Unit] = save(customer1).map(_ => save(customer2)) 

Die map Schaffung ist ein Future[Future[Unit]] und der Compiler ein Future[Unit] erfordert. Doch das kompiliert!

Zuerst dachte ich, das war, weil Future[+T] covariant ist, aber eigentlich Future[Unit] ist kein Subtyp von Unit, damit es nicht, dass zu sein scheint.

Wenn der Typ zu Boolean beispielsweise geändert wird, erkennt der Compiler den Fehler:

def save(customer: Customer): Future[Boolean] = ... 

def foo: Future[Boolean] = save(customer1).map(_ => save(customer2)) // Compiler fails this 

Und für jeden anderen nicht Unit Art wird es nicht kompilieren (außer Any weil Future[Any] ein Subtyp sein geschieht von Any durch Zufall).

Hat der Compiler in diesem Fall spezielle Regeln? Oder gibt es einen allgemeineren Prozess?

+1

im Grunde, wenn Sie irgendeine Art als Einheit (ein Wert oder eine Funktion Rückkehr zum Beispiel) erklären, der Compiler wandelt, was Wert, den es zu Einheit hat. nicht sicher, ob es die Antwort ist, die Sie suchen, oder wenn Sie nach mehr internen Details suchen. – pedrorijo91

Antwort

11

Ich werde die Titelfrage für mehr Abdeckung beantworten. Unit bekommt an einigen Stellen eine spezielle Behandlung, mehr als das, was in diesen Codebeispielen vor sich geht. Dies liegt zum Teil daran, dass Unit ein Teil des Compilers ist, der auf void auf der JVM reduziert wird.


Wert Wegwerfen

Dies ist der überraschendste Fall für die Menschen.Jedes Mal, den erwarteten Typ von einigem Wert ist Unit der Compiler Angriffe auf Unit am Ende des Ausdrucks, der den Wert erzeugt, nach dem SLS - 6.26.1:

Wenn ee hat einen Wert Typ und der erwarteten Typ ist Unitee wird in den erwarteten Typ konvertiert, indem sie in den Ausdruck { ee;() } eingebettet wird.

So

def foo3(): Unit = "foo" 

wird:

def foo3(): Unit = { "foo" ;() } 

Ebenso

def foo: Future[Unit] = save(customer1).map(_ => save(customer2)) 

wird:

def foo: Future[Unit] = save(customer1).map(_ => { save(customer2);() }) 

Der Nutzen von diesem ist, dass Sie nicht die letzte Aussage einer Methode haben müssen, haben Sie den Typ Unit, wenn Sie nicht wollen. Dieser Vorteil ist jedoch gering, denn wenn die letzte Anweisung Ihrer Methode, die Unit zurückgibt, keine Unit ist, gibt normalerweise einen Fehler an, weshalb es eine Warnmeldung dafür gibt (-Ywarn-value-discard).

Im Allgemeinen finde ich es besser, wenn möglich einen spezifischeren Typ zurückzugeben, als Unit zurückzugeben. Wenn Sie beispielsweise in einer Datenbank speichern, können Sie möglicherweise den gespeicherten Wert zurückgeben (möglicherweise mit einer neuen ID oder etwas anderem).


Value Klasse

Unit ist ein Wert, Klasse vom Compiler Scala erstellt, mit nur einer Instanz (wenn es überhaupt als eine Klasse instanziiert werden muss). Dies bedeutet, dass es bis zum Primitiv void auf der JVM kompiliert wird, es sei denn, Sie behandeln es als eine Klasse (z. B. ().toString). Es hat seinen eigenen Abschnitt in der Spezifikation, SLS - 12.2.13.


leeren Block Typ

Vom SLS - 6.11, wird der Standardtyp eines leeren Block angenommen Unit zu sein. Zum Beispiel:

scala> val x = { } 
x: Unit =() 

Equals

Wenn ein Unit an einen anderen Unit zu vergleichen (was dasselbe Objekt sein muss, da es nur eine ist), wird der Compiler eine spezielle Warnung auszusenden informieren Sie etwas ist wahrscheinlich falsch in Ihrem Programm.

scala>().==(()) 
<console>:12: warning: comparing values of types Unit and Unit using `==' will always yield true 
     ().==(()) 
      ^
res2: Boolean = true 

Casting Sie alles zu einem Unit werfen kann, wie der Compiler es weg optimieren (obwohl es mir unklar ist, wenn der Wert Wegwerfen übernimmt nach Typinferenz).

object Test { 
    val a = "a".asInstanceOf[Unit] 
    val b = a 
} 

wird:

object Test extends Object { 
    def <init>(): Test.type = { 
    Test.super.<init>(); 
    () 
    }; 
    private[this] val a: scala.runtime.BoxedUnit = scala.runtime.BoxedUnit.UNIT; 
    <stable> <accessor> def a(): Unit =(); 
    private[this] val b: scala.runtime.BoxedUnit = scala.runtime.BoxedUnit.UNIT; 
    <stable> <accessor> def b(): Unit =() 
} 
+0

Danke für die große Antwort! Ich denke, Sie meinen 6.26.1, nicht 6.22.1, wenn Sie die Sprachspezifikation verknüpfen. – rmin

14

Wie im 6.26.1 scala language specification Kapitel geschrieben:

Wert Wegwerfen

Wenn e einen Wert Typ und der erwarteten Typ hat, ist Einheit, e in den erwarteten Typ von umgewandelt wird Einbettung in den Term {e;()}.

13

Die Antwort von rethab gab Ihnen bereits den Link zur Spezifikation; Lassen Sie mich noch hinzufügen, dass

  • Sie können deaktivieren dies durch die -Xfatal-warnings Compiler-Flag
  • Sie erhalten eine bessere Nachrichten mit der -Ywarn-value-discard Flagge (die Warnung einen Fehler machen); für foo3 wird die Compiler Warnung der informative discarded non-Unit value

Hinweis sein, dass diese „jede zu Unit“ Umwandlung Compiler Magie ist, so dass weder -Yno-predef oder -Yno-imports wird es deaktivieren; Du brauchst die obigen Flaggen. Ich halte dies als Teil der Sprachspezifikation einen Fehler, als wenn Sie aus irgendeinem Grund dieses fragwürdige Verhalten möchten, können Sie einfach nur etwas hinzufügen, wie

implicit def any2Unit(a: Any): Unit =() 

während opting-out davon eine nicht unterstützte (per Definition erfordert, wie es bricht die Spezifikation) Compiler-Flag.

Ich empfehle auch , wo Sie this und vieles mehr haben.

Verwandte Themen