2016-09-02 1 views
0

Ich versuche, die Typ Parameter Konzepte in Scala zu verstehen.Type Parameter in Scala

def sum [A] (a:A):A={a} 
// used single parameter and its working fine, able to pass any data type. 

hier:

def sum[A](a:A,b:A):A={a+b} //declare two arguments 

<console>:7: error: type mismatch; 
found : A 
required: String 
def sum[A](a:A,b:A):A={a+b} 

Die obige Funktion ein Typparameter hat, habe ich keinen Datentyp erwähnt - dann wie kommt es als String behandelt wird?

Antwort

3
def sum[A](a:A,b:A):A={a+b} 

Sie fügen T+T kann der Compiler ableiten, nicht die + für T, wird es den Standardwert verwenden implicit +: any2stringadd, so wird der Compiler required: String Fehler werfen.

siehe https://issues.scala-lang.org/browse/SI-8229

implicit final class any2stringadd[A](private val self: A) extends AnyVal { 
    def +(other: String): String = String.valueOf(self) + other 
    } 
2

ist an diesem von einer kleinen anderen Perspektive betrachten lassen: was eigentlich ist ein Parameter? Was heißt das?

Und beginnen wir mit etwas, mit dem Sie wahrscheinlich vertrauter sind: Wertparameter. Was bedeutet ein Wertparameter in einem Unterprogramm? Sprich, wir haben so etwas wie diese:

def foo(a, b) = { /* … */ } // deliberately ignoring types and the body 

Es bedeutet: Ich habe ein Unterprogramm foo mit zwei Parametern, a und b. Und das Unterprogramm ist allgemein, es ist egal, was die tatsächlichen konkrete Werte von a und b sind. Es funktioniert für alle Werte von a und b weil es nicht wissen, was die Werte a und b sind.

Etwas konkreter:

def plus(a: Int, b: Int) = a + b 

Wieder plus nicht weiß, was die tatsächlichen Werte von a und b sind. Es funktioniert für 2 und 3 ebenso wie für 23 und 42 oder 0 und 0. Es ist völlig und völlig unwissend über die konkreten Werte von a und b. Es weiß nur, dass a und b existieren.

Jetzt Typ Parameter sind das gleiche, nur auf der Typenebene statt der Wertstufe.

blabla[A] 

bedeutet dasselbe auf der Typebene als blabla(a) auf dem Wert Ebene bedeutet: „Ich habe eine Sache haben, ich habe keine Ahnung, was es ist (und ich interessiere mich nicht), aber ich rufe es A. "

Also, was Sie effektiv getan haben, ist, Scala zu sagen, dass Sie nichts über A wissen.Aber dann machst du etwas damit (oder vielmehr mit seiner Instanz): du fügst es hinzu. Aber, wie weißt du überhaupt kann es hinzufügen? Du weißt nicht einmal was es ist!

Also, es hinzufügen kann nicht möglicherweise arbeiten, muss es müssen fehlschlagen!

Allerdings ist die Weg es fehlschlägt etwas seltsam ist, und hat mit einigen der vordefinierten impliziten Konvertierungen im scala.Predef Objekt zu tun. Insbesondere, there is an implicit conversion for string concatenation, which can convert an arbitrary object into a String and then concatenate another String to it. In diesem Fall konvertiert es a in any2stringadd und versucht dann, b hinzuzufügen, aber die any2stringadd.+ Methode nimmt nur ein String als sein Argument, und damit erhalten Sie die seltsame Fehlermeldung, dass es eine String erwartet.

Es gibt ein paar andere ähnliche Typen, die manchmal auftauchen, wenn Sie komplexe Typfehler haben:

  • Any, AnyRef, AnyVal: diese Typen an der Spitze der Scala Typ Hierarchie sitzen. Manchmal, wenn Sie einen Programmierfehler haben, wo Sie denken, dass Sie den gleichen Typ aus zwei verschiedenen Codepfaden zurückgeben, aber Sie tatsächlich zwei verschiedene Typen zurückgeben, wird Scala trotzdem versuchen, den gemeinsamen Typ zwischen den beiden abzuleiten, und endet mit dem nur gemeinsamer Vorfahr ist Any, AnyRef oder AnyVal. (Das wäre wie Object in Java oder C♯.)
  • Serializable: das ist eigentlich das Gleiche wie oben. Viele verschiedene Arten implementieren die Serializable Schnittstelle, also manchmal, wenn Sie zwei sehr unterschiedliche Arten haben, wo Sie eigentlich den gleichen Typ erwarten, wird Scala hilfreich Serializable als der nächste gemeinsame Vorfahre ableiten, und dann Sie anschreien, um Sachen mit einem Serializable das zu tun Es wird nicht unterstützt.
  • Product ist ein super-Merkmal aller Product s (d.h. Product1, Product2, ... Product22) und damit alle Tuple s (d.h. Tuple1, Tuple2, ... Tuple22). ist auch in alle case class es gemischt. Also, wenn Sie zwei Tuple s verschiedener arity oder zwei nicht verwandten case class es (zB manchmal Sie None zurück, aber dann versehentlich zurückgeben somethingOfTypeFoo statt Some(somethingOfTypeFoo)), dann die präziseste allgemeinste Art zwischen Option und Ihre case class Foo als Product oder abgeleitet werden ...
  • Product with Serializable: es ist auch möglich, eine Kombination der oben genannten zu erhalten. Z.B. Tuple s sind Serializable, also dieser ist eigentlich der genaueste gemeinsame Typ von zwei Tuple s unterschiedlicher Arity.

Eine übliche Art und Weise in diese Probleme zu laufen ist in einem bedingten Ausdruck ohne else:

if (true) 42 

welche Art diese Rückkehr tut? Int? Nein! Der Zweig then gibt Int zurück, aber der Zweig else gibt nichts zurück (weil es keine else Verzweigung gibt).Scala hat tatsächlich einen Rückgabetyp, um nichts zurückzugeben: Unit. Unit ist ein Subtyp von AnyVal, Int ist ein Subtyp von AnyVal, so dass der nächste gemeinsame Vorfahr der Typen der beiden Zweige des bedingten Ausdrucks tatsächlich AnyVal ist, nicht Int. (Anmerkung: die Tatsache, dass der Zweig else nicht erreichbar ist, ist aus einer Schreibperspektive irrelevant. Erreichbarkeit ist eine Laufzeit-Sache, Typen sind eine Kompilierzeit-Sache.)