Scala CPS框架中的行为不一致

时间:2013-03-28 05:58:17

标签: scala continuations continuation-passing delimited-continuations

我正在尝试构建一个协程框架,通过并行逐步执行每个数据相关的函数来启用批量数据获取。以下是我到目前为止:http://pastie.org/7147798

  1. 这不起作用

    def get(id: Long) = reset {
      // Is it not already cached?
      if (!cached.isDefinedAt(id)) {
        // Store the ID we want to fetch.
        queued += id
        // Come back later...
        shift { fetch[Object]() } : Seq[Any] @cps[ExecState[Object]]
      }
      // We should have the ID fetched now.
      Result(cached(id))
    }
    

    我收到以下错误

    ashoat@ashoatmbp [~/project]# scala -P:continuations:enable Loader.scala
    /Users/ashoat/project/Loader.scala:134: error: type mismatch;
     found   : Unit
     required: Any @util.continuations.package.cps[Main.$anon.Loader.ExecState[Main.$anon.Loader.Object]]
          if (!cached.isDefinedAt(id)) {
          ^
    one error found
    

    此作品

    def get(id: Long) = reset {
      // Is it not already cached?
      if (!cached.isDefinedAt(id)) {
        // Store the ID we want to fetch.
        queued += id
        // Come back later...
        shift { fetch[Object]() } : Seq[Any] @cps[ExecState[Object]]
        // We should have the ID fetched now.
        Result(cached(id))
      } else {
        // We should have the ID fetched now.
        Result(cached(id))
      }
    }
    
  2. 这不起作用

    val getFive = reset {
      if (true) {
        Result(5)
      } else {
        val seq: Seq[Any] = shift { fetch[Int](Object.get(15181990251L)) }
        val Seq(obj: Object) = seq
        Result(obj.fields("test").toInt)
      }
    }
    

    我收到以下错误

    ashoat@ashoatmbp [~/project]# scala -P:continuations:enable Loader.scala
    /Users/ashoat/project/Loader.scala:170: error: cannot cps-transform expression new this.Loader.Result[Int](5): type arguments [this.Loader.Result[Int],this.Loader.Result[Int],Nothing] do not conform to method shiftUnit's type parameter bounds [A,B,C >: B]
        Result(5)// : Result[Int] @cps[Result[Int]]
              ^
    one error found
    

    此作品

    val getFive = reset {
      if (true) {
        Result(5) : Result[Int] @cps[Result[Int]]
      } else {
        val seq: Seq[Any] = shift { fetch[Int](Object.get(15181990251L)) }
        val Seq(obj: Object) = seq
        Result(obj.fields("test").toInt)
      }
    }
    

    但我收到以下警告

    ashoat@ashoatmbp [~/project]# scala -P:continuations:enable Loader.scala
    /Users/ashoat/project/Loader.scala:170: warning: expression (new this.Loader.Result[Int](5): this.Loader.Result[Int]) is cps-transformed unexpectedly
        Result(5) : Result[Int] @cps[Result[Int]]
                  ^
    one warning found
    8
    

1 个答案:

答案 0 :(得分:1)

虽然我仍然不太了解 continuations ,但就我所知,您的示例中的关键问题是您的代码并不总是向shift提供reset shift

编译器希望在reset内嵌套shift。然后,它会将shift转换为ControlContext][A, B, C],并将ControlContext.map之后的代码转换为if调用。

因为您有shift语句,如果采用 else 分支,则没有嵌套reset { if (false) { shift { ... } } Result(cached(id)) // no shift }

reset {
  if (false) {
    shift { ... }
  } else {
    Result(cached(id)) // no shift
  }
}

相同
if (!cached.isDefinedAt(id)) reset {
   shift { ... }
   Result(cached(id))
} else {
   Result(cached(id))
}

// or

reset {
  if (!cached.isDefinedAt(id)) {
    shift { ... }
    Result(cached(id))
  } else {
    shift[Result[Object], ExecState[Object], ExecState[Object]] { k => 
      Result(cached(id))
    }
  }
}    

无法转换为有效的CPS代码。

似乎你可以在if分支中重置或者向else分支提供一个简单的shift语句:

var b = false
def test[A](a: A) = reset {
  if (b) {
    a
  } else {
    shift{ (k: Unit => A) => k() }
    a
  }
}

编辑:似乎cps插件如何推断出这些类型存在一些不一致之处。例如:

-Xprint:selectivecps

使用Reset[A, Nothing]选项运行编译显示编译器将类型推断为var b = false def test[A](a: A) = reset { if (b) { shift{ (k: Unit => A) => k() } a } else { a } } ,然后运行代码将在运行时产生错误。如果 if 反转为:

reset[A, A]

然后编译器正确推断出reset。如果我将类型参数提供给test[A](a: A) = reset[A, A] { reset,那么它在两种情况下都有效。

也许指定shiftResult(5)的类型参数,而不是使用shiftUnit[A, B, C],使用{{1}}方法有助于减少不一致性。