问题
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