Comprendre ce que fait le mot-clé 'type' dans Scala

144

Je suis nouveau sur Scala et je n'ai pas vraiment trouvé grand-chose sur le typemot - clé. J'essaie de comprendre ce que l'expression suivante peut signifier:

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

FunctorType est une sorte d'alias, mais que signifie-t-il?

Core_Dumped
la source

Réponses:

148

Oui, l' alias de type FunctorType n'est qu'un raccourci pour

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

Les alias de type sont souvent utilisés pour garder le reste du code simple: vous pouvez maintenant écrire

def doSomeThing(f: FunctorType)

qui sera interprété par le compilateur comme

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

Cela permet d'éviter de définir de nombreux types personnalisés qui ne sont que des tuples ou des fonctions définies sur d'autres types, par exemple.

Il existe également plusieurs autres cas d'utilisation intéressants pour type, comme décrit par exemple dans ce chapitre de Programmation dans Scala .

Roland Ewald
la source
198

En fait, le typemot - clé dans Scala peut faire beaucoup plus que simplement aliaser un type compliqué vers un nom plus court. Il présente les membres de type .

Comme vous le savez, une classe peut avoir des membres de champ et des membres de méthode. Eh bien, Scala permet également à une classe d'avoir des membres de type.

Dans votre cas particulier, il types'agit en effet d'introduire un alias qui vous permet d'écrire du code plus concis. Le système de type remplace simplement l'alias par le type réel lorsque la vérification de type est effectuée.

Mais tu peux aussi avoir quelque chose comme ça

trait Base {
  type T

  def method: T
}

class Implementation extends Base {
  type T = Int

  def method: T = 42
}

Comme tout autre membre d'une classe, les membres de type peuvent également être abstraits (vous ne spécifiez simplement pas leur valeur réelle) et peuvent être remplacés dans les implémentations.

Les membres de type peuvent être considérés comme doubles des génériques car la plupart des éléments que vous pouvez implémenter avec des génériques peuvent être traduits en membres de type abstrait.

Donc oui, ils peuvent être utilisés pour l'aliasing, mais ne les limitez pas à cela, car ils sont une fonctionnalité puissante du système de types de Scala.

Veuillez consulter cette excellente réponse pour plus de détails:

Scala: types abstraits vs génériques

Marius Danila
la source
44
Il est important de se rappeler que l'utilisation de type dans une classe crée un membre de type au lieu d'un alias. Donc, si vous n'avez besoin que d'un alias de type, définissez-le dans l'objet compagnon.
Rüdiger Klaehn
9

J'ai aimé la réponse de Roland Ewald car il a décrit avec un cas d'utilisation très simple d'alias de type, et pour plus de détails, il a présenté un très beau tutoriel. Cependant, puisqu'un autre cas d'utilisation est introduit dans cet article nommé type members , je voudrais en mentionner le cas d'utilisation le plus pratique, que j'ai beaucoup aimé: (cette partie est tirée d' ici :)

Type de résumé:

type T

T ci-dessus dit que ce type qui va être utilisé, est encore inconnu, et en fonction de la sous-classe concrète, il sera défini. La meilleure façon de toujours comprendre les concepts de programmation est de fournir un exemple: supposons que vous ayez le scénario suivant:

Sans abstraction de type

Ici, vous obtiendrez une erreur de compilation, car la méthode eat dans les classes Cow et Tiger ne remplace pas la méthode eat dans la classe Animal, car leurs types de paramètres sont différents. C'est Herbe dans la classe Vache et Viande dans la classe Tigre vs Nourriture dans la classe Animal qui est super classe et toutes les sous-classes doivent se conformer.

Revenons maintenant à l'abstraction de type, par le diagramme suivant et en ajoutant simplement une abstraction de type, vous pouvez définir le type de l'entrée, en fonction de la sous-classe elle-même.

Avec type abstrait

Maintenant, regardez les codes suivants:

  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

Le compilateur est content et nous améliorons notre conception. Nous pouvons nourrir notre vache avec de la vache. La nourriture et le compilateur nous empêchent de nourrir la vache avec la nourriture qui convient à Tiger. Mais que se passe-t-il si nous voulons faire la différence entre le type de vache1 AppropriéFood et vache2 SuitabeFood. En un autre mot, il serait très pratique dans certains scénarios si le chemin par lequel nous atteignons le type (bien sûr via objet) importe fondamentalement. Grâce aux fonctionnalités avancées de scala, il est possible:

Types dépendant du chemin: les objets Scala peuvent avoir des types comme membres. La signification du type dépend du chemin que vous utilisez pour y accéder. Le chemin est déterminé par la référence à un objet (c'est-à-dire une instance d'une classe). Afin d'implémenter ce scénario, vous devez définir la classe Grass à l'intérieur de la vache, c'est-à-dire que Cow est la classe externe et Grass est la classe interne. La structure sera comme ceci:

  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 = {}
  }

Maintenant, si vous essayez de compiler ce code:

  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

À la ligne 4, vous verrez une erreur car Grass est maintenant une classe interne de Cow, par conséquent, pour créer une instance de Grass, nous avons besoin d'un objet vache et cet objet vache détermine le chemin. Donc 2 objets vache donnent lieu à 2 chemins différents. Dans ce scénario, cow2 veut uniquement manger de la nourriture spécialement créée pour elle. Alors:

cow2 eat new cow2.SuitableFood

Maintenant tout le monde est content :-)

Mehran
la source
5

Juste un exemple pour voir comment utiliser "type" comme alias:

type Action = () => Unit

La définition ci-dessus définit Action comme étant un alias du type de procédures (méthodes) qui prennent une liste de paramètres vide et qui renvoient Unit.

sofiene zaghdoudi
la source