vault
Project Goals
Vault is a tiny library that provides a single data structure called vault.
Inspiration was drawn from HeinrichApfelmus/vault and the original blog post
A vault is a type-safe, persistent storage for values of arbitrary types. Like Ref
, it should be capable of storing values of any type in it, but unlike Ref
, behave like a persistent, first-class data structure.
It is analogous to a bank vault, where you can access different bank boxes with different keys; hence the name.
Quick Start
To use vault in an existing SBT project with Scala 2.11 or a later version, add the following dependencies to your
build.sbt
depending on your needs:
libraryDependencies ++= Seq(
"org.typelevel" %% "vault" % "3.2.1",
)
First the imports
import cats.effect._
import cats.implicits._
import org.typelevel.vault._
// Importing global cats-effect runtime to allow .unsafeRunSync;
// In real code you should follow cats-effect advice on obtaining a runtime
import cats.effect.unsafe.implicits.global
Then some basic operations
case class Bar(a: String, b: Int, c: Long)
// Creating keys are effects, but interacting with the vault
// not, it acts like a simple persistent store.
val basicLookup = for {
key <- Key.newKey[IO, Bar]
} yield {
Vault.empty
.insert(key, Bar("", 1, 2L))
.lookup(key)
}
// basicLookup: IO[Option[Bar]] = Map(
// ioe = Map(
// ioe = Delay(
// thunk = cats.effect.kernel.Sync$$Lambda$10144/1471091654@68fb76e3,
// event = cats.effect.tracing.TracingEvent$StackTrace
// ),
// f = org.typelevel.vault.Key$$$Lambda$10146/1565177491@f78536b,
// event = cats.effect.tracing.TracingEvent$StackTrace
// ),
// f = <function1>,
// event = cats.effect.tracing.TracingEvent$StackTrace
// )
basicLookup.unsafeRunSync
// res0: Option[Bar] = Some(value = Bar(a = "", b = 1, c = 2L))
val lookupValuesOfDifferentTypes = for {
key1 <- Key.newKey[IO, Bar]
key2 <- Key.newKey[IO, String]
key3 <- Key.newKey[IO, String]
} yield {
val myvault = Vault.empty
.insert(key1, Bar("", 1, 2L))
.insert(key2, "I'm at Key2")
.insert(key3, "Key3 Reporting for Duty!")
(myvault.lookup(key1), myvault.lookup(key2), myvault.lookup(key3))
.mapN((_,_,_))
}
// lookupValuesOfDifferentTypes: IO[Option[(Bar, String, String)]] = FlatMap(
// ioe = Map(
// ioe = Delay(
// thunk = cats.effect.kernel.Sync$$Lambda$10144/1471091654@68fb76e3,
// event = cats.effect.tracing.TracingEvent$StackTrace
// ),
// f = org.typelevel.vault.Key$$$Lambda$10146/1565177491@f78536b,
// event = cats.effect.tracing.TracingEvent$StackTrace
// ),
// f = <function1>,
// event = cats.effect.tracing.TracingEvent$StackTrace
// )
lookupValuesOfDifferentTypes.unsafeRunSync
// res1: Option[(Bar, String, String)] = Some(
// value = (
// Bar(a = "", b = 1, c = 2L),
// "I'm at Key2",
// "Key3 Reporting for Duty!"
// )
// )
val emptyLookup = for {
key <- Key.newKey[IO, Bar]
} yield {
Vault.empty
.lookup(key)
}
// emptyLookup: IO[Option[Bar]] = Map(
// ioe = Map(
// ioe = Delay(
// thunk = cats.effect.kernel.Sync$$Lambda$10144/1471091654@68fb76e3,
// event = cats.effect.tracing.TracingEvent$StackTrace
// ),
// f = org.typelevel.vault.Key$$$Lambda$10146/1565177491@f78536b,
// event = cats.effect.tracing.TracingEvent$StackTrace
// ),
// f = <function1>,
// event = cats.effect.tracing.TracingEvent$StackTrace
// )
emptyLookup.unsafeRunSync
// res2: Option[Bar] = None
val doubleInsertTakesMostRecent = for {
key <- Key.newKey[IO, Bar]
} yield {
Vault.empty
.insert(key, Bar("", 1, 2L))
.insert(key, Bar("Monkey", 7, 5L))
.lookup(key)
}
// doubleInsertTakesMostRecent: IO[Option[Bar]] = Map(
// ioe = Map(
// ioe = Delay(
// thunk = cats.effect.kernel.Sync$$Lambda$10144/1471091654@68fb76e3,
// event = cats.effect.tracing.TracingEvent$StackTrace
// ),
// f = org.typelevel.vault.Key$$$Lambda$10146/1565177491@f78536b,
// event = cats.effect.tracing.TracingEvent$StackTrace
// ),
// f = <function1>,
// event = cats.effect.tracing.TracingEvent$StackTrace
// )
doubleInsertTakesMostRecent.unsafeRunSync
// res3: Option[Bar] = Some(value = Bar(a = "Monkey", b = 7, c = 5L))
val mergedVaultsTakesLatter = for {
key <- Key.newKey[IO, Bar]
} yield {
(
Vault.empty.insert(key, Bar("", 1, 2L)) ++
Vault.empty.insert(key, Bar("Monkey", 7, 5L))
).lookup(key)
}
// mergedVaultsTakesLatter: IO[Option[Bar]] = Map(
// ioe = Map(
// ioe = Delay(
// thunk = cats.effect.kernel.Sync$$Lambda$10144/1471091654@68fb76e3,
// event = cats.effect.tracing.TracingEvent$StackTrace
// ),
// f = org.typelevel.vault.Key$$$Lambda$10146/1565177491@f78536b,
// event = cats.effect.tracing.TracingEvent$StackTrace
// ),
// f = <function1>,
// event = cats.effect.tracing.TracingEvent$StackTrace
// )
mergedVaultsTakesLatter.unsafeRunSync
// res4: Option[Bar] = Some(value = Bar(a = "Monkey", b = 7, c = 5L))
val deletedKeyIsMissing = for {
key <- Key.newKey[IO, Bar]
} yield {
Vault.empty
.insert(key, Bar("", 1, 2L))
.delete(key)
.lookup(key)
}
// deletedKeyIsMissing: IO[Option[Bar]] = Map(
// ioe = Map(
// ioe = Delay(
// thunk = cats.effect.kernel.Sync$$Lambda$10144/1471091654@68fb76e3,
// event = cats.effect.tracing.TracingEvent$StackTrace
// ),
// f = org.typelevel.vault.Key$$$Lambda$10146/1565177491@f78536b,
// event = cats.effect.tracing.TracingEvent$StackTrace
// ),
// f = <function1>,
// event = cats.effect.tracing.TracingEvent$StackTrace
// )
deletedKeyIsMissing.unsafeRunSync
// res5: Option[Bar] = None
We can also interact with a single value locker
instead of the
larger datastructure that a vault
enables.
val lockerExample = for {
key <- Key.newKey[IO, Bar]
} yield {
Locker(key, Bar("", 1, 2L))
.unlock(key)
}
// lockerExample: IO[Option[Bar]] = Map(
// ioe = Map(
// ioe = Delay(
// thunk = cats.effect.kernel.Sync$$Lambda$10144/1471091654@68fb76e3,
// event = cats.effect.tracing.TracingEvent$StackTrace
// ),
// f = org.typelevel.vault.Key$$$Lambda$10146/1565177491@f78536b,
// event = cats.effect.tracing.TracingEvent$StackTrace
// ),
// f = <function1>,
// event = cats.effect.tracing.TracingEvent$StackTrace
// )
lockerExample.unsafeRunSync
// res6: Option[Bar] = Some(value = Bar(a = "", b = 1, c = 2L))
val wrongLockerExample = for {
key <- Key.newKey[IO, Bar]
key2 <- Key.newKey[IO, Bar]
} yield {
Locker(key, Bar("", 1, 2L))
.unlock(key2)
}
// wrongLockerExample: IO[Option[Bar]] = FlatMap(
// ioe = Map(
// ioe = Delay(
// thunk = cats.effect.kernel.Sync$$Lambda$10144/1471091654@68fb76e3,
// event = cats.effect.tracing.TracingEvent$StackTrace
// ),
// f = org.typelevel.vault.Key$$$Lambda$10146/1565177491@f78536b,
// event = cats.effect.tracing.TracingEvent$StackTrace
// ),
// f = <function1>,
// event = cats.effect.tracing.TracingEvent$StackTrace
// )
wrongLockerExample.unsafeRunSync
// res7: Option[Bar] = None