scoped
Use case
Scoped uses IOLocal to share context through an application as documented here
(from the documentation)
┌────────────┐ ┌────────────┐
fork │ Fiber B │ update(_ + 1) │ Fiber B │
┌─────►│ (local 42) │──────────────►│ (local 43) │
│ └────────────┘ └────────────┘
┌────────────┐─┘ ┌────────────┐
│ Fiber A │ update(_ - 1) │ Fiber A │
│ (local 42) │────────────────────────────────────►│ (local 41) │
└────────────┘─┐ └────────────┘
│ ┌────────────┐ ┌────────────┐
│ fork │ Fiber C │ update(_ + 2) │ Fiber C │
└─────►│ (local 42) │──────────────►│ (local 44) │
└────────────┘ └────────────┘
One example use case for this is passing around transaction ids where you can set them at the start and any further effects are able to access.
Example
Lets start by creating a datatype we want to be shared around
opaque type TransactionId = String
object TransactionId:
def fromString(str: String): TransactionId = str
We can wrap it up and give it some useful name
import is.ashley.scoped.Scoped
type Transactional[F[_]] = Scoped[F, TransactionId]
object Transactional:
def apply[F[_]](using ev: Transactional[F]): Transactional[F] = ev
And now a service that will be using our new datatype, here we can use Transactional
as a constraint on F[_]
ensuring
that we have the value set in scope.
class MyService[F[_]: Transactional]:
def doSomething: IO[Unit] =
for
txid <- Scoped[F, TransactionId].get // txid = "not-set"
_ <- Transactional[F].scope("123").use(new MyOtherService)
yield ()
Another service using Transactional
this service is called by the .use
above and has had the TransactionId value changed.
class MyOtherService[F[_]: Transactional]:
def doOtherThing: IO[Unit] =
for
txid <- Transactional[F].get // txid = "123"
...
yield ()
In order to setup the scoping we create a TransactionId
here and hand it to Scoped.fromIOLocal
we can then use this to meet
our Transactional
constraint further down the stack.
Scoped.fromIOLocal(TransactionId.fromString("not-set")).flatMap {
implicit transactionalScope: Transactional[IO] =>
val service = new MyService[IO]
service.doSomething
}