Правила для использования подчеркивания в параметрах типа более высокого типа

#scala #generics #wildcard #existential-type #higher-kinded-types

#scala #общие #подстановочный знак #existential-type #типы более высокого типа

Вопрос:

Интересно, почему работает следующее (обратите внимание, что функтор или аппликатив не имеют значения для примера)

 trait Functor[F[_]]
trait Applicative[F[_]] extends Functor[F]
  

Но не

 trait Functor[F[_]]
trait Applicative[F[_]] extends Functor[F[_]]
  

Моя проблема связана с F vs F[_]

Какое здесь правило?

Особенно компилятор выдает критическую ошибку:

F[_] не принимает параметров типа, ожидается: один

Комментарии:

1. Вас смущают синтаксисы для экзистенциальных типов и типов более высокого типа?

Ответ №1:

Подчеркивание ( _ ) имеет разное значение на сайте определения (1) и на сайте вызова (2).

(1) На сайте определения подчеркивание означает, что универсальный (параметр типа) является конструктором типа ( * => * ), а не надлежащим типом ( * ).

 trait A[X]                        // X is a proper type, *
//     ^^^ definition
trait B[X]   extends  A[X]        // X is a proper type, *
//     ^^^ definition, ^^^ call

trait A[F[_]]                     // F is a type constructor, * => *
//      ^^^^ definition
trait B[F[_]]   extends  A[F]     // F is a type constructor, * => *
//      ^^^^ definition,  ^^^ call
  

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

(2) На сайте вызова для конструктора типа ( * => * ) F F[_] означает экзистенциальный тип. Существующий тип — это правильный тип ( * ).

 trait A[X]                        // X is a proper type, *
//     ^^^ definition

trait B[F[_]]                     // F is a type constructor, * => *
                extends  A[F[_]]  // F[_] is an existential type, *
//      ^^^^ definition,   ^^^^ call
  

Комментарии:

1. Глупый вопрос, но каково было бы правильное определение сайта вызова для типа. Я понял, что вы написали выше, но могу обобщить, что в моей голове делает сайт вызова, сайт вызова. Это только в случае расширений?

2. @MaatDeamon Я имел в виду везде, где вы ссылаетесь на тип (за исключением случаев, когда он определен). extends A[X] , type T = X , foo[X]() Bar[X] и т.д.

Ответ №2:

trait Functor[F[_]] означает, что для функтора требуется конструктор типа (тип с отверстием, который создает новый тип, когда отверстие заполнено).

Тогда, trait Applicative[F[_]] extends Functor[F] здесь вы говорите, что для Applicative также требуется что-то с одним отверстием. Таким образом, F уже понимается как нечто с одним отверстием, и поэтому передача его функтору имеет смысл.

Наконец, здесь extends Functor[F[_]] то же самое, что и extends Functor[F[T] forSome { type T } ] , поэтому вы заполняете пробел F , и, следовательно, он больше не применим к функтору.

Ответ №3:

В Scala 3.2 существует план (Dotty) сделать F[_] значение одинаковым как на сайте определения, так и на сайте вызова

В Scala 3.2 значение _ меняется с подстановочного знака на заполнитель для параметра типа.

таким образом, допустимым синтаксисом станет следующее

 trait Applicative[F[_]] extends Functor[F[_]] // Both F[_] represent type lambda [X] =>> F[X]
  

Цель состоит в том, чтобы заменить подстановочный знак (экзистенциальный) тип F[_] на F[?] . Это уже видно в Dotty REPL

 Starting dotty REPL...
scala> val l: List[_] = List(42)
val l: List[?] = List(42)
  

и если скомпилировано с source:3.1 -deprecation , предупреждение уже поднято

 dotc -source:3.1 -deprecation Main.scala
-- Deprecation Warning: Main.scala:2:14 ----------------------------------------
2 |  val l: List[_] = List(42)
  |              ^
  |        `_` is deprecated for wildcard arguments of types: use `?` instead
1 warning found
  

Для этой цели kind-projector превентивно изменил свой синтаксис type lambda с F[?] на F[*] , чтобы освободить место ? для существующего типа Scala 3.

В качестве дополнительного примечания рассмотрим, как вид конструктора типа может быть выражен в настоящее время в Dotty

 Starting dotty REPL...
scala> trait Functor[F <: [X] =>> Any]
     | trait Applicative[F <: [X] =>> Any] extends Functor[F]
// defined trait Functor
// defined trait Applicative

scala> trait Applicative[F <: [X] =>> Any] extends Functor[[X] =>> F[X]]
// defined trait Applicative
  

Вид [X] =>> Any является синтаксическим подобием неформальной нотации, * => * упомянутой Дмитрием выше.