scala - 返回类型取决于Scala中的输入类型的通用函数?
问题描述
我正在尝试编译此代码:
import cats.effect.IO
sealed trait Shape {
val x: Int
}
case class Square(x: Int, y: Int) extends Shape
case class Cube(x: Int, y: Int, z: Int) extends Shape
def modifyShape[S <: Shape](shape: S): IO[S] = shape match {
case s: Square => IO(s.copy(y = 5))
case c: Cube => IO(c.copy(z = 5))
}
当我尝试编译此代码时,出现错误:
类型不匹配;
发现 : Square
required: S
case s: Square => IO(s.copy(y = 5))
如何使这段代码工作?
更新:
阅读评论和文章后,我尝试像这样使用 F-bound:
sealed trait Shape[A <: Shape[A]] { this: A =>
val x: Int
}
case class Square(x: Int, y: Int) extends Shape[Square]
case class Cube(x: Int, y: Int, z: Int) extends Shape[Cube]
def modifyShape[S <: Shape[S]](shape: S): IO[S] = shape match {
case s: Square => IO(s.copy(y = 5))
case c: Cube => IO(c.copy(z = 5))
}
但似乎我错过了什么。这仍然行不通。
解决方案
现在modifyShape
的身体
shape match {
case s: Square => IO(s.copy(y = 5))
case c: Cube => IO(c.copy(z = 5))
}
只是不满足它的签名
def modifyShape[S <: Shape](shape: S): IO[S]
在此处查看详细信息:
如果 A 的泛型子类型被声明为返回参数,为什么我不能返回 A 的具体子类型?
foo[S <: Shape]
意味着它必须foo
适用于任何 . 假设我带走,你不回来。S
Shape
S := Shape with SomeTrait
IO[Shape with SomeTrait]
尝试使用 F 有界类型参数的 GADT
sealed trait Shape[S <: Shape[S]] { this: S =>
val x: Int
def modifyShape: IO[S]
}
case class Square(x: Int, y: Int) extends Shape[Square] {
override def modifyShape: IO[Square] = IO(this.copy(y = 5))
}
case class Cube(x: Int, y: Int, z: Int) extends Shape[Cube] {
override def modifyShape: IO[Cube] = IO(this.copy(z = 5))
}
def modifyShape[S <: Shape[S]](shape: S): IO[S] = shape.modifyShape
https://tpolecat.github.io/2015/04/29/f-bounds.html(@LuisMiguelMejíaSuárez提醒链接)
或具有 F 有界类型成员的 GADT
sealed trait Shape { self =>
val x: Int
type S >: self.type <: Shape { type S = self.S }
def modifyShape: IO[S]
}
case class Square(x: Int, y: Int) extends Shape {
override type S = Square
override def modifyShape: IO[Square] = IO(this.copy(y = 5))
}
case class Cube(x: Int, y: Int, z: Int) extends Shape {
override type S = Cube
override def modifyShape: IO[Cube] = IO(this.copy(z = 5))
}
def modifyShape[_S <: Shape { type S = _S}](shape: _S): IO[_S] = shape.modifyShape
// or
// def modifyShape(shape: Shape): IO[shape.S] = shape.modifyShape
或 GADT(无 F-bound)
(请参阅@MatthiasBerndt的答案和我对它的评论中的详细信息,此代码部分来自他的答案)
sealed trait Shape[A] { val x: Int } case class Square(x: Int, y: Int) extends Shape[Square] case class Cube(x: Int, y: Int, z: Int) extends Shape[Cube] def modifyShape[S](shape: Shape[S]): IO[S] = shape match { case s: Square => IO(s.copy(y = 5)) case c: Cube => IO(c.copy(z = 5)) }
或 ADT + 反射
sealed trait Shape {
val x: Int
}
case class Square(x: Int, y: Int) extends Shape
case class Cube(x: Int, y: Int, z: Int) extends Shape
import scala.reflect.runtime.universe._
def modifyShape[S <: Shape : TypeTag](shape: S): IO[S] = (shape match {
case s: Square if typeOf[S] <:< typeOf[Square] => IO(s.copy(y = 5))
case c: Cube if typeOf[S] <:< typeOf[Cube] => IO(c.copy(z = 5))
}).asInstanceOf[IO[S]]
或 ADT + 类型类
sealed trait Shape {
val x: Int
}
case class Square(x: Int, y: Int) extends Shape
case class Cube(x: Int, y: Int, z: Int) extends Shape
trait ModifyShape[S <: Shape] {
def modifyShape(s: S): IO[S]
}
object ModifyShape {
implicit val squareModifyShape: ModifyShape[Square] = s => IO(s.copy(y = 5))
implicit val cubeModifyShape: ModifyShape[Cube] = c => IO(c.copy(z = 5))
}
def modifyShape[S <: Shape](shape: S)(implicit ms: ModifyShape[S]): IO[S] =
ms.modifyShape(shape)
或 ADT + 磁铁
sealed trait Shape {
val x: Int
}
case class Square(x: Int, y: Int) extends Shape
case class Cube(x: Int, y: Int, z: Int) extends Shape
import scala.language.implicitConversions
trait ModifyShape {
type Out
def modifyShape(): Out
}
object ModifyShape {
implicit def fromSquare(s: Square): ModifyShape { type Out = IO[Square] } = new ModifyShape {
override type Out = IO[Square]
override def modifyShape(): IO[Square] = IO(s.copy(y = 5))
}
implicit def fromCube(c: Cube): ModifyShape { type Out = IO[Cube] } = new ModifyShape {
override type Out = IO[Cube]
override def modifyShape(): IO[Cube] = IO(c.copy(z = 5))
}
}
def modifyShape(shape: ModifyShape): shape.Out = shape.modifyShape()
推荐阅读
- c# - 进程无法访问该文件,因为它正在被另一个进程在第二次调用时使用
- php - Symfony4 SwitchUserSubscriber 不区分两个用户
- node.js - Angular在没有列表的情况下在firestore中添加新文档
- laravel - 为什么相关的 Answer 模型没有得到 project_id?
- corda - 为 Corda 节点登录创建 Web 应用程序
- c# - 创建一个 Linq 到实体 IQueryable 扩展以进行日期分组 (GroupBy)
- angular - Angular - 多个错误拦截器
- javascript - 从函数中的函数创建新的函数对象 [JavaScript]
- sql - 为每行 SQL 查找 Min 、 2nd min 、 3rd min 等
- javascript - 如何使用 javaScript 函数为添加到我的数据表中的最新行调用 rowEditor 按钮?