Is there a continuation-like type for wrapping an execution block, like (Ctx => R) => R or (=> R) => R?

穿精又带淫゛_ 提交于 2019-12-10 14:35:34

问题


I'm looking for such a type that would allow me to represent a context, in which a piece of code is run. For example:

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

which then I can use simply as

withinContext {
  ...
}

Or, if the inner block of code needs some information from the context, generalize it as

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

Their use cases roughly correspond to Haskell's bracket_ and bracket.

I could use the types (=> R) => R and (A => R) => R directly, but then I have no utility functions for combining such context wrappers, so I wonder, is there anything like that already existing in the Scala ecosystem?

The closes thing I know is scala.util.control.Exception.Catch, which provides nice functions for constructing and combining Catch instances, but there seems to be no way for running any initialization before the inner block is executed. Also (this isn't so important to my use-case) it doesn't allow giving a parameter to the inner computation, like in the case (A => R) => R.

The type (A => R) => R is the continuation monad, corresponding to Haskell's ContT r IO a, but I couldn't find an implementation of the continuation monad in any standard Scala library (maybe it's hidden somewhere deep in Scalaz where I missed it).


回答1:


I do this sort of thing fairly often for use with Specs2 test code. Essentially we want some context set up to surround a block of code. The idiom I use is like this:

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

I realize you are trying to make a continuation of a continuation, but I have to ask: why? The idiom I've just given is a continuation monad of type (RSRC => TYPE) => TYPE and it can be used just as you've suggested with your withContext. For example, a real use case from a ReactiveMongo context object I've written:

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

This acquires a database, passes it to the continuation and then drops the database when it is done even if the continuation throws an exception. It gets used like this:

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

Which simply uses the database to obtain a (future) number of collections in the database.

Hope this helps.



来源:https://stackoverflow.com/questions/26203092/is-there-a-continuation-like-type-for-wrapping-an-execution-block-like-ctx

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!