Scala如何在trait上派生类型类

q43xntqr  于 2023-02-12  发布在  Scala
关注(0)|答案(2)|浏览(169)

在下面的例子中,我希望能够使用一个隐式类型类- Process -将trait作为输入。但是编译器不能识别这种情况下的任何隐式。我期望Input是一个密封trait,并且我为InputA和InputB都做了隐式实现,在运行时它会根据提供的类型自行选择。

sealed trait Input
  final case class InputA(i: Int) extends Input
  final case class InputB(s: String) extends Input

  trait Process[U] {
    def give(u :U): U
  }

  object Process {
    implicit val processInputA: Process[InputA] = (u: InputA) => u.copy(i = u.i+10)
    implicit val processInputB: Process[InputB] = (u: InputB) => u.copy(s = u.s+"add")
  }
  
  object UseProcess {
    def run[U](u: U)(implicit process: Process[U]): U = process.give(u)
  }
  val g: Input = InputB("1")
  val res3: Input = UseProcess.run(g). ==>> No implicits found for parameter process: Process[Input]

有没有办法让它工作或者类型类只在具体类型上实现的情况下工作。
提前感谢您的回答

owfi6suc

owfi6suc1#

这不是特质与阶级的问题。
您正在调用的方法如下所示

run(x: Input)(implicit y: Process[Input]

为了调用,编译器需要在作用域中的某个地方找到Process[Input]类型的隐式示例,但实际上并没有。
注意,Process[InputB]不是Process[Input]的子类,因为Process的参数是不变的,你可以试着让它像trait Process[+U]一样协变......但在这种情况下不起作用,因为函数参数是逆变的(如果U是协变的,就不能有give(u: U))。
如果你能让你的特质协变,它仍然不会起作用,因为现在 * processInputAprocessInputB都适合,所以解决方案将是模糊的。
基本上,在g: Input上转换到Input是个坏主意,如果你想写一个函数来使用trait进行抽象,你应该参数化它:

def foo[T <: Input : Process](g: T) = UseProcess.run(g)

这应该行得通。

8e2ybdfx

8e2ybdfx2#

我期待Input是一个密封的特征,我为InputAInputB做了隐式实现,在运行时它根据提供的类型自行选择。
隐式(类型类的示例)在编译时(而不是运行时)、类型检查(编译phasetyper)期间解决。
Runtime vs. Compile time
val g: Input = InputB("1")中,只有在运行时才知道gInputB而不是InputA,在编译时才知道gInput
因此,要么让编译器在编译时知道gInputB

val g: InputB = InputB("1")

或为Input定义type类的示例

implicit val processInput: Process[Input] = {
  case u: InputA => implicitly[Process[InputA]].give(u)
  case u: InputB => implicitly[Process[InputB]].give(u)
}

正如你所看到的,如果你的逻辑是基于一个运行时值的,你需要模式匹配(大多发生在运行时)而不是隐式(类型类)解析,隐式(类型类)是一种编译时的“模式匹配”。
您还可以基于示例Process[InputA]Process[InputB] * 派生 * 示例Process[Input],而不是为每个sealed-trait层次结构手动定义它。
Use the lowest subtype in a typeclass?

// libraryDependencies += "com.chuusai" %% "shapeless" % "2.3.10"
import shapeless.{:+:, CNil, Coproduct, Inl, Inr, Generic}

implicit def genericProcess[A, C <: Coproduct](implicit
  generic: Generic.Aux[A, C],
  process: Process[C]
): Process[A] = a => generic.from(process.give(generic.to(a)))

implicit def cNilProcess: Process[CNil] = _.impossible

implicit def cConsProcess[H, T <: Coproduct](implicit
  hProcess: Process[H],
  tProcess: Process[T]
): Process[H :+: T] =
  _.eliminate(h => Inl(hProcess.give(h)), t => Inr(tProcess.give(t)))

相关问题