加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

scala – 如何定义输出类型取决于输入类型的函数

发布时间:2020-12-16 09:33:04 所属栏目:安全 来源:网络整理
导读:给出以下类: case class AddRequest(x: Int,y: Int)case class AddResponse(sum: Int)case class ToUppercaseRequest(str: String)case class ToUppercaseResponse(upper: String) 如何以类型安全的方式定义一些功能: def process(req: ???): ??? 使以下内
给出以下类:

case class AddRequest(x: Int,y: Int)
case class AddResponse(sum: Int)
case class ToUppercaseRequest(str: String)
case class ToUppercaseResponse(upper: String)

如何以类型安全的方式定义一些功能:

def process(req: ???): ???

使以下内容成立:

val r1: AddResponse = process(AddRequest(2,3))
val r2: ToUppercaseResponse = process(ToUppercaseRequest("aaa"))

另外,以下不能编译:

val r3 = process("somestring")

解决方法

这在Scala中完全是可能的,也是完全合理的。例如,这种事情全都是Shapeless,而且类似的东西(但不太原则的)是在Spray等显示的磁体图案的基础。

更新:请注意,以下解决方案假设“给定以下类”意味着您不想自己触摸案例类。如果您不在乎,请参阅下面的答案的第二部分。

您需要一个将输入类型映射到输出类型的类型类型:

case class AddRequest(x: Int,y: Int)
case class AddResponse(sum: Int)
case class ToUppercaseRequest(str: String)
case class ToUppercaseResponse(upper: String)

trait Processable[In] {
  type Out
  def apply(in: In): Out
}

然后一些类型类实例:

object Processable {
  type Aux[I,O] = Processable[I] { type Out = O }

  implicit val toUppercase: Aux[ToUppercaseRequest,ToUppercaseResponse] =
    new Processable[ToUppercaseRequest] {
      type Out = ToUppercaseResponse
      def apply(in: ToUppercaseRequest): ToUppercaseResponse =
        ToUppercaseResponse(in.str.toUpperCase)
    }

  implicit val add: Aux[AddRequest,AddResponse] =
    new Processable[AddRequest] {
      type Out = AddResponse
      def apply(in: AddRequest): AddResponse = AddResponse(in.x + in.y)
    }
}

现在您可以使用此类型类来定义进程:

def process[I](in: I)(implicit p: Processable[I]): p.Out = p(in)

其工作原理(注意适当的静态类型):

scala> val res: ToUppercaseResponse = process(ToUppercaseRequest("foo"))
res: ToUppercaseResponse = ToUppercaseResponse(FOO)

scala> val res: AddResponse = process(AddRequest(0,1))
res: AddResponse = AddResponse(1)

但它不适用于任意类型:

scala> process("whatever")
<console>:14: error: could not find implicit value for parameter p: Processable[String]
       process("whatever")
              ^

您甚至不需要使用路径依赖类型(您应该只能在类型类上具有两个类型参数),但是如果使用过程更好你必须明确地提供类型参数。

更新:以上所有内容假定您不想更改您的案例类签名(这绝对不是必需的)。但是,如果你愿意改变它们,那么你可以更简洁一点地做这个:

trait Input[Out] {
  def computed: Out
}

case class AddRequest(x: Int,y: Int) extends Input[AddResponse] {
  def computed: AddResponse = AddResponse(x + y)
}
case class AddResponse(sum: Int)

case class ToUppercaseRequest(str: String) extends Input[ToUppercaseResponse] {
  def computed: ToUppercaseResponse = ToUppercaseResponse(str.toUpperCase)
}
case class ToUppercaseResponse(upper: String)

def process[O](in: Input[O]): O = in.computed

接着:

scala> process(AddRequest(0,1))
res9: AddResponse = AddResponse(1)

scala> process(ToUppercaseRequest("foo"))
res10: ToUppercaseResponse = ToUppercaseResponse(FOO)

您应该选择哪种多态(参数或特设)完全取决于您。如果您想要描述任意类型之间的映射,请使用类型类。如果您不在乎,或者主动不希望此操作可用于任意类型,请使用子类型。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读