web-dev-qa-db-fra.com

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

Je suis nouveau sur Scala et je n'ai pas trouvé grand chose à propos du mot clé type. J'essaie de comprendre ce que l'expression suivante peut vouloir dire:

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

FunctorType est une sorte de pseudonyme, mais que signifie-t-il?

137
Core_Dumped

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 sur Programmation en Scala .

136
Roland Ewald

En fait, le mot clé type dans Scala peut faire beaucoup plus que simplement aliaser un type compliqué en un nom plus court. Il introduit des 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, type introduit effectivement un alias qui vous permet d'écrire du code plus concis. Le système de types remplace simplement l'alias par le type actuel lors de la vérification du type.

Mais vous pouvez 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 pas quelle est leur valeur réelle) et peuvent être remplacés dans les implémentations.

Les membres de type peuvent être considérés comme deux types de génériques, car une grande partie de ce que vous pouvez implémenter avec des génériques peut être traduite en membres de type abstrait.

Donc, oui, ils peuvent être utilisés pour créer des alias, mais ne les limitez pas à cela, car ils constituent une fonctionnalité puissante du système de types de Scala.

S'il vous plaît voir cette excellente réponse pour plus de détails:

Scala: Types abstraits vs génériques

184
Marius Danila

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

type Action = () => Unit

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

5
sofiene zaghdoudi

J'ai aimé la réponse de Roland Ewald puisqu'il a décrit avec un cas d'utilisation très simple de type alias, et pour plus de détails introduit un très beau tutoriel. Cependant, puisqu’un autre cas d’utilisation est introduit dans ce post nommé membres de type , je voudrais mentionner le cas d’utilisation le plus pratique, qui me plaisait beaucoup. beaucoup: (cette partie est tirée de ici :)

Type abstrait:

type T

T ci-dessus indique que ce type, qui sera utilisé, est encore inconnu et qu’il sera défini en fonction de la sous-classe de béton. La meilleure façon de toujours comprendre les concepts de programmation est de fournir un exemple: supposons que vous ayez le scénario suivant:

Without Type Abstraction

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 l'herbe dans la classe Vache et la viande dans la classe Tiger contre la nourriture dans la classe Animal qui est une classe superbe et toutes les sous-classes doivent être conformes.

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.

With Abstract Type

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 heureux et nous améliorons notre conception. Nous pouvons nourrir notre vache avec une vache.La nourriture et le compilateur appropriés nous empêchent de nourrir la vache avec la nourriture qui convient au Tigre. Mais que se passe-t-il si nous voulons faire la différence entre le type de vache1 ReadyFood et cow2 SuitabeFood Dans 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 un objet) a une importance fondamentale. Grâce aux fonctionnalités avancées de scala, il est possible:

Types dépendants du chemin: _ Les objets Scala peuvent avoir des types en tant que 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). Pour implémenter ce scénario, vous devez définir la classe Grass à l’intérieur de la vache, c’est-à-dire que la vache est la classe externe et que 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

Sur 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 ce dernier détermine le chemin. Donc 2 objets vache donnent lieu à 2 chemins différents. Dans ce scénario, cow2 ne veut manger que des aliments spécialement créés à cet effet. Alors:

cow2 eat new cow2.SuitableFood

Maintenant tout le monde est content :-)

4
Mehran