【问题标题】:Inferring result type in continuations在延续中推断结果类型
【发布时间】:2012-06-02 14:36:33
【问题描述】:

是否可以从以下代码中删除某些类型:

import util.continuations._

object TrackingTest extends App {

  implicit def trackable(x: Int) = new {
    def tracked[R] = shift { cf: (Int => (R, Set[Int])) =>
      cf(x) match {
        case (r, ints) => (r, ints + x)
      }
    }
  }


  def track[R](body: => R @cpsParam[(R, Set[Int]), (R, Set[Int])]) = reset {
    (body, Set[Int]())
  }

  val result = track(7.tracked[Int] + 35.tracked[Int])
  assert(result == (42, Set(7, 35)))

  val differentTypes = track(9.tracked[String].toString)
  assert(differentTypes == ("9", Set(9)))
}

track 函数跟踪对Int 实例(例如7.tracked)的tracked 调用。

是否可以在 tracked 隐式上推断类型参数,因此可以编译以下内容:

track(7.tracked + 35.tracked)

【问题讨论】:

    标签: scala type-inference continuations


    【解决方案1】:

    您的问题让我想到了延续如何跟踪状态。因此,我根据您的情况对其进行了调整并提出了以下建议:

    import util.continuations._
    
    object TrackingTest extends App {
    
      type State = Set[Int]
      type ST = State => State
    
      implicit class Tracked(val i: Int) extends AnyVal { 
        def tracked = shift{ (k: Int=>ST) => (state:State) => k(i)(state + i) }
      }
    
      def track[A](thunk: => A@cps[ST]): (A, State) = {
        var result: A = null.asInstanceOf[A]
        val finalSate = (reset {
          result = thunk
          (state:State) => state
        }).apply(Set[Int]())
        (result, finalSate)
      }
    
      val result = track(7.tracked + 35.tracked)
      assert(result == (42, Set(7, 35)))
    
      val differentTypes = track(9.tracked.toString)
      assert(differentTypes == ("9", Set(9)))
    }
    

    这是使用 2.10.1,但它也适用于 2.9.1,前提是您将 2.10.x 隐式值类替换为:

    implicit def tracked(i: Int) = new {
      def tracked = shift{ (k: Int=>ST) => (state:State) => k(i)(state + i) }
    }
    

    我所做的关键更改是让tracked 不使用任何类型推断,固定为Int@cps[ST]。然后,CPS 插件将计算映射到适当的类型(如String@cps[ST])。状态由返回 State=>State 函数的延续线程化,该函数采用当前状态(整数集)并返回下一个状态。 reset 的返回类型是一个从一个状态到另一个状态的函数(类型为ST),它将采用初始状态并返回最终状态。

    最后一个技巧是使用 var 来捕获结果,同时仍保持 reset 的预期类型。

    【讨论】:

      【解决方案2】:

      虽然这个问题的确切答案只能由编译器的作者给出,但我们可以通过查看 continuation 插件源代码来猜测这是不可能的。

      如果您查看延续的来源,您会看到:

        val anfPhase = new SelectiveANFTransform() {
          val global = SelectiveCPSPlugin.this.global
          val runsAfter = List("pickler")
        }
      
        val cpsPhase = new SelectiveCPSTransform() {
          val global = SelectiveCPSPlugin.this.global
          val runsAfter = List("selectiveanf")
        }
      

      anfPhase 阶段在pickler 阶段之后执行,cpsPhase 在selectiveAnf 之后执行。如果您查看 SelectiveANFTransform.scala

      abstract class SelectiveANFTransform extends PluginComponent with Transform with
        TypingTransformers with CPSUtils {
        // inherits abstract value `global' and class `Phase' from Transform
      
        import global._                  // the global environment
        import definitions._             // standard classes and methods
        import typer.atOwner             // methods to type trees
      
        /** the following two members override abstract members in Transform */
        val phaseName: String = "selectiveanf"
      

      如果我们使用scalac -Xshow-phases,我们可以看到编译过程中的阶段:

      parser
      namer
      packageobjects
      typer
      superaccessors
      pickler
      refchecks
      selectiveanf
      liftcode
      selectivecps
      uncurry
      ......
      

      如您所见,typer 阶段应用在selectiveAnf 和selectiveCps 阶段之前。应该确认类型推断发生在 typer 阶段,但如果真的是这样并且有道理,现在应该清楚为什么不能在 7.tracked 和 35.tracked 上省略 Int 类型。

      现在,如果您还不满意,您应该知道编译器通过对“树”执行一组转换来工作,您可能会看到这些转换,使用以下选项:

      • -Xprint:在某个阶段执行后显示你的 scala 代码
      • -Xprint:-Yshow-trees 显示您的 scala 代码和执行阶段后的树
      • -YBrowse:打开一个 GUI 来浏览两者。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-11-03
        • 1970-01-01
        • 1970-01-01
        • 2017-07-05
        • 2019-03-12
        • 2013-08-13
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多