Параметр типа универсального метода Scala 3 не работает

Работает:

trait Transformer[A, B] {
  def transform(a: A): B
}

class TransformerImpl extends Transformer[Int, String] {
  override def transform(value: Int): String = {
    value.toString
  }
}

Это то, что я хочу, потому что черта с одним параметром проще и потому что конкретное значение B определяется A. Однако это не работает.

trait Transformer2[A] {
  def transform[B](a: A): B
}

class Transformer2Impl extends Transformer2[Int] {
  override def transform[String](value: Int): String = {
    value.toString
  }
}

Ошибка:

-- [E007] Type Mismatch Error: --------
3 |    value.toString
  |    ^^^^^^^^^^^^^^
  |    Found:    String
  |    Required: String

Это тоже с другой сигнатурой метода не работает:

class Transformer2IntToIntImpl extends Transformer2[Int] {
  override def transform(value: Int): Int = {
    value
  }
}

Ошибка:

-- Error: ----------------------------------------------------------------------
1 |class Transformer2IntToIntImpl extends Transformer2[Int] {
  |      ^
  |class Transformer2IntToIntImpl needs to be abstract, since def transform[B](a: A): B in trait Transformer2 is not defined 
  |(Note that
  | parameter A in def transform[B](a: A): B in trait Transformer2 does not match
  | parameter Int in override def transform(value: Int): Int in class Transformer2IntToIntImpl
  | )
-- [E038] Declaration Error: ---------------------------------------------------
2 |  override def transform(value: Int): Int = {
  |               ^
  |method transform has a different signature than the overridden declaration
  |-----------------------------------------------------------------------------

2 errors found

Судя по ошибкам, я попробовал несколько альтернатив, но безуспешно.

Как правило, любая функция с сигнатурой f[A](...): A, не имеющая A на входе, должна выбрасывать/расходиться. В частности, def transform[Str](value: Int): Str должен бросить/разойтись.

Andrey Tyukin 29.04.2024 00:44
Стоит ли изучать 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
1
51
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

Это сработало для меня:

trait Transformer2[A] {
  type B
  def transform(a: A): B
}

class Transformer2Impl extends Transformer2[Int] {
  type B = String
  override def transform(value: Int): String = {
    value.toString
  }
}

class Transformer2IntToIntImpl extends Transformer2[Int] {
  type B = Int
  override def transform(value: Int): Int = {
    value
  }
}

val ans1 = new Transformer2Impl().transform(5)
// val ans1: String = 5

val ans2 = new Transformer2IntToIntImpl().transform(5)
// val ans2: Int = 5

Мне все равно хотелось бы знать, произошел ли сбой в исходном коде (хотя и с запутанными сообщениями об ошибках), потому что он синтаксически неверен.

Это не синтаксически неверно, это семантически неверно.

Levi Ramsey 29.04.2024 00:53

Определения

// (1)
trait Transformer[A, B] {
  def transform(a: A): B
}

или

// (2)
trait Transformer2[A] {
  type B
  def transform(a: A): B
}

означает, что реализация (для фиксированного A, B) Transformer знает, как преобразовать это фиксированное A в это фиксированное B.

О (1) и (2) см.

Scala: абстрактные типы против дженериков ( ответ)

Абстрактные типы и параметры типа

Это определение

// (3)
trait Transformer2[A] {
  def transform[B](a: A): B
}

означает нечто иное. А именно, его реализация (для фиксированного A) умеет преобразовать этот фиксированный A в произвольный B.

потому что конкретное значение B определяется A

Это (2), а не (3).

Здесь

// (4)
class Transformer2Impl extends Transformer2[Int] {
  override def transform[String](value: Int): String = {
    value.toString
  }
}

String в [String] не является стандартным java.lang.String (он же scala.Predef.String). Вы не указываете B. На самом деле вы определяете новый параметр типа, обозначаемый String, и этот новый тип затеняет стандарт String. Не имеет значения, как обозначить параметр типа. Итак, (4) точно такой же, как

// (5)
class Transformer2Impl extends Transformer2[Int] {
  override def transform[B](value: Int): B = {
    value.toString
  }
}

Вот почему ошибка компиляции.

Лучше включить scalacOptions += "-Xlint:type-parameter-shadow", тогда в такой ситуации вы получите предупреждение.

Здесь

// (6)
class Transformer2IntToIntImpl extends Transformer2[Int] {
  override def transform(value: Int): Int = {
    value
  }
}

вы явно изменили подпись, поэтому ошибка компиляции.

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