Как преодолеть проекцию недостающего типа в Scala 3?

В Scala 2 я использовал проекцию типа в простой модели ORM, которая содержит IdEntity и несколько репозиториев. Теперь я попытался перейти на Scala 3, но застрял, поскольку проекция типа с # отсутствует.

Упрощенный код выглядит так:

trait IdEntity:
  type ID_Type
  val id: ID_Type

trait IdLongEntity extends IdEntity:
  override type ID_Type = Long

trait IdStringEntity extends IdEntity:
  override type ID_Type = String

trait IdRepository[E <: IdEnity]:
  def findById(id: E#ID_Type): Option[E]:

Есть идеи, как преодолеть проекцию отсутствующего типа в def findById(id: E#ID_Type)?

Я уже пробовал использовать совпадение типов, например

object IdEntity:
  type Aux[K] = IdEntity {type ID_Type = K}

type IdType[T <: IdEntity] = T match
    case IdEntity.Aux[k] => k

но это не работает, так как компиляция считает, что типы не совпадают.

Обновлять:

Благодаря ответу я попробовал классы типов, а также тип соответствия, но оба они терпят неудачу, как только я попробую следующее (https://scastie.scala-lang.org/UtW9u0mbS5ydiZmQNNWV6g):

trait IdEntity:
  type ID_Type
  val idDefault: ID_Type
  val id: Option[ID_Type]
  def idOrDefault: ID_Type = id.getOrElse(idDefault)

trait IdRepository[E <: IdEntity](using val e: E):
  type ID_Type = e.ID_Type  
  def delete(id: ID_Type): Either[String, Int] = deleteImpl(id)
  def delete(entity: E): Either[String, Int] = delete(entity.idOrDefault) // here the type error occures
  protected def deleteImpl(id: ID_Type): Either[String, Int]

Дело в том, что E#ID_Type по сути Any

Luis Miguel Mejía Suárez 23.05.2024 19:13
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
101
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий
trait IdEntity:
  type ID_Type
  val id: ID_Type
object IdEntity:
  type Aux[K] = IdEntity {type ID_Type = K}

trait IdLongEntity extends IdEntity:
  override type ID_Type = Long

trait IdStringEntity extends IdEntity:
  override type ID_Type = String

// match type
type IdType[T <: IdEntity] = T match
  case IdEntity.Aux[k] => k

trait IdRepository[E <: IdEntity]:
  def findById(id: IdType[E]): Option[E] = ???

https://scastie.scala-lang.org/QoV6Z7xRTOa6hVhAYOb0uw

это не работает, так как компиляция считает, что типы не совпадают

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

trait IdEntity:
  type ID_Type
  val id: ID_Type
object IdEntity:
  type Aux[K] = IdEntity {type ID_Type = K}

trait IdLongEntity extends IdEntity:
  override type ID_Type = Long

trait IdStringEntity extends IdEntity:
  override type ID_Type = String

// type class
trait IdType[T <: IdEntity]:
  type Out
object IdType:
  given [K, T <: IdEntity.Aux[K]]: IdType[T] with
    type Out = K

trait IdRepository[E <: IdEntity]:
  def findById(using idType: IdType[E])(id: idType.Out): Option[E] = ???

https://scastie.scala-lang.org/166gIUf3Rg2DwSgLlheXvQ

Или вы даже можете попробовать использовать классы типов, начиная с IdEntity, например, создав IdEntity класс типа:

trait IdEntity:
  type ID_Type
  val id: ID_Type

trait IdLongEntity extends IdEntity:
  override type ID_Type = Long

trait IdStringEntity extends IdEntity:
  override type ID_Type = String

trait IdRepository[E <: IdEntity](using val e: E):
  def findById(id: e.ID_Type): Option[E]

https://scastie.scala-lang.org/ap4GJY9NQyOyt2eHwobXSg


Как в Scala 3 заменить проекцию общего типа, которая была удалена?

Чем Дотти предлагает заменить проекции шрифтов?

E не является допустимым путем, поскольку не является конкретным типом (Scala 3)

https://docs.scala-lang.org/scala3/reference/dropped-features/type-projection.html


Перевод

// scala 2.13, type projections

trait IdEntity {
  type ID_Type
  val idDefault: ID_Type
  val id: Option[ID_Type]
  def idOrDefault: ID_Type = id.getOrElse(idDefault)
}

trait IdRepository[E <: IdEntity] {
  type ID_Type = E#ID_Type
  def delete(id: ID_Type): Either[String, Int] = deleteImpl(id)
  def delete(entity: E): Either[String, Int] = delete(entity.idOrDefault)
  protected def deleteImpl(id: ID_Type): Either[String, Int]
}

возможно

// scala 3, match types

trait IdEntity:
  type ID_Type
  val idDefault: ID_Type
  val id: Option[ID_Type]
  def idOrDefault: ID_Type = id.getOrElse(idDefault)
object IdEntity:
  type Aux[K] = IdEntity {type ID_Type = K}

type IdType[T <: IdEntity] = T match
  case IdEntity.Aux[k] => k

// unfortunately, E <: IdEntity.Aux[IdType[E]] doesn't work (match types are not lazy enough): Recursion limit exceeded. Maybe there is an illegal cyclic reference?
trait IdRepository[E <: IdEntity]:
  type ID_Type = IdType[E]
  def delete(id: ID_Type): Either[String, Int] = deleteImpl(id)
  def delete(entity: E {type ID_Type = IdType[E]}): Either[String, Int] = delete(entity.idOrDefault)
  protected def deleteImpl(id: ID_Type): Either[String, Int]

или

// scala 3, type classes (path-dependent types)

trait IdEntity:
  type ID_Type
  val idDefault: ID_Type
  val id: Option[ID_Type]
  def idOrDefault: ID_Type = id.getOrElse(idDefault)

trait IdRepository[E <: IdEntity](using val e: E):
  type ID_Type = e.ID_Type
  def delete(id: ID_Type): Either[String, Int] = deleteImpl(id)
  def delete(entity: E {type ID_Type = e.ID_Type}): Either[String, Int] = delete(entity.idOrDefault)
  protected def deleteImpl(id: ID_Type): Either[String, Int]

Спасибо, я попробовал использовать тип соответствия, но как только у меня появился метод def delete(e: E): Unit = delete(e.id), я получил ошибку типа: e.id тип e.ID_Type не соответствует IdType[E]. То же самое с классом типа.

MiNoS 24.05.2024 10:21

Вот более подробный фрагмент кода, включающий ошибку scastie.scala-lang.org/UtW9u0mbS5ydiZmQNNWV6g

MiNoS 24.05.2024 11:03

Спасибо большое, обе попытки работают. Один лучше другого?

MiNoS 24.05.2024 20:53

@MiNoS Нет, оба работают. Проекции типов и классы типов в Scala 2, сопоставление типов и классов типов в Scala 3, семейства типов и классы типов в Haskell. Просто разные подходы к программированию на уровне типа. Оба имеют угловые корпуса. Типы совпадений могут быть сложными на уровне значений, типы, зависящие от пути, могут быть сложными.

Dmytro Mitin 25.05.2024 11:20

Я почти закончил, но застрял в случае, когда мне нужно «привести» экземпляр E к экземпляру E {type ID_Type = IdType[E]}, возможно ли это? Я попробовал TypeTest, но не смог заставить его работать.

MiNoS 29.05.2024 11:30

@MiNoS Пожалуйста, подготовьте воспроизведение (возможно, начните новый вопрос). На уровне значения существует зависимая типизация сопутствующие типы соответствия на уровне типа.

Dmytro Mitin 29.05.2024 11:37

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