Attenuation
This is an example of attenuation in ocaps.
Attenuation is used to “narrow” a capability, by limiting the accessible functionality. It is the dual of Composition.
You can read Managing Capabilities in the guide for more information.
import ocaps.macros._
object Attenuation {
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 {
def doer(foo: Foo): Doer = foo.capabilities.doer
def changer(foo: Foo): Changer = foo.capabilities.changer
}
}
def main(args: Array[String]): Unit = {
import Foo._
val access = new 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)
// We want an attenuation that makes only Doer available
val castDoer: Doer = doerChangerDerper.asInstanceOf[Doer]
// But we can recover Derper capability here!
val doerAsDerper: Derper = castDoer.asInstanceOf[Derper]
// whoops :-(
doerAsDerper.derp()
// Attenuation doesn't use downcasting, is safe!
val attenuatedDoer: Doer = attenuate[Doer](doerChangerDerper)
try {
val downcastAttenuatedDerper = attenuatedDoer.asInstanceOf[Derper]
attenuatedDoer.doTheThing()
} catch {
case e: ClassCastException =>
println("Can't downcast to a different type using the macro!")
}
}
}
Full source at GitHub
0.2.0