Вывод типа результата в продолжениях

Можно ли удалить некоторые типы из следующего кода:

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 функция отслеживает вызовы tracked на Int экземпляры (например, 7.tracked).

Можно ли вывести параметр типа на tracked неявный, поэтому следующее будет компилироваться:

track(7.tracked + 35.tracked)

2 ответа

Решение

Ваш вопрос заставил меня задуматься о том, как продолжения могут отслеживать состояние. Поэтому я адаптировал это к вашему случаю и придумал следующее:

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 функция, которая принимает текущее состояние (набор целых) и возвращает следующее состояние. Возвращаемый тип сброса - это функция от состояния к состоянию (типа ST), который примет начальное состояние и вернет конечное состояние.

Последний трюк состоит в том, чтобы использовать переменную для получения результата, сохраняя при этом ожидаемый тип для reset,

Хотя точный ответ на этот вопрос могут дать только авторы компилятора, мы можем догадаться, что это невозможно, если взглянуть на исходный код плагина продолжения.

Если вы посмотрите на источник продолжения, вы можете увидеть это:

  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 выполняется после фазы выбора, а фаза cpsPhase - после selectedAnf. Если вы посмотрите на 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"

Если мы используем скалярные -Xshow-фазы, мы можем видеть фазы во время процесса компиляции:

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

Как вы можете видеть, фаза typer применяется перед фазами selectiveAnf и selecps. Следует подтвердить, что вывод типа происходит в фазе Typer, но если это действительно так и имеет смысл, теперь должно быть понятно, почему нельзя опускать тип Int в 7.tracked и 35.tracked.

Теперь, если вы еще не удовлетворены, вы должны знать, что компилятор работает, выполняя набор преобразований для "деревьев", которые вы можете посмотреть, используя следующие параметры:

  • -Xprint: показывает ваш код Scala после выполнения определенной фазы
  • -Xprint: -Yshow-trees показывает ваш код Scala и деревья после выполнения фазы
  • -YBrowse: открывает графический интерфейс для серфинга обоих.
Другие вопросы по тегам