Компилятор Scala расширяет типы

Рассмотрим этот код:

  trait TypeOr[E, F] {
    type T
  }

  implicit def noneq2[E, F](implicit ev: E =!= F): TypeOr[E, F] = new TypeOr[E, F] {
    type T = (E, F)
  }

  sealed trait Error[+E, +A]

  case class Err[E, A](e: Error[E, A]) {
    def combine[B, F](f: A => Error[F, B])(implicit ev: TypeOr[E, F]): Error[ev.T, B] = ???
  }
  val result = Err(null.asInstanceOf[Error[Int, Int]]).combine(_ => null.asInstanceOf[Error[String, String]])

Все идет нормально. Из приведенных выше определений я сделал вывод, что расширенный тип результата следующий:

  val itsType: Error[(Int, String), String] = result

Но, видимо, это не так, поскольку компилятор отвечает:

 found   : returnerror.Comb.Error[returnerror.Comb.TypeOr[Int,String]#T,String]
 required: returnerror.Comb.Error[(Int, String),String]
  val itsType: Error[(Int, String), String] = result

Можно ли узнать упрощенно-расширенный тип выражения? Я не могу получить эту информацию от компилятора, я пытался напечатать AST перед фазой стирания, но расширенного типа все еще нет.

Вы также можете предоставить код для returnerror.TypeOr?

Bogdan Vakulenko 10.04.2019 11:51

Компилятор не считает, что TypeOr и Tuple2 эквивалентны. Должны ли они быть?

jwvh 10.04.2019 12:03

Мне особенно интересно узнать, что компилятор считает этими типами больше, чем просто решение этого конкретного случая. Я попытался распечатать фазы компилятора, но он не печатает расширенный тип ни на одной фазе.

ryskajakub 10.04.2019 16:47

@coubeatczech Попробуйте добавить scalacOptions in Compile ++= Seq("-Xprint-types", "-Xprint:typer") в build.sbt.

Dmytro Mitin 10.04.2019 17:00

Или вы можете ввести выражение в REPL, и оно напечатает тип и значение этого выражения. Или вы можете использовать scala-reflect. import scala.reflect.runtime.universe._def printType[A: TypeTag](a: A) = println(typeOf[A])

Dmytro Mitin 10.04.2019 17:04

Или println(typeOf[A].dealias).

Dmytro Mitin 10.04.2019 17:07
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
7
182
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Во-первых, когда вы пишете, что имплицитный noneq2 имеет тип TypeOr[E, F], вы теряете уточнение типа https://typelevel.org/blog/2015/07/19/forget-refinement-aux.html. Правильно

implicit def noneq2[E, F](implicit ev: E =:!= F) = new TypeOr[E, F] {
  type T = (E, F)
}

или лучше с явным типом

implicit def noneq2[E, F](implicit ev: E =:!= F): TypeOr[E, F] { type T = (E, F) }  = new TypeOr[E, F] {
  type T = (E, F)
}

Вот почему обычно вводится тип Aux

object TypeOr {
  type Aux[E, F, T0] = TypeOr[E, F] { type T = T0 }

  implicit def noneq2[E, F](implicit ev: E =:!= F): Aux[E, F, (E, F)] = new TypeOr[E, F] {
    type T = (E, F)
  }
}

Во-вторых, автоматически выведенный тип result, т.е. Error[TypeOr[Int, String]#T, String] (проекция типа TypeOr[Int,String]#T является надтипом (y.T forSome { val y: TypeOr[Int, String] }) и, более того, x.T) слишком грубый https://typelevel.org/blog/2015/07/23/type-projection.html

Для result лучше написать зависимый от пути тип.

Но

val x = implicitly[TypeOr[Int, String]]
val result: Error[x.T, String] =
  Err(null.asInstanceOf[Error[Int, Int]]).combine(_ => null.asInstanceOf[Error[String, String]])

не компилируется.

Дело в том, что implicitly может повредить уточнения типа https://typelevel.org/blog/2014/01/18/implicitly_existential.html

Именно поэтому существует макрос shapeless.the.

val x = the[TypeOr[Int, String]]
val result: Error[x.T, String] = Err(null.asInstanceOf[Error[Int, Int]]).combine(_ => null.asInstanceOf[Error[String, String]])
val itsType: Error[(Int, String), String] = result

В качестве альтернативы можно определить пользовательский материализатор

object TypeOr {
  //...
  def apply[E, F](implicit typeOr: TypeOr[E, F]): Aux[E, F, typeOr.T] = typeOr
}

val x = TypeOr[Int, String]
val result: Error[x.T, String] =
  Err(null.asInstanceOf[Error[Int, Int]]).combine(_ => null.asInstanceOf[Error[String, String]])
val itsType: Error[(Int, String), String] = result

если вы определяете def a: Unit = result - это явно неправильный тип, как вы заставляете компилятор использовать расширенный тип, который он ожидает?

ryskajakub 10.04.2019 16:48

@coubeatczech Что вы подразумеваете под «развернуть тип»? На самом деле вы можете проверить, что def a: Unit = result компилируется, так что это правильный тип. В Scala значение любого типа можно преобразовать в тип Unit.

Dmytro Mitin 10.04.2019 16:56

Я этого не знал, спасибо. Допустим, это будет def a: Int = result. Под расширенным типом я подразумеваю, что type T = String показывает String, а не T - разрешение имени типа до тех пор, пока больше нечего разрешать. В моем примере #T относится к какому-то типу, и я хочу его разрешить.

ryskajakub 10.04.2019 18:34

@coubeatczech Попробуйте import scala.reflect.runtime.universe._def printType[A: TypeTag](a: A) = println(typeOf[A].dealias)printType(result)

Dmytro Mitin 10.04.2019 18:46

я получу: src/main/scala/returnerror/Error.scala:49: No TypeTag available for ev.T

ryskajakub 10.04.2019 19:28

Я постараюсь добавить полный пример к вопросу

ryskajakub 10.04.2019 19:29

@coubeatczech Это означает, что T - это не псевдоним типа, это просто абстрактный тип.

Dmytro Mitin 10.04.2019 19:30

Нравится trait TypeOr[A, B] { type T }.

Dmytro Mitin 10.04.2019 19:31

Другие вопросы по теме