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



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

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库中都找不到延续monad的实现(也许它隐藏在Scalaz深处,我错过了它)。

为了与Specs2测试代码一起使用,我经常做这种事情。从本质上讲,我们希望设置一些上下文来围绕代码块。我使用的习语是这样的:

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

我意识到你试图让一个延续成为一个延续,但我不得不问:为什么?我刚才给出的习语是类型(RSRC=>type)=>type的延续monad,它可以像您在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 } 
}

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

希望这能有所帮助。

相关内容

最新更新