Question

I am new to Scala. I know the basic of type theory.

The problem is to create a graph object of nodes with callbacks.

The following is simplified version of the current implementation. This is a part of a library.

It works, but full of Any. I think it is impossible to get rid of all Any, but I suspect there is a better way.

Especially, CallbackNode takes covariant type parameter, but callback's type is contravariant, then becomes Any.
But callbacks are everywhere in user code, so it is better to have type A for usability.

Let me know how you will design against such problem.

trait Graph[N] {
  var nodes: List[N]
  var edges: List[Edge[N]]

  def register(node: N) { nodes = node :: nodes }
  def dependants(node: N): Seq[N] = { ... }
}

object MyGraph with Graph[CallbackNode[Any]] {
  ...
}

class CallbackNode[+A](getter: => A) {
  var cache: Option[Any]
  MyGraph.register(this)

  def onChange(callback: Any => Unit) { ... }
  def update() {
    val v = getter
    storeInCache(v)
    dependants.foreach { n => n.notify() }
    callbacks.foreach { c => c(v) }
  }

  ...
}
Was it helpful?

Solution 2

Thanks to @senia's comment, I found the method.

In this case, callbackList and cache can be private[this] (instance private. which is invisible from other instance of the same class). Then I made types in methods to be invariant.

class CallbackNode[+A](getter: => A) {
  private[this] var cache: Option[A]
  private[this] var callbackList: List[A => Unit]
  MyGraph.register(this)

  def onChange(callback: A => Unit) { ... }
  def update() {
    val v = getter
    storeInCache(v)
    dependants.foreach { n => n.notify() }
    callbacks.foreach { c => c(v) }
  }

  ...
}

Graph is still CallbackNode[Any] as nodes. MyGraph is for internal use, so this Any is not a large problem. The idea from shapeless will solve this problem, too, if needed.

OTHER TIPS

If you can accept that per a given graph instance all the callbacks have the same type A, you can move the type as a part of the graph definition. Since you probably don't want, then you need to use the first common ancestors, which might end up to be Any.

There is however way in Scala, even if it is not trivial, to have data structures which behaves like collections but preserve types as you are looking to do. You should have a look to @MilesSabin shapeless library to understand how it's done. https://github.com/milessabin/shapeless

Once you master Shapeless concepts such as HList and HMap, you can develop HGraph and open source it :)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top