Theo Dickson
Theo Dickson

Reputation: 53

Scala - Implementing a polymorphic abstract trait without polymorphism

I'm designing a simple data-processing pipeline in Scala. It involves PipelineStage's, which transform some StageOutput into another StageOutput. A Pipeline is a wrapper for a sequence of PipelineStage's which needs to generically access all of their transform methods.

However I'm having trouble as both solutions I've come up with fundamentally don't work... the first relies on an abstract polymorphic method being implemented in a non-polymorphic way (doesn't compile) and the second relies on being able to use a Seq[AbstractTrait] where the AbstractTrait is polymorphic, which is again meaningless to the compiler. See as follows...

Scenario 1. Make the transform method polymorphic.

    abstract trait PipelineStage {
        def transform[A <: StageOutput, B <: StageOutput](in: A): B
    }

    class PipelineStage2 extends PipelineStage {
        def transform(in: StageOutput1): StageOutput2
    }

    class Pipeline {
        def stages: Seq[PipelineStage]
    }

Here the Pipeline has no trouble compiling, but the stages won't compile since the signatures of their transform methods, whilst they 'respect' the abstract signature's polymorphism, aren't actually polymorphic themselves so don't match up as far as the compiler is concerned.

Scenario 2. Make the PipelineStage trait itself polymorphic.

    abstract trait PipelineStage[A <: StageOutput, B <: StageOutput] {
        def transform(in: A): B
    }

    class PipelineStage2 extends PipelineStage[StageOutput1, StageOutput2] {
        def transform(in: StageOutput1): StageOutput2
    }

    class Pipeline {
        def stages: Seq[PipelineStage]
    }

This solves the problem for the PipelineStages, they have no trouble compiling, and their transform methods work fine on their own. However, they don't technically implement the same trait now, so Pipeline doesn't compile since Seq[PipelineStage] is meaningless now...

Is there an established pattern for either implementing polymorphic abstract methods without the polymorphism, or referencing a sequence of classes implementing the same abstract polymorphic trait? My feeling is no and that I've probably approached this in the wrong way, but perhaps there's a syntactical trick I'm missing somewhere along the line. Thanks.

Upvotes: 2

Views: 145

Answers (1)

Alexey Romanov
Alexey Romanov

Reputation: 170723

The first one doesn't do what you want at all: StageOutput1 and 2 in def transform[StageOutput1, StageOutput2](in: StageOutput1): StageOutput2 are just parameter names, it means exactly the same as def transform[A, B](in: A): B.

My suggestion would be to use the second, but instead of class Pipeline you can add

def compose[C <: StageOutput](other: PipelineStage[B, C]): PipelineStage[A, C] = new PipelineStage[A, C] {
  def transform(in: A) = other.transform(PipelineStage.this.transform(in))
}

and build up the pipeline you want, provided the types actually match.

(Also, your PipelineStage is basically a Function1 with a constraint; if you don't plan to add other methods, or to intentionally limit the kinds of stages that can be written, you may want to just use functions and take advantage of the standard library!)

Upvotes: 4

Related Questions