关联两个类型的参数 [英] Correlate two type parameters

查看:79
本文介绍了关联两个类型的参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一系列操作,其中一些操作取决于先前操作的一些结果.像这样:

Suppose, I have a sequence of operations, some of which depend on some of the results of previous ones. Something like this:

 type Results = List[(Operation[_], Any)] // ???
 trait Operation[Out] { 
   type Result = Out
   def apply(results: Results): Out
 }

 class SomeOp extends Operation[String] {
    def apply(results: Results) = "foo"
 }

 class OtherOp extends Operation[String] {
    def apply(results: Results) = results
      .collectFirst { case (_: SomeOp, x: String) => x } 
      .getOrElse("") + "bar"
 }

 def applyAll(
  ops: List[Operation[_]], 
  results: Results = Nil
): Results = ops match {
  case Nil => results.reverse
  case head :: tail => applyAll(tail, (head -> head(results)) :: results)
}

applyAll(List(new SomeOp, new OtherOp)).last._2 // foobar

这有效,但是结果列表中的Any看起来很丑:( 有办法解决吗?我可以以某种方式声明它以确保元组的第二个元素是第一个元素声明的#Result类型的吗?

This works, but Any in the result list looks ugly :( Is there a way around it? Can I declare it somehow to guarantee that second element of the tuple is the of the #Result type declared by the first element?

推荐答案

有几种方法可以摆脱Any.这是到目前为止我可以想到的选项列表:

There are several ways to get rid of Any. Here is the list of options I could come up with so far:

  1. 使用forSome将结果与操作关联"
  2. 定义一个既包含操作又包含结果的自定义类
  3. 将整个设计从列表转换为monad
  1. Use forSome to "correlate" result with the operation
  2. Define a custom class that holds both operations and results
  3. Convert the whole design from lists to monad


forSome解决方案


The forSome solution

问题标题似乎恰好是关于forSome的问题:

The question title seems to ask exactly about the forSome:

(Operation[X], X) forSome { type X }

在这里,类型变量XforSome量词绑定,并保证列表中的元组只能存储匹配类型的操作和输出.

Here, the type variable X is bound by the forSome quantifier, and it guarantees that the tuples in your list can store only operations and outputs of matching types.

虽然它禁止像(SomeOperation[String], Int)这样的元组的出现,但实例化却有点麻烦:

While it prohibits the occurrence of tuples like (SomeOperation[String], Int), the instantiation becomes a bit cumbersome:

    val newResult: (Operation[Y], Y) forSome { type Y } = head match {
      case op: Operation[t] => (op -> op(results))
    }

t这里是如何使用它的演示:

Here is a demo of how this can be used:

type Results = List[(Operation[X], X) forSome { type X }]
trait Operation[Out] { 
  type Result = Out
  def apply(results: Results): Out
}

class SomeOp extends Operation[String] {
   def apply(results: Results) = "foo"
}

class OtherOp extends Operation[String] {
   def apply(results: Results) = results
     .collectFirst { case (_: SomeOp, x: String) => x } 
     .getOrElse("") + "bar"
}

def applyAll(
  ops: List[Operation[_]], 
  results: Results = Nil
): Results = ops match {
  case Nil => results.reverse
  case head :: tail => {
    val newResult: (Operation[Y], Y) forSome { type Y } = head match {
      case op: Operation[t] => (op -> op(results))
    } 
    applyAll(tail, newResult :: results)
  }
}

println(applyAll(List(new SomeOp, new OtherOp)).last._2)

它像以前一样简单地输出foobar.

It simply outputs foobar, as before.

用于操作和结果的自定义类别

与其使用具有复杂的存在性的元组,不如使用它更容易 定义一个自定义类型以将操作与结果一起保存:

Instead of using tuples with complex existentials, it might be easier to define a custom type to hold operations together with results:

case class OpRes[X](op: Operation[X], result: X)

使用返回OpRes的相应方法添加到Operation, 一切都变得非常简单:

With a corresponding method returning OpRes added to Operation, everything becomes rather straightforward:

  def opWithResult(results: Results): OpRes[Out] = OpRes(this, apply(results))

这是一个完整的可编译示例:

Here is a full compilable example:

case class OpRes[X](op: Operation[X], result: X)
type Results = List[OpRes[_]]
trait Operation[Out] { 
  type Result = Out
  def apply(results: Results): Out
  def opWithResult(results: Results): OpRes[Out] = OpRes(this, apply(results))
}

class SomeOp extends Operation[String] {
   def apply(results: Results) = "foo"
}

class OtherOp extends Operation[String] {
   def apply(results: Results) = results
     .collectFirst { case OpRes(_: SomeOp, x: String) => x } 
     .getOrElse("") + "bar"
}

def applyAll(
  ops: List[Operation[_]], 
  results: Results = Nil
): Results = ops match {
  case Nil => results.reverse
  case head :: tail => applyAll(tail, head.opWithResult(results) :: results)
}

println(applyAll(List(new SomeOp, new OtherOp)).last.result)

同样,它像以前一样输出foobar.

Again, it outputs foobar, as before.

也许应该只是一个单子吗?

最后,您的问题的第一句话包含短语

Finally, the first sentence of your question contains the phrase

操作顺序,其中一些取决于先前操作的结果

sequence of operations, some of which depend on some of the results of previous ones

在我看来,这几乎像一个monad的完美实用定义,因此也许您想用for -comprehensions而不是存在类型的列表来表示计算序列.这是一个粗略的草图:

This seems to me almost like the perfect practical definition of what a monad is, so maybe you want to represent sequences of computations by for-comprehensions instead of existentially typed lists. Here is a rough sketch:

trait Operation[Out] { outer =>
  def result: Out
  def flatMap[Y](f: Out => Operation[Y]): Operation[Y] = new Operation[Y] {
    def result: Y = f(outer.result).result
  }
  def map[Y](f: Out => Y) = new Operation[Y] {
    def result: Y = f(outer.result)
  }
}

object SomeOp extends Operation[String] {
   def result = "foo"
}

case class OtherOp(foo: String) extends Operation[String] {
   def result = foo + "bar"
}

case class YetAnotherOp(foo: String, bar: String) extends Operation[String] {
  def result = s"previous: $bar, pre-previous: $foo"
}

def applyAll: Operation[String] = for {
  foo <- SomeOp
  fbr <- OtherOp(foo)
  fbz <- YetAnotherOp(foo, fbr)
} yield fbz

println(applyAll.result)

它打印

previous: foobar, pre-previous: foo

我已将操作链延长了一个操作,以证明单子理解的操作当然可以访问以前定义的 all 个中间结果(在本例中为foofbr),不仅限于上一个.

I've made the chain of operations one operation longer to demonstrate that an operation in a monadic for-comprehension of course has access to all previously defined intermediate results (in this case, foo and fbr), not only to the previous one.

这篇关于关联两个类型的参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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