Capire cosa fa la parola chiave 'type' in Scala


144

Sono nuovo di Scala e non sono riuscito a trovare molto sulla typeparola chiave. Sto cercando di capire cosa può significare la seguente espressione:

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

FunctorType è una specie di alias, ma cosa significa?

Risposte:


148

Sì, il tipo alias FunctorType è solo una scorciatoia per

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

Gli alias di tipo vengono spesso utilizzati per mantenere semplice il resto del codice: ora puoi scrivere

def doSomeThing(f: FunctorType)

che sarà interpretato dal compilatore come

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

Questo aiuta ad evitare di definire molti tipi personalizzati che sono solo tuple o funzioni definite su altri tipi, ad esempio.

Ci sono anche molti altri casi d'uso interessanti per type, come descritto ad esempio in questo capitolo di Programmazione alla Scala .


198

In realtà la typeparola chiave in Scala può fare molto di più che semplicemente aliasare un tipo complicato con un nome più breve. Presenta membri del tipo .

Come sapete, una classe può avere membri del campo e membri del metodo. Bene, Scala consente anche a una classe di avere membri del tipo.

Nel tuo caso particolare type, infatti, viene introdotto un alias che ti consente di scrivere codice più conciso. Il sistema dei tipi sostituisce semplicemente l'alias con il tipo effettivo quando viene eseguito il controllo del tipo.

Ma puoi anche avere qualcosa del genere

trait Base {
  type T

  def method: T
}

class Implementation extends Base {
  type T = Int

  def method: T = 42
}

Come qualsiasi altro membro di una classe, anche i membri del tipo possono essere astratti (semplicemente non si specifica quale sia il loro valore in realtà) e possono essere sovrascritti nelle implementazioni.

I membri del tipo possono essere visti come un doppio dei generici poiché molte delle cose che è possibile implementare con i generici possono essere tradotte in membri di tipo astratto.

Quindi sì, possono essere usati per l'aliasing, ma non limitarli a questo, poiché sono una potente funzionalità del sistema di tipi di Scala.

Si prega di vedere questa risposta eccellente per maggiori dettagli:

Scala: tipi astratti vs generici


44
È importante ricordare che l'uso del tipo all'interno di una classe crea un membro del tipo anziché un alias. Quindi, se hai bisogno solo di un alias di tipo, definiscilo nell'oggetto associato.
Rüdiger Klaehn,

9

Mi è piaciuta la risposta di Roland Ewald poiché mi ha descritto con un semplicissimo caso d'uso di alias di tipo, e per maggiori dettagli ho introdotto un tutorial molto carino. Tuttavia, poiché in questo post viene introdotto un altro caso d'uso denominato membri di tipo , vorrei menzionarne il caso d'uso più pratico, che mi è piaciuto molto: (questa parte è tratta da qui :)

Tipo astratto:

type T

T sopra dice che questo tipo che verrà usato, è ancora sconosciuto, e in base alla sottoclasse concreta, verrà definito. Il modo migliore per comprendere sempre i concetti di programmazione è fornire un esempio: supponiamo di avere il seguente scenario:

Senza astrazione di tipo

Qui otterrai l'errore di compilazione, perché il metodo eat nelle classi Cow e Tiger non sovrascrive il metodo eat nella classe Animal, perché i loro tipi di parametri sono diversi. È Grass in class Cow e Meat in class Tiger vs. Food in class Animal che è super class e tutte le sottoclassi devono conformarsi.

Ora torniamo al tipo di astrazione, con il seguente diagramma e semplicemente aggiungendo un tipo di astrazione, è possibile definire il tipo di input, in base alla sottoclasse stessa.

Con Tipo Astratto

Ora guarda i seguenti codici:

  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

Il compilatore è felice e miglioriamo il nostro design. Siamo in grado di nutrire la nostra mucca con la mucca.AdattoCibo e compilatore ci impediscono di nutrire la mucca con il cibo adatto per Tiger. E se volessimo fare la differenza tra il tipo di cow1 AdattoFood e cow2 SuitabeFood. In altre parole, sarebbe molto utile in alcuni scenari se il percorso attraverso il quale raggiungiamo il tipo (ovviamente tramite l'oggetto) è fondamentalmente importante. Grazie alle funzionalità avanzate di scala, è possibile:

Tipi dipendenti dal percorso: gli oggetti Scala possono avere tipi come membri. Il significato del tipo dipende dal percorso utilizzato per accedervi. Il percorso è determinato dal riferimento a un oggetto (ovvero un'istanza di una classe). Per implementare questo scenario, è necessario definire la classe Grass all'interno della Cow, ovvero Cow è la classe esterna e Grass è la classe interna. La struttura sarà così:

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

Ora se provi a compilare questo codice:

  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

Nella riga 4 vedrai un errore perché Grass è ora una classe interna di Cow, quindi, per creare un'istanza di Grass, abbiamo bisogno di un oggetto cow e questo oggetto cow determina il percorso. Quindi 2 oggetti di mucca danno origine a 2 percorsi diversi. In questo scenario, cow2 vuole solo mangiare cibo appositamente creato per questo. Così:

cow2 eat new cow2.SuitableFood

Ora tutti sono felici :-)


5

Solo un esempio per vedere come usare "type" come alias:

type Action = () => Unit

La definizione sopra definisce Action come un alias del tipo di procedure (metodi) che accettano un elenco di parametri vuoto e che restituiscono Unit.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.