返回类型取决于Scala中的输入类型的通用函数? [英] Generic function where the return type depends on the input type in Scala?

查看:44
本文介绍了返回类型取决于Scala中的输入类型的通用函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试编译此代码:

导入cats.effect.IO密封性状形状{val x: 整数}case class Square(x: Int, y: Int) extends Shapecase class Cube(x: Int, y: Int, z: Int) extends Shapedef modifyShape[S <: Shape](shape: S): IO[S] = 形状匹配{情况 s:Square =>IO(s.copy(y = 5))情况c:立方体=>IO(c.copy(z = 5))}

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

<块引用>

类型不匹配;
找到:方形
要求:S
情况 s:Square =>IO(s.copy(y = 5))

如何使此代码工作?

更新:
阅读评论和文章后,我尝试像这样使用 F-bound:

sealed trait Shape[A <: Shape[A]] { this: A =>val x: 整数}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] = 形状匹配{情况 s:Square =>IO(s.copy(y = 5))情况c:立方体=>IO(c.copy(z = 5))}

但是我好像错过了什么.这仍然不起作用.

解决方案

现在 modifyShape's body

形状匹配{情况 s:Square =>IO(s.copy(y = 5))情况c:立方体=>IO(c.copy(z = 5))}

只是不满足其签名

def modifyShape[S <: Shape](shape: S): IO[S]

在此处查看详细信息:

如果将 A 的泛型子类型声明为返回参数,为什么我不能返回 A 的具体子类型?

模式匹配中使用的抽象类型的类型不匹配

foo[S <: Shape] 意味着 foo 必须适用于 any SShape 的子类型.假设我使用 S := Shape with SomeTrait,你不会返回 IO[Shape with SomeTrait].

尝试使用 F 有界类型参数的 GADT

密封特性Shape[S <: Shape[S]] { this: S =>val x: 整数def modifyShape: IO[S]}case class Square(x: Int, y: Int) extends Shape[Square] {覆盖 def modifyShape: IO[Square] = IO(this.copy(y = 5))}case class Cube(x: Int, y: Int, z: Int) extends Shape[Cube] {覆盖 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

密封特征形状{ self =>val x: 整数type S >: self.type <: Shape { type S = self.S }def modifyShape: IO[S]}case class Square(x: Int, y: Int) extends Shape {覆盖类型 S = 方形覆盖 def modifyShape: IO[Square] = IO(this.copy(y = 5))}case class Cube(x: Int, y: Int, z: Int) extends Shape {覆盖类型 S = Cube覆盖 def modifyShape: IO[Cube] = IO(this.copy(z = 5))}def modifyShape[_S <: Shape { type S = _S}](shape: _S): IO[_S] = shape.modifyShape//或者//def modifyShape(shape: Shape): IO[shape.S] = shape.modifyShape

或 GADT(无 F-bound)

(请参阅 @MatthiasBerndtanswer 中的详细信息以及我对它,这个代码部分来自他的回答)

<块引用>

密封性状Shape[A] {val x: 整数}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] = 形状匹配{情况 s:Square =>IO(s.copy(y = 5))情况c:立方体=>IO(c.copy(z = 5))}

或 ADT + 反射

密封特征形状{val x: 整数}case class Square(x: Int, y: Int) extends Shapecase class Cube(x: Int, y: Int, z: Int) extends Shape导入 scala.reflect.runtime.universe._def modifyShape[S <: Shape : TypeTag](shape: S): IO[S] = (shape match {case s:如果 typeOf[S] <:IO(s.copy(y = 5))case c: Cube if typeOf[S] <:IO(c.copy(z = 5))}).asInstanceOf[IO[S]]

或 ADT + 类型类

密封特征形状{val x: 整数}case class Square(x: Int, y: Int) extends Shapecase class Cube(x: Int, y: Int, z: Int) extends Shape特性 ModifyShape[S <: 形状] {def modifyShape(s: S): IO[S]}对象修改形状{隐式 val squareModifyShape:ModifyShape[Square] = s =>IO(s.copy(y = 5))隐式 val cubeModifyShape: ModifyShape[Cube] = c =>IO(c.copy(z = 5))}def modifyShape[S <: Shape](shape: S)(implicit ms: ModifyShape[S]): IO[S] =ms.modifyShape(形状)

或 ADT + 磁铁

密封特征形状{val x: 整数}case class Square(x: Int, y: Int) extends Shapecase class Cube(x: Int, y: Int, z: Int) extends Shape导入 scala.language.implicitConversions特质修改形状{输入def modifyShape(): 出}对象修改形状{隐式 def fromSquare(s: Square): ModifyShape { type Out = IO[Square] } = new ModifyShape {覆盖类型 Out = IO[Square]覆盖 def modifyShape(): IO[Square] = IO(s.copy(y = 5))}隐式 def fromCube(c: Cube): ModifyShape { type Out = IO[Cube] } = new ModifyShape {覆盖类型 Out = IO[Cube]覆盖 def modifyShape(): IO[Cube] = IO(c.copy(z = 5))}}def modifyShape(shape: ModifyShape): shape.Out = shape.modifyShape()

I'm trying make this code to compile:

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

When I'm trying to compile this code I'm getting error:

type mismatch;
found : Square
required: S
case s: Square => IO(s.copy(y = 5))

How to make this code work?

Update:
After reading comments and articles I tried to use F-bound like this:

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

But seems I missed something. This still doesn't work.

解决方案

Now modifyShape's body

shape match {
  case s: Square => IO(s.copy(y = 5))
  case c: Cube => IO(c.copy(z = 5))
}

just doesn't satisfy its signature

def modifyShape[S <: Shape](shape: S): IO[S] 

See details here:

Why can't I return a concrete subtype of A if a generic subtype of A is declared as return parameter?

Type mismatch on abstract type used in pattern matching

foo[S <: Shape] means that foo has to work for any S that is a subtype of Shape. Suppose I take S := Shape with SomeTrait, you don't return IO[Shape with SomeTrait].

Try GADT with F-bounded type parameter

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 reminded the link)

or GADT with F-bounded type member

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

or GADT (without F-bound)

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

or ADT + reflection

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]]

or ADT + type class

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)

or ADT + magnet

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

这篇关于返回类型取决于Scala中的输入类型的通用函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆