Scala 中返回类型取决于输入类型的通用函数?

2023-12-13

我正在尝试编译此代码:

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))
}

当我尝试编译此代码时,出现错误:

类型不匹配;
发现:正方形
必需:S
case s: Square => IO(s.copy(y = 5))

如何使这段代码工作?

Update:
阅读评论和文章后,我尝试像这样使用 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))
}

但似乎我错过了一些东西。这还是不行。


Now modifyShape's body

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必须为any 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 绑定)

(see details in @MatthiasBerndt's answer and my comments to it, this code portion is from his answer)

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()
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Scala 中返回类型取决于输入类型的通用函数? 的相关文章

随机推荐

  • 除了 COM 之外,还有更好的方法来远程控制 Excel 吗?

    我正在开发一个回归测试工具 该工具将验证大量的 Excel 电子表格 目前 我使用最新版本的 pywin32 产品通过 Python 脚本通过 COM 控制它们 不幸的是 COM 似乎有许多恼人的缺点 例如 最轻微的干扰似乎就能中断与 CO
  • 创建用于导航的 ViewModel

    我有一个带有多个视图的 MVC 4 应用程序 IE 产品 食谱 分销商和商店 每个视图都基于一个模型 让我们保持简单 假设我的所有控制器都传递一个类似的视图模型 看起来像我的 Product 操作 public ActionResult I
  • 从代码隐藏中将页面异步模式设置为 true

    是否可以在我的代码隐藏文件中设置页面指令的异步模式 我没有办法直接修改属性 并努力寻找一种在我的代码隐藏中实现此功能的方法 我尝试在我的 Page Load 方法中添加Page AsyncMode true 但它返回以下错误 由于其保护级别
  • 如何使用 # 作为 CoffeeScript hereregex 的一部分?

    我正在尝试匹配 jQuery Mobile URL 的哈希片段 如下所示 matches window location hash match we re interested in the hash fragment the path t
  • Python:Flask 的模拟补丁错误

    在编写 Python 方面 我完全是个新手 更不用说测试它了 这是我的 Flask 端点 blueprint route mailing finish
  • 如何更换|| (两个管道)来自带有 | 的字符串(一)管道

    我收到此标签内一些 json 格式图像的响应 xmlImageIds 57948916 57948917 57948918 57948919 57948920 57948921 57948 922 57948923 57948924 579
  • 复制构造函数需要调用依赖于对象的方法,但构造函数不能是虚拟的

    我有一个带有两个继承类的抽象基类 在这两个类中 我定义了一个由构造函数使用的虚拟方法 现在我需要创建一个复制构造函数 但我不能将复制构造函数声明为虚拟 但我希望其中的方法调用依赖于作为参数提供的对象的类型 我该如何解决这个问题 现在我使用基
  • 当有受保护的工作表时如何保持宏运行?

    我用密码保护了工作表 4 因为工作表 4 中的某些单元格不允许用户输入 密码是 1234 但是 我想运行我的宏 如果出现错误 单元格将自动突出显示 我的宏未运行并出错 因为我要突出显示的单元格位于受保护的工作表中 当我单击验证按钮时 如何使
  • 你能指定 std::getline 中什么不是分隔符吗?

    我希望它将任何非字母字符视为分隔符 我怎样才能做到这一点 你不能 默认分隔符是 n while std getline std cin str n is implicit 对于其他分隔符 请传递它们 while std getline st
  • mongoDB 聚合:根据数组名称求和

    我有一场比赛的以下数据 date 20140101 duration 23232 win player Player1 score 2344324 player Player4 score 23132 loss player Player2
  • 如何处理大多数十进制小数无法准确表示为二进制的事实?

    所以 我们知道像 0 1 这样的分数无法用二进制精确表示 这会导致精确问题 例如这里提到的 在 C 中格式化双精度数以进行输出 我们知道我们有十进制类型来表示数字 但问题是 很多数学方法不支持十进制类型 所以我们将它们转换为双精度 这再次破
  • 为什么 R 中的 apply() 方法比 for 循环慢?

    作为最佳实践 我试图确定创建一个函数是否更好apply 它穿过一个矩阵 或者是否最好简单地通过该函数循环一个矩阵 我尝试了两种方法并惊讶地发现apply 速度较慢 任务是获取一个向量并将其评估为正或负 然后返回一个向量 如果为正则返回 1
  • 使用 React 将提交的表单值保存在 JSON 文件中

    我正在尝试创建一个 React 表单 它将在提交时使用表单中的值更新 JSON 文件 最终结果是 每次提交表单时 这都会在 JSON 文件中创建一个数据数组 然后可以使用该数组填充应用程序中其他地方提交的结果的 列表 表单本身工作正常 但每
  • 如何在android中安排一些代码执行或者:android中的守护线程到底是什么?

    我目前正在开发一个适用于 Android 操作系统的应用程序 它需要不时从远程服务器获取数据 由于即使实际的前端应用程序未运行 也应该执行此 更新 因此我实现了一个在系统启动时启动的远程服务 现在我需要安排一个计时器来开始更新 Timer
  • django 在 modelform 中使用模型选择

    我想知道应该如何在模型表单中使用模型选择选项 示例 型号 class NPCGuild models Model CATEGORIES COM Combat CRA Crafting WAR Warfare faction models F
  • ZLib解压

    我正在尝试使用 zlib net 库压缩数据 无论未压缩字符串的内容如何 我似乎只在 raw 中获得两个字节的数据 string uncompressed 1234567890 byte data UTF8Encoding Default
  • 如何将android芯片对齐到芯片组的末尾?

    您好 我有一个芯片组 我动态创建芯片并添加到芯片组 However 我希望芯片位于芯片组的右端 但它始终位于左侧 如此处所示 我希望芯片 Text 0 和 Text 1 位于最右端 这是我的代码
  • 端口号未隐藏在 nginx 反向代理(下一个 js 服务器)中

    我正在尝试通过 create next app 部署 next js 应用程序 我有一个像这样的自定义 Express 服务器 const express require express const next require next co
  • 基本身高百分比

    我不明白身高百分比是如何运作的 为什么下面的片段没有填充颜色 body height 100 div1 height 20 background color red div2 height 80 background color blue
  • Scala 中返回类型取决于输入类型的通用函数?

    我正在尝试编译此代码 import cats effect IO sealed trait Shape val x Int case class Square x Int y Int extends Shape case class Cub