Понимание того, что делает ключевое слово type в Scala

144

Я новичок в Scala, и я не мог найти много о typeключевом слове. Я пытаюсь понять, что может означать следующее выражение:

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

FunctorType это какой-то псевдоним, но что он означает?

Core_Dumped
источник

Ответы:

148

Да, псевдоним типа FunctorType это просто сокращение для

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

Псевдонимы типа часто используются для простоты остальной части кода: теперь вы можете написать

def doSomeThing(f: FunctorType)

который будет интерпретирован компилятором как

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

Это помогает избежать определения многих пользовательских типов, которые являются просто кортежами или функциями, определенными, например, для других типов.

Есть также несколько других случаев интересно использовать для type, как описано, например , в этой главе о Программирование в Scala .

Роланд Эвальд
источник
198

На самом деле typeключевое слово в Scala может сделать гораздо больше, чем просто наложить сложный тип на более короткое имя. Он вводит членов типа .

Как вы знаете, у класса могут быть члены поля и члены метода. Ну, Scala также позволяет классу иметь членов типа.

В вашем конкретном случае type , действительно, вводится псевдоним, который позволяет писать более лаконичный код. Система типов просто заменяет псевдоним реальным типом, когда выполняется проверка типа.

Но вы также можете иметь что-то вроде этого

trait Base {
  type T

  def method: T
}

class Implementation extends Base {
  type T = Int

  def method: T = 42
}

Как и любой другой член класса, члены типа также могут быть абстрактными (вы просто не указываете их значение на самом деле) и могут быть переопределены в реализациях.

Члены типа могут рассматриваться как двойные обобщения, так как многие вещи, которые вы можете реализовать с помощью обобщения, могут быть преобразованы в члены абстрактного типа.

Так что да, они могут быть использованы для псевдонимов, но не ограничивайте их только этим, так как они являются мощной функцией системы типов Scala.

Пожалуйста, посмотрите этот отличный ответ для более подробной информации:

Scala: абстрактные типы и дженерики

Мариус Данила
источник
44
Важно помнить, что использование типа внутри класса создает член типа вместо псевдонима. Поэтому, если вам нужен просто псевдоним типа, определите его в объекте-компаньоне.
Rüdiger Klaehn
9

Мне понравился ответ Роланда Эвальда, так как он описал с очень простым вариантом использования псевдонима типа и для более подробной информации представил очень хороший учебник. Однако, поскольку в этом посте представлен еще один вариант использования с именами членов типа , я хотел бы упомянуть наиболее практичный вариант его использования, который мне очень понравился: (эта часть взята отсюда :)

Абстрактный тип:

type T

T выше говорит, что этот тип, который будет использоваться, пока неизвестен, и в зависимости от конкретного подкласса он будет определен. Лучший способ всегда понять концепции программирования - привести пример. Предположим, у вас есть следующий сценарий:

Без абстракции типов

Здесь вы получите ошибку компиляции, потому что метод eat в классах Cow и Tiger не переопределяет метод eat в классе Animal, потому что их типы параметров различны. Это Трава в классе Корова и Мясо в классе Tiger vs. Food в классе Animal, который является суперклассом, и все подклассы должны соответствовать.

Теперь вернемся к абстракции типов, используя следующую диаграмму и просто добавив абстракцию типов, вы можете определить тип ввода в соответствии с самим подклассом.

С абстрактным типом

Теперь посмотрите на следующие коды:

  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

Компилятор доволен, и мы улучшаем наш дизайн. Мы можем кормить нашу корову коровой. Подходящие корма и компилятор мешают нам кормить коров едой, подходящей для тигра. Но что, если мы хотим сделать различие между типом коровы1 Подходящая еда и корова2 Подходящая еда? Другими словами, в некоторых сценариях было бы очень удобно, если бы путь, по которому мы достигаем тип (конечно, через объект), действительно имел значение. Благодаря расширенным функциям в Scala возможно:

Типы, зависящие от пути: объекты Scala могут иметь типы в качестве членов. Значение типа зависит от пути, который вы используете для доступа к нему. Путь определяется ссылкой на объект (он же экземпляр класса). Для реализации этого сценария необходимо определить класс Grass внутри Cow, т. Е. Cow - это внешний класс, а Grass - внутренний класс. Структура будет такой:

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

Теперь, если вы попытаетесь скомпилировать этот код:

  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

В строке 4 вы увидите ошибку, поскольку Grass теперь является внутренним классом Cow, поэтому для создания экземпляра Grass нам нужен объект cow, и этот объект cow определяет путь. Таким образом, 2 объекта коровы дают начало двум различным путям. В этом сценарии cow2 хочет есть только пищу, специально созданную для этого. Так:

cow2 eat new cow2.SuitableFood

Теперь все счастливы :-)

Mehran
источник
5

Просто пример, чтобы увидеть, как использовать «тип» в качестве псевдонима:

type Action = () => Unit

Вышеприведенное определение определяет Action как псевдоним типа процедур (методов), которые принимают пустой список параметров и возвращают Unit.

Софиене Загдуди
источник