Entendendo o que a palavra-chave 'type' faz no Scala

144

Eu sou novo no Scala e não consegui encontrar muita coisa sobre a typepalavra - chave. Estou tentando entender o que a seguinte expressão pode significar:

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

FunctorType é algum tipo de apelido, mas o que isso significa?

Core_Dumped
fonte

Respostas:

148

Sim, o alias de tipo FunctorType é apenas uma abreviação de

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

Os aliases de tipo geralmente são usados ​​para manter o restante do código simples: agora você pode escrever

def doSomeThing(f: FunctorType)

que será interpretado pelo compilador como

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

Isso ajuda a evitar a definição de muitos tipos personalizados que são apenas tuplas ou funções definidas em outros tipos, por exemplo.

Existem também vários outros casos de uso interessantes para type, como descrito, por exemplo, neste capítulo de Programação no Scala .

Roland Ewald
fonte
198

Na verdade, a typepalavra - chave no Scala pode fazer muito mais do que alias um tipo complicado para um nome mais curto. Introduz membros do tipo .

Como você sabe, uma classe pode ter membros de campo e membros de método. Bem, Scala também permite que uma classe tenha membros do tipo.

No seu caso particular, typeé, de fato, a introdução de um alias que permite escrever um código mais conciso. O sistema de tipos apenas substitui o alias pelo tipo real quando a verificação de tipo é realizada.

Mas você também pode ter algo parecido com isto

trait Base {
  type T

  def method: T
}

class Implementation extends Base {
  type T = Int

  def method: T = 42
}

Como qualquer outro membro de uma classe, os membros do tipo também podem ser abstratos (você não especifica qual é realmente o valor deles) e podem ser substituídos nas implementações.

Os membros do tipo podem ser vistos como dois genéricos, pois muitas das coisas que você pode implementar com os genéricos podem ser traduzidas em membros do tipo abstrato.

Portanto, sim, eles podem ser usados ​​para criar aliases, mas não os limite apenas a isso, pois são um recurso poderoso do sistema de tipos da Scala.

Por favor, veja esta excelente resposta para mais detalhes:

Scala: Tipos abstratos vs genéricos

Marius Danila
fonte
44
É importante lembrar que o uso de tipo dentro de uma classe cria um membro de tipo em vez de um alias. Portanto, se você precisar apenas de um alias de tipo, defina-o no objeto complementar.
Rüdiger Klaehn
9

Gostei da resposta de Roland Ewald, pois ele descreveu com um caso de uso muito simples do alias de tipo e, para mais detalhes, apresentou um tutorial muito bom. No entanto, como outro caso de uso é introduzido nesta postagem com o nome de membros do tipo , gostaria de mencionar o caso de uso mais prático, do qual gostei muito: (esta parte é retirada daqui :)

Tipo de resumo:

type T

T acima diz que esse tipo que será usado ainda é desconhecido e, dependendo da subclasse de concreto, será definido. A melhor maneira sempre de entender os conceitos de programação é fornecer um exemplo: Suponha que você tenha o seguinte cenário:

Sem abstração de tipo

Aqui você receberá um erro de compilação, porque o método eat nas classes Cow e Tiger não substitui o método eat na classe Animal, porque seus tipos de parâmetros são diferentes. É Grass na classe Cow e Meat na classe Tiger vs. Food na classe Animal, que é super classe e todas as subclasses devem estar em conformidade.

Agora, de volta ao tipo abstração, pelo diagrama a seguir e simplesmente adicionando um tipo abstração, você pode definir o tipo da entrada, de acordo com a própria subclasse.

Com tipo abstrato

Agora observe os seguintes códigos:

  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

O compilador está feliz e melhoramos nosso design. Podemos alimentar nossa vaca com vaca. O alimento e o compilador nos impedem de alimentar a vaca com o alimento adequado para o Tiger. Mas e se quisermos fazer a diferença entre o tipo de vaca1 AdequadoFood e cow2 SuitabeFood. Em outra palavra, seria muito útil em alguns cenários se o caminho pelo qual chegamos ao tipo (é claro via objeto) realmente importa. Graças aos recursos avançados do scala, é possível:

Tipos dependentes de caminho: os objetos Scala podem ter tipos como membros. O significado do tipo depende do caminho que você usa para acessá-lo. O caminho é determinado pela referência a um objeto (também conhecido como instância de uma classe). Para implementar esse cenário, você precisa definir a classe Grass dentro da Cow, ou seja, Cow é a classe externa e Grass é a classe interna. A estrutura será assim:

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

Agora, se você tentar compilar este código:

  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

Na linha 4, você verá um erro porque o Grass agora é uma classe interna do Cow, portanto, para criar uma instância do Grass, precisamos de um objeto de vaca e esse objeto de vaca determina o caminho. Então, 2 objetos de vaca dão origem a 2 caminhos diferentes. Nesse cenário, o cow2 quer apenas comer alimentos especialmente criados para ele. Assim:

cow2 eat new cow2.SuitableFood

Agora todo mundo está feliz :-)

Mehran
fonte
5

Apenas um exemplo para ver como usar "type" como alias:

type Action = () => Unit

A definição acima define Ação como um alias do tipo de procedimentos (métodos) que recebem uma lista de parâmetros vazia e que retornam Unidade.

sofiene zaghdoudi
fonte