Verstehen, was das Schlüsselwort 'type' in Scala bewirkt

144

Ich bin neu bei Scala und konnte nicht wirklich viel über das typeKeyword finden. Ich versuche zu verstehen, was der folgende Ausdruck bedeuten kann:

type FunctorType = (LocalDate, HolidayCalendar, Int, Boolean) => LocalDate

FunctorType ist eine Art Alias, aber was bedeutet es?

Core_Dumped
quelle

Antworten:

148

Ja, der Typalias FunctorType ist nur eine Abkürzung für

(LocalDate, HolidayCalendar, Int, Boolean) => LocalDate

Typ-Aliase werden häufig verwendet, um den Rest des Codes einfach zu halten: Sie können jetzt schreiben

def doSomeThing(f: FunctorType)

Dies wird vom Compiler als interpretiert

def doSomeThing(f: (LocalDate, HolidayCalendar, Int, Boolean) => LocalDate)

Dies hilft zu vermeiden, dass viele benutzerdefinierte Typen definiert werden, die beispielsweise nur Tupel oder Funktionen sind, die für andere Typen definiert wurden.

Es gibt auch einige andere interessante Anwendungsfälle für type, wie zum Beispiel in diesem Kapitel der Programmierung in Scala beschrieben .

Roland Ewald
quelle
198

Tatsächlich kann das typeSchlüsselwort in Scala viel mehr als nur das Aliasing eines komplizierten Typs auf einen kürzeren Namen. Es führt Typ Mitglieder .

Wie Sie wissen, kann eine Klasse Feld- und Methodenmitglieder haben. Nun, Scala erlaubt einer Klasse auch, Typmitglieder zu haben.

In Ihrem speziellen Fall typewird in der Tat ein Alias ​​eingeführt, mit dem Sie präziseren Code schreiben können. Das Typsystem ersetzt den Alias ​​nur durch den tatsächlichen Typ, wenn eine Typprüfung durchgeführt wird.

Sie können aber auch so etwas haben

trait Base {
  type T

  def method: T
}

class Implementation extends Base {
  type T = Int

  def method: T = 42
}

Wie jedes andere Mitglied einer Klasse können auch Typmitglieder abstrakt sein (Sie geben nur nicht an, welchen Wert sie tatsächlich haben) und in Implementierungen überschrieben werden.

Typmitglieder können als Dual von Generika angesehen werden, da viele der Dinge, die Sie mit Generika implementieren können, in abstrakte Typmitglieder übersetzt werden können.

Ja, sie können für das Aliasing verwendet werden, aber beschränken Sie sich nicht nur darauf, da sie eine leistungsstarke Funktion des Scala-Typsystems sind.

Weitere Informationen finden Sie in dieser ausgezeichneten Antwort:

Scala: Abstrakte Typen gegen Generika

Marius Danila
quelle
44
Es ist wichtig zu beachten, dass durch die Verwendung von type innerhalb einer Klasse ein Typelement anstelle eines Alias ​​erstellt wird. Wenn Sie also nur einen Typalias benötigen, definieren Sie ihn im Begleitobjekt.
Rüdiger Klaehn
9

Ich mochte die Antwort von Roland Ewald, da er sie mit einem sehr einfachen Anwendungsfall des Typalias beschrieb und für weitere Einzelheiten ein sehr schönes Tutorial vorstellte. Da in diesem Beitrag jedoch ein anderer Anwendungsfall mit dem Namen Typmitglieder vorgestellt wird , möchte ich den praktischsten Anwendungsfall erwähnen, der mir sehr gut gefallen hat: (Dieser Teil stammt von hier :)

Abstrakter Typ:

type T

T oben besagt, dass dieser Typ, der verwendet werden soll, noch unbekannt ist und abhängig von der konkreten Unterklasse definiert wird. Der beste Weg, um die Programmierkonzepte immer zu verstehen, ist ein Beispiel: Angenommen, Sie haben das folgende Szenario:

Ohne Typabstraktion

Hier wird ein Kompilierungsfehler angezeigt, da die Eat-Methode in den Klassen Cow und Tiger die Eat-Methode in der Klasse Animal nicht überschreibt, da ihre Parametertypen unterschiedlich sind. Es ist Gras in der Klasse Kuh und Fleisch in der Klasse Tiger gegen Futter in der Klasse Tier, was eine Superklasse ist und alle Unterklassen müssen übereinstimmen.

Zurück zur Typabstraktion: Durch das folgende Diagramm und einfaches Hinzufügen einer Typabstraktion können Sie den Typ der Eingabe in der entsprechenden Unterklasse selbst definieren.

Mit abstraktem Typ

Schauen Sie sich nun folgende Codes an:

  val cow1: Cow = new Cow
  val cow2: Cow = new Cow

  cow1 eat new cow1.SuitableFood
  cow2 eat new cow1.SuitableFood

  val tiger: Tiger = new Tiger
  cow1 eat new tiger.SuitableFood // Compiler error

Der Compiler freut sich und wir verbessern unser Design. Wir können unsere Kuh mit Kuh füttern. Geeignete Lebensmittel und Compiler verhindern, dass wir Kuh mit dem für Tiger geeigneten Futter füttern. Aber was ist, wenn wir zwischen dem Typ von cow1 SuitableFood und cow2 SuitabeFood unterscheiden wollen? Mit anderen Worten, es wäre in einigen Szenarien sehr praktisch, wenn der Weg, über den wir zum Typ gelangen (natürlich über ein Objekt), grundsätzlich eine Rolle spielt. Dank der erweiterten Funktionen von Scala ist Folgendes möglich:

Pfadabhängige Typen: Scala-Objekte können Typen als Mitglieder haben. Die Bedeutung des Typs hängt von dem Pfad ab, über den Sie darauf zugreifen. Der Pfad wird durch den Verweis auf ein Objekt (auch als Instanz einer Klasse bezeichnet) bestimmt. Um dieses Szenario zu implementieren, müssen Sie die Klasse Gras innerhalb der Kuh definieren, dh Kuh ist die äußere Klasse und Gras ist die innere Klasse. Die Struktur wird wie folgt aussehen:

  class Cow extends Animal {
    class Grass extends Food
    type SuitableFood = Grass
    override def eat(food: this.SuitableFood): Unit = {}
  }

  class Tiger extends Animal {
    class Meat extends Food
    type SuitableFood = Meat
    override def eat(food: this.SuitableFood): Unit = {}
  }

Wenn Sie nun versuchen, diesen Code zu kompilieren:

  1. val cow1: Cow = new Cow
  2. val cow2: Cow = new Cow

  3. cow1 eat new cow1.SuitableFood
  4. cow2 eat new cow1.SuitableFood // compilation error

In Zeile 4 wird ein Fehler angezeigt, da Gras jetzt eine innere Klasse von Kuh ist. Um eine Instanz von Gras zu erstellen, benötigen wir ein Kuhobjekt, und dieses Kuhobjekt bestimmt den Pfad. 2 Kuhobjekte führen also zu 2 verschiedenen Pfaden. In diesem Szenario möchte cow2 nur speziell dafür geschaffene Lebensmittel essen. So:

cow2 eat new cow2.SuitableFood

Jetzt sind alle glücklich :-)

Mehran
quelle
5

Nur ein Beispiel, um zu sehen, wie "Typ" als Alias ​​verwendet wird:

type Action = () => Unit

In der obigen Definition wird Aktion als Alias ​​für die Art der Prozeduren (Methoden) definiert, die eine leere Parameterliste verwenden und Unit zurückgeben.

sofiene zaghdoudi
quelle