Is there a way to use Scala's type-system to concisely specify the context-relevant subgraph of a complete object graph?
DCI argues that you often have a fairly complex object graph but in any one use-case you often only want to work with a sub-graph. You have a Foo
that has a Bar
and a Bat
, but when you're in use-case 1, you only care about the Bar
and when in use-case 2, only about the Bat
.
For instance, let's say that you have this structure, and the Role1 use-case requires Foo->Bar->Baz->Bin
and Role2 use-case requires Foo->Bat->Baz->Buz
:
class Foo{
val bar = new Bar() //Only relevant to Role 1
val bat = new Bat() //Only relevant to Role 2
}
class Bar {
val baz = new Baz()
}
class Bat {
val baz = new Baz()
}
//Relevant to both Role 1 and 2 (via Bar or Bat)
class Baz {
val bin = new Bin() //Only relevant to Role 1
val buz = new Buz() //Only relevant to Role 2
}
class Bin{}
class Buz{}
It's easy to see how you can constrain access in a single class by using traits:
trait FooInRole1 { def bar : Bar } //Define accessor in trait
s/Foo/Foo extends FooInRole1/ //Change Foo's declaration to implement trait
val f : FooInRole1 = new Foo //LHS is i'face, RHS is implementation
//f.bat <--Compile error Irrelevant field is not available. \o/
But you have to repeat this pattern for every object relevant to the use-case. (For instance, you need a BazInRole1
to access bin
and a BazInRole2
to access biz
)
My question is whether there's some way to avoid writing all these easy-to-get-wrong, namespace-crowding traits. For instance, I could imagine something like this code (that doesn't compile):
class Foo[T] {
T match {
case r1 : Role1 => def bar : Bar[T]
case r2 : Role2 => def bat : Bat[T]
case _ => //Nothing
}
}
val fInRole1 = new Foo[Role1] //Provides Foo->Bar->Baz->Bin
val fInRole2 = new Foo[Role2] //Provides Foo->Bat->Baz->Buz
It seems like Scala's type-system is expressive enough to do something like this, but I cannot figure it out.
Not extremely concise, and the members are there, just impossible to use, but maybe going in this direction would be acceptable?
class Foo[R] {
def bar(implicit ev: R <:< Role1) = new Bar[R] //Only relevant to Role 1
def bat(implicit ev: R <:< Role2) = new Bat[R] //Only relevant to Role 2
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With