编译器无法解析扩展泛型类型的 Scala 类型

问题描述 投票:0回答:1

考虑抽象类 Fruit:

trait Fruit
case object Apple extends Fruit
case object Orange extends Fruit

我想为具有通用输入和输出的水果实现处理器服务:

trait FruitProcessorInput[T <: Fruit]
case class AppleProcessorInput(parm: List[Int]) extends FruitProcessorInput[Apple.type]
case class OrangeProcessorInput(parm: List[Int]) extends FruitProcessorInput[Orange.type]

trait FruitProcessorOutput[T <: Fruit]
case class AppleProcessorOutput(parm: List[Int]) extends FruitProcessorOutput[Apple.type]
case class OrangeProcessorOutput(parm: List[Int]) extends FruitProcessorOutput[Orange.type]

trait FruitProcessorService[T <: Fruit] {
  def processFruit(input: FruitProcessorInput[T]): FruitProcessorOutput[T]
}

问题是我无法具体实现以下两个作品:

// THIS COMPILES:
case class AppleProcessorService() extends FruitProcessorService[Apple.type] {
  override def processFruit(input: FruitProcessorInput[Apple.type]): FruitProcessorOutput[Apple.type] = ???
}

// THIS COMPILES:
case class AppleProcessorService() extends FruitProcessorService[Apple.type] {
  override def processFruit(input: FruitProcessorInput[Apple.type]): AppleProcessorOutput = ???
}

但以下情况则不然:

// THIS DOES NOT COMPILE:
case class AppleProcessorService() extends FruitProcessorService[Apple.type] {
  override def processFruit(input: AppleProcessorInput): AppleProcessorOutput = ???
}

主要是当我使用扩展类型

AppleProcessorInput
作为参数时,即使它扩展了
FruitProcessorInput[T]
,scala也不将其视为类型
FruitProcessorInput[Apple.type]
,而当它用于返回时,它接受
FruitProcessorOutput[Apple.type]
的AppleProcessorOutput。为什么它在返回类型时有效,但在参数类型时无效

我不确定 scala 是否有充分的理由不允许这样做,或者是否有解决方法。

我有这个结构的原因是有一个我映射的通用 http4s api

val routes: HttpRoutes[IO] = Router[IO](
  "/apples"         -> service(appleService),
  "/oranges"        -> service(orangeService)
)

服务是什么样的

def service[T <: Fruit](
    fruitService: FruitProcessorService[T]
)(implicit decoder: EntityDecoder[IO, FruitProcessorInput[T]]): HttpRoutes[IO] =
  HttpRoutes.of[IO] {
    case req @ GET -> Root                   =>
      for {
        input    <- req.attemptAs[FruitProcessorInput[T]].fold(err => IO.raiseError(err), out => IO(out)).flatten
        output   <- fruitService.processFruit(input)
        response <- Ok(output.asJson)
      } yield response
  }
scala type-systems cats-effect
1个回答
0
投票

问题是

FruitProcessorService[Apple]
必须接受任何
FruitProcessorInput[Apple]
,否则会破坏Liskov。

但是,我们可以进一步调整界面以模拟您想要的内容。
请尝试进行以下更改,并让我知道它是否有效。

// Further refine FruitProcessorService:
trait FruitProcessorService[T <: Fruit] {
  type Input <: FruitProcessorInput[T]
  type Output <: FruitProcessorOutput[T]
  
  def processFruit(input: Input): Output
}

// Adapt service to the new interface.
def service[T <: Fruit](
  fruitService: FruitProcessorService[T]
)(implicit
  decoder: EntityDecoder[IO, fruitService.Input]
  encoder: EntityEncoder[IO, fruitService.Ouput]
): HttpRoutes[IO] =
  HttpRoutes.of[IO] {
    case req @ GET -> Root                   =>
      req.as[fruitService.Input].flatMap { input =>
        Ok(fruitService.processFruit(input))
      }
  }
© www.soinside.com 2019 - 2024. All rights reserved.