Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can Scala constrain an object graph so that only those objects relevant to the context are visible?

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.

like image 763
Larry OBrien Avatar asked Nov 14 '22 09:11

Larry OBrien


1 Answers

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
}
like image 156
Michał Politowski Avatar answered Dec 05 '22 06:12

Michał Politowski