2

我试图建立一个协同框架,通过并行地逐步通过每个数据相关功能来启用批量数据获取。以下是我迄今为止:http://pastie.org/7147798斯卡拉CPS框架中的行为不一致

  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)) 
    } 
    

    我收到以下错误

    [email protected] [~/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) 
        } 
    } 
    

    我收到以下错误

    [email protected] [~/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) 
        } 
    } 
    

    但我得到以下警告

    [email protected] [~/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 
    
+0

你为什么期望“现在获取ID”?我读取你的代码的方式,我看到'Result(...)'在* shift *块之后,也是* reset *中的最后一个表达式。所以我期望'shift'采用Result [Int]并用它计算另一个表达式,这将是整个* reset *块返回的值。 – huynhjl 2013-03-28 07:25:16

+0

@Ashoat请大家帮忙解释一下你想达到的目标。获取函数的功能是什么?看起来你使用shift来调用外部过程而不是上下文转换。不想听起来粗鲁,但我必须问你是否对使用shift/reset有信心。 – 2013-03-28 08:17:04

+0

@ pagoda_5b:我编辑了这个问题来添加一些更多的细节,包括到目前为止我已经完整源代码的链接。我对shift的使用只是将定界复位块的当前状态与一些指定的依赖关系一起传递给一个将它们转换为单个continuation的函数,然后将其返回给reset块的调用者。 – Ashoat 2013-03-28 09:17:25

回答

1

虽然我还是不太明白延续自己,作为最好的,我可以告诉大家,在你的榜样的关键问题是,您的代码并不总是向reset提供shift

编译器希望找到嵌套在reset内的一些shift。然后CPS会将shift转换为ControlContext][A, B, C],并将在shift之后发生的代码转换为ControlContext.map调用。

因为你有一个if声明,在情况下其他分支,没有嵌套shift

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

同样的,

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

不能转化成有效的CPS代码。

看来,你可以有复位如果分支内或提供一个简单的转变语句else分支:

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如何插件推断一些不一致的地方类型。例如:

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

运行与-Xprint:selectivecps选项汇编示出了编译器推断类型为Reset[A, Nothing]然后运行该代码将在运行时产生一个错误。如果如果逆转为:

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

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

也许指定类型参数resetshift并且还代替使用Result(5),使用shiftUnit[A, B, C]方法将减少不一致性帮助。

+0

感谢您的建议。看起来这些问题似乎只发生在'reset'中没有经过'shift'块的返回路径。然而,这仍然不能解释为什么我会得到我所处的行为:计算上等价的语句是以单向编写的方式进行编译,而在写入其他语言时出现错误。我的怀疑是这与类型推断如何与延续插件一起工作,但我想明确知道哪里出了问题。 – Ashoat 2013-03-29 22:03:05

+0

我认为编译器cps插件无法报告错误的代码。当你说“它有效”时,你的意思是代码实际执行它应该做的事情吗?或者它只是编译?我的直觉是它编译,但你会得到一个运行时错误或不正确的行为。 – huynhjl 2013-03-30 01:51:04

+0

代码执行它应该...除非我反转if-else块,在这种情况下,它会产生运行时错误。行为非常不一致。 – Ashoat 2013-03-30 04:27:39