Question

Here is my problem: I have a large sequence of objects on which I want to apply procedures (which I call "compilers"), if some predicate apply. For clarity, I want to separate the predicate function from the procedure; however in many case the predicate may be quite complex, and builds information that I would like to reuse in my latter procedure function.

I define a compiler as follows:

trait Compiler[A] {
  def mtch(o: SourceObject): Option[A]
  def proceed(o: SourceObject, data: A): Unit
}

And how compilers are called:

val compilers: Seq[Compiler[_]]
val objects: Seq[SourceObject]

for (o <- objects; c <- compilers; data <- c.mtch(o)) {
  c.proceed(o, data)
}

That is, if the mtch function returns Some(data), then the proceed method is called, with data attached. However, I cannot compile this, as I do not know the type of data when I manage my compilers.

Moreover, I have cases where I do not actually need any data. In my current state, I have the matcher return Some(null), which stinks.

Was it helpful?

Solution

Go with path-dependent types instead. Replace

trait Compiler[A] {
  def mtch(o: SourceObject): Option[A]
  def proceed(o: SourceObject, data: A): Unit
}

with

trait Compiler {
  type A
  def mtch(o: SourceObject): Option[A]
  def proceed(o: SourceObject, data: A): Unit
}

and everything will work.

The trick here is that the type of data in your for-comprehension becomes c.A, which is the type c.proceed expects as its second parameter.

As for null, make compiler which don't need to pass parameters type A = Unit, so you return Some(()) if it should proceed.

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