Composition
This is an example of composition using ocaps.
Composition is fairly simple. You have two capabilities implemented as traits, A
and B
. Using composition, you create a proxy which has a compound type of A with B
.
You can read more in Constructing Capabilities section of the guide.
sourceimport ocaps.macros._
object Composition {
final class Foo(private var name: String) {
private object capabilities {
val doer: Foo.Doer = new Foo.Doer {
override def doTheThing(): Unit = {
println(s"$name.doTheThing()")
}
}
val changer: Foo.Changer = new Foo.Changer {
override def changeName(name: String): Foo.this.type = {
Foo.this.name = name
Foo.this
}
}
}
}
object Foo {
trait Doer {
def doTheThing(): Unit
}
trait Changer {
def changeName(name: String): Foo
}
trait Derper {
def derp(): Unit
}
class Access private {
def doer(foo: Foo): Doer = foo.capabilities.doer
def changer(foo: Foo): Changer = foo.capabilities.changer
}
object Access {
def apply(): Access = new Access
}
}
def main(args: Array[String]): Unit = {
import Foo._
val access = Foo.Access()
val foo = new Foo("foo")
val doer: Doer = access.doer(foo)
val changer: Changer = access.changer(foo)
val derper: Derper = new Derper {
override def derp(): Unit = println("derp!")
}
val doerChangerDerper =
compose[Doer with Changer with Derper](doer, changer, derper)
// composition is often used when you want to return a "set" of capabilities after
// some authorization event has taken place, after which you can do some pattern matching
doerChangerDerper match {
case d: Derper =>
d.derp()
}
// this of course works with all of the compound types, but you can also use attenuation
// to pull out a particular capability.
val attenuatedChanger: Changer = doerChangerDerper match {
case c: Changer =>
attenuate[Changer](c)
}
attenuatedChanger.changeName("bar")
}
}
The source code for this page can be found here.