How do you mix functionality with each step of an iterative procedure using Scala?

I am working on an optimization routine in Scala and am looking for advice on how to structure my problem. The procedure takes one step at a time, so I naively modeled the problem using the class Step

:

class Step(val state:State) {
  def doSomething = ...
  def doSomethingElse = ...
  def next:Step = ... // produces the next step in the procedure
}

      

Each step in the procedure is represented by an immutable class Step

whose constructor is set to the state generated by the previous step and the method next

creates the next instance Step

. The basic idea is to wrap this with a Iterator[Step]

so that the steps can be taken until the optimization converges. While this is a bit oversimplified, it works well for the vanilla case.

Now, however, I need to add various extensions to the algorithm, and I need to randomly mix these extensions depending on the problem being optimized. This is usually done with a stacked pattern, but this approach poses challenges for this problem. Below is an example of two potential extensions:

trait FeatureA extends Step {
  // Extension-specific state passed from step to step
  val aState:FeatureAState = ...

  // Wrap base methods to extend functionality
  abstract override def doSomething = { ...; super.doSomething(); ... }
}

// Just like Feature A
trait FeatureB extends Step {
  val bState:FeatureBState = ...
  abstract override def doSomething = { ...; super.doSomething(); ... }
}

      

Sometimes optimization will require FeatureA

, and sometimes FeatureB

, sometimes both.

The main problem is that the next

base class method does not know which extensions were mixed, so subsequently created stages will not contain any extensions mixed with the original ones.

In addition, each extension must pass its own state from step to step. In this example, instances of FeatureAState

/ FeatureBState

are included in their respective traits, but without method overriding next

FeatureA

and FeatureB

no way to traverse their unique state. Redefinition next

in every feature is impossible, since there can be a combination of these extensions mixed inside, and each of them knows only about itself.

So, I seem to have drawn myself in a corner and hope someone has an idea of ​​how to approach this with Scala. What's the best design pattern for this type of problem?

+3


source to share


1 answer


You may be interested in exploring the F-bound polymorphism pattern . This template allows you to define methods that return the current subtype in a trait or base class. Here's a simplified version of your example:

trait Step[T <: Step[T]] { self: T =>
    val name: String
    def next: T
}

case class BasicStep(name: String) extends Step[BasicStep] {
    def next = this.copy(name = name + "I")
}

case class AdvancedStep(baseName: String, iteration: Int) extends Step[AdvancedStep] {
    val name = s"$baseName($iteration)"
    def advancedFunction = println("foobar")
    def next = this.copy(iteration = iteration + 1)
}

      

So, we have defined a base trait Step

that has a method name

and next

that returns everything of type self from the expanding class. For example, method next

in BasicStep

returns a BasicStep

. This allows us to iterate over and use overridden subtypes as desired:

val basicSteps = Iterator.iterate(BasicStep("basic"))(_.next).take(3).toList
//basicSteps: List[BasicStep] = List(BasicStep(basic), BasicStep(basicI), BasicStep(basicII))

val advancedSteps = Iterator.iterate(AdvancedStep("advanced", 0))(_.next).take(3).toList
//advancedSteps: List[AdvancedStep] = List(AdvancedStep(advanced,0), AdvancedStep(advanced,1), AdvancedStep(advanced,2))
val names = advancedSteps.map(_.name)
//names: List[String] = List(advanced(0), advanced(1), advanced(2))
advancedSteps.last.advancedFunction
//foobar

      

If you want to mix several types like this, you unfortunately cannot use generics (you will get the error "inherits different types of trait instances"). However, you can use abstract type members to express F-linked polymorphism:



trait Step { self =>
    type Self <: Step { type Self = self.Self }
    val name: String
    def next: Self
}

trait Foo extends Step {
    val fooMarker = "foo"
}
trait Bar extends Step {
    val barMarker = "bar"
}

case class FooBar(name: String) extends Foo with Bar {
    override type Self = FooBar
    def next = this.copy(name + "I")
}

      

Then the instances FooBar

will have methods on Foo

and on Bar

:

val fooBar = FooBar("foobar").next.next
fooBar.barMarker //"bar"
fooBar.fooMarker //"foo"
fooBar.name //"fooNameII"

      

Note that the name comes from Foo

as it was first mixed.

+2


source







All Articles