Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/redis/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scala 关联两个类型参数_Scala_Types_Type Parameter_Existential Type_Path Dependent Type - Fatal编程技术网

Scala 关联两个类型参数

Scala 关联两个类型参数,scala,types,type-parameter,existential-type,path-dependent-type,Scala,Types,Type Parameter,Existential Type,Path Dependent Type,假设我有一个操作序列,其中一些操作依赖于前面操作的一些结果。大概是这样的: 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) = "

假设我有一个操作序列,其中一些操作依赖于前面操作的一些结果。大概是这样的:

 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
这是可行的,但结果列表中的任何看起来都很难看:(
有什么办法吗?我可以声明它以保证元组的第二个元素是第一个元素声明的
#Result
类型的一部分吗?

有几种方法可以消除
任何
。以下是我目前可以想到的选项列表:

  • 对某些使用
    将结果与操作“关联”
  • 定义一个包含操作和结果的自定义类
  • 将整个设计从列表转换为monad

  • 针对某些
    解决方案的

    问题标题似乎正好询问了一些

    (Operation[X], X) forSome { type X }
    
    这里,类型变量
    X
    forSome
    量词绑定,它保证列表中的元组只能存储匹配类型的操作和输出

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

        val newResult: (Operation[Y], Y) forSome { type Y } = head match {
          case op: Operation[t] => (op -> op(results))
        }
    
    t
    在那里是一个名称。这有时有助于处理存在主义,因为它允许我们为存在主义类型命名,在本例中是
    t

    以下是如何使用此功能的演示:

    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


    操作+结果的自定义类

    与其将元组与复杂的存在式结合使用,不如更容易理解 定义自定义类型以将操作与结果保存在一起:

    case class OpRes[X](op: Operation[X], result: X)
    
    将返回
    操作
    的相应方法添加到
    操作
    , 一切都变得相当简单:

      def opWithResult(results: Results): OpRes[Out] = OpRes(this, apply(results))
    
    下面是一个完整的可编译示例:

    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


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

    最后,你问题的第一句话包含了这个短语

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

    在我看来,这几乎像是对单子的完美实用定义,因此,也许你想用
    来表示计算序列,而不是存在类型的列表。这里是一个粗略的草图:

    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)
    

    我将操作链延长了一个操作,以证明一元理解中的操作当然可以访问所有先前定义的中间结果(在本例中,
    foo
    fbr
    ),而不仅仅是前一个结果。

    您可以为某些{type X}设置
    results=List[(操作[X],X)]
    ,或者简单地引入一个新的
    案例类Result[X](op:Operation[X],output:X)
    ,而不是使用元组。但这无助于您摆脱标记为
    foobar
    的行中的
    任何
    。您确定首先需要一个包含存在类型的操作列表吗?因为“操作顺序,其中一些操作取决于以前操作的某些结果“听起来很像monad,而且这些都可以很好地用scala的类型系统来实现。@Andreytukin我知道monad是什么…但不确定你在这里得到了什么。
    flatMap
    在这里不太有效,因为我的操作不一定取决于上一个(为了通用性,我需要整个列表)。实际上,在标有
    //foobar
    的行中没有
    Any
    (这只是
    Operation[\u]
    ),
    Any
    在第一行,声明
    结果
    。对于某些{type X},将其更改为
    list[(操作[X],X)]
    不编译:
    (head->head(results))::results
    因“found(Operation[\u$1],Any)…”而失败。我想,问题是
    X
    对于所有元素都必须是相同的,这不是这里的情况……是的,它以某种方式推断出
    \u$1
    Any
    ,而不是针对某些{type t}的
    (Op[t],t)
    。我的答案的第一部分显示了如何通过在模式匹配的左侧引入类型变量
    t
    来解决这个问题:scala的模式匹配的一个被低估的特性,imho。我添加了
    存在类型
    标记,它似乎比
    路径依赖类型
    更接近问题,因为有一些
    \uuuu
    但是没有
    p.T
    -s。monad解决方案的问题就是我想避免硬编码操作。这个想法是将一个列表传递到
    applyAll
    ,而不是在
    中拼写出每一个调用,以理解
    的内容……实际上列表相当大。但是,
    操作[X]
    thingy似乎有效(我可以发誓我试过了,但它不允许我用通配符添加到列表中,但我猜,我在列表中输入了错误的内容)@Dima您可能在
    操作
    上没有返回
    操作
    的方法,而是尝试在
    applyAll
    中实例化
    新操作
    ,因此失败的原因与您在下面的评论中提到的第一次尝试对某些
    的原因相同。如果您想省略
    opWithResult
    ,您将再次需要类似于
    head match{case op:Operation[t]=>new OpRes(op,op.apply(results))
    -使用类型参数
    t
    的构造来实例化
    OpRes
    。您可能尝试了一些非常类似的方法,但很快就将其视为不可编译/可修复。
    previous: foobar, pre-previous: foo