是否有类似延续的类型来包装执行块,如(Ctx => R)=> R或(=> R)=> R'

时间:2014-10-05 13:40:04

标签: scala continuations

我正在寻找这样一种类型,它允许我代表一个上下文,其中运行一段代码。例如:

def withinContext[R]: ((=> R) => R) =
  (inner) => {
    initializeSomeResource()
    try {
      inner
    } finally {
      releaseTheResource()
    }
  }

然后我可以简单地用作

withinContext {
  ...
}

或者,如果内部代码块需要来自上下文的一些信息,则将其概括为

def withinContext[R]: ((Ctx => R) => R) = ...

他们的用例大致对应于Haskell的bracket_bracket

我可以直接使用类型(=> R) => R(A => R) => R,但是我没有用于组合这些上下文包装器的实用程序函数,所以我想,是否存在类似于Scala生态系统?

我知道的关闭事件是scala.util.control.Exception.Catch,它为构造和组合Catch实例提供了很好的函数,但是在内部块执行之前似乎没有办法运行任何初始化。另外(这对我的用例来说并不重要)它不允许为内部计算提供参数,就像(A => R) => R一样。

类型(A => R) => R是延续monad,对应于Haskell的ContT r IO a,但我无法在任何标准Scala库中找到continuation monad的实现(也许它& #39; s隐藏在斯卡拉兹深处,我错过了它。)

1 个答案:

答案 0 :(得分:0)

我经常使用这种方法与Specs2测试代码一起使用。基本上我们希望设置一些上下文来包围代码块。我使用的成语是这样的:

def contextName[TYPE,RSRC](doit: (RSRC) => TYPE) : TYPE = {
  val thing : RSRC = acquireResource(args)
  try doit(thing)
  finally releaseResource(thing)
}

我意识到你正试图继续延续,但我不得不问:为什么?我刚刚给出的成语是类型的连续monad(RSRC => TYPE)=> TYPE,它可以像您在withContext中建议的那样使用。例如,我写的ReactiveMongo上下文对象的实际用例:

def withDatabase[T](dbName: String)(doit: (DefaultDB) => T) : T = {
  val db = connection.db(dbName)
  try doit(db)
  finally db.drop()
}

这将获取一个数据库,将其传递给continuation,然后在完成后删除数据库,即使continuation抛出异常也是如此。它被这样使用:

val numCollections = withDatabase("foo") { db => 
  db.collectionNames.map { list => list.size } 
}

其中只使用数据库来获取数据库中的(未来)数量的集合。

希望这有帮助。