Troy Daniels
Troy Daniels

Reputation: 3598

Using object-bound classes in scala

I am trying to write code to wrap a modeling library. A very simplified version of the model looks like this:

class Model() {
    class Thing(val name: String) {
        private[Model] val foo = name + " foo "

        def join(other: Thing) = new Thing(foo + other.foo)
    }

    def thing(name: String) = new Thing(name)
}

foo is actually some data structure that is tied to the specific instance of the Model. Using a Thing from two different Models does not make sense, and I think this syntax prevents that.

I have several scala tests call model.thing and use them, which works as desired.

I then started to write an abstraction layer on top of this:

class Widget(name: String)(implicit val model: Model) {
    val thing = model.thing(name)
}

class CompoundWidget(name: String, children: Seq[Widget])(implicit val model: Model) extends Widget(name)
{
    val combo =
        children.sliding(2).foreach(pair => pair(0).thing join pair(1).thing)
}

This fails to compile:

Simple.scala:20: error: type mismatch;
  found   : (some other)_1.type(in value $anonfun)#model.Thing where type (some other)_1.type(in value $anonfun) <: com.proteus.orchestration.mediatorModule.scheduling.Widget with Singleton
  required: _1.type(in value $anonfun)#model.Thing where type _1.type(in value $anonfun) <: com.proteus.orchestration.mediatorModule.scheduling.Widget with Singleton
         children.sliding(2).foreach(pair => pair(0).thing join pair(1).thing)
                                                                ^

This appears to be my attempt to prevent mixing models working too well. The compiler does not know that the Seq[Widget] I am passing in uses the same Model for each element, so it is giving an error.

Is there a way around this? The thing that seems likely to work (but I have not tried) is to make a class to hold the entire abstraction layer, so then the compiler can see that I am always using the same instance of Model. However, that means that, regardless of how large it grows, I would need to keep all of the code in a single file, which will eventually become unwieldy.

My background is a Java programmer starting to use Scala, so I may just need to have the Scala Way (tm) explained to me.

Upvotes: 2

Views: 59

Answers (1)

Brian McCutchon
Brian McCutchon

Reputation: 8584

What you're playing with here are dependent types. One way to do this is to make sure that both Widget and CompoundWidget refer to the same Model by putting it in a shared scope:

class View(val model: Model) {
  class Widget(name: String) {
    val thing = model.thing(name)
  }

  class CompoundWidget(name: String, children: Seq[Widget]) extends Widget(name) {
    val combo =
        children.sliding(2).foreach(pair => pair(0).thing join pair(1).thing)
  }
}

I call it View because I assume you're going for something like MVC. I suppose the reason you're using implicits is to reduce boilerplate. You can achieve something similar here:

val view = new View(new Model)
import view._
new Widget("foo")  // No need to specify View or Model

Upvotes: 3

Related Questions