Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala: How to inherit the same trait twice?

I'm following along in Odersky's "Programming in Scala" 2nd edition, and in section 12.5 "Traits as stackable modifications", he presents an IntQueue along with a trait that doubles any values you insert into the queue:

import scala.collection.mutable.ArrayBuffer
abstract class IntQueue {
 def get(): Int
 def put(x: Int)
}

class BasicIntQueue extends IntQueue {
 private val buf = new ArrayBuffer[Int]
 def get() = buf.remove(0)
 def put(x: Int) { buf += x }
}

trait Doubling extends IntQueue {
 abstract override def put(x: Int) {
  super.put(2 * x)
 }
}

The book then shows that you can instantiate a queue which doubles every integer you insert into it via new BasicIntQueue with Doubling. What I wanted to do was created a similar queue which multiplies every integer by 4, like this: new BasicIntQueue with Doubling with Doubling. However, this triggers a compile error "trait Doubling is inherited twice". Looking into this, I guess this has something to do with the limitations of linearlization; specifically that a given trait cannot appear twice in the linearlization of a class hierarchy.

What's the best way, then, to achieve the effect I want?

Here's a bit more information on my "real world" use case, in case the answer depends on this:

I have a class SoundFile, which reads a .wav file, and yields a SoundFile object, which extends a WaveForm trait. The SoundFile class is analogous to the BasicIntQueue above, and the WaveForm trait is analogous to the IntQueue above.

I have 2 traits that are analogous to Doubling, one called Echo and one called Reverse.

I wanted to write new SoundFile("myFile.wav") with Reverse with Echo with Reverse, but I ran into that same compile error about inheriting from the Reverse trait twice.

like image 786
Nebu Pookins Avatar asked Oct 26 '25 07:10

Nebu Pookins


1 Answers

Unfortunately you can't inherit from the same trait twice. Instead you should use some other mechanism. For instance, Reverse and Echo are both manipulations of the waveform. You could have

val reverse = (w: Waveform) => // produce a reverse waveform
val echo =    (w: Waveform) => // copy the waveform onto itself with delay
new SoundFile("myFile.wav", reverse andThen echo andThen reverse)

or somesuch.

If you require more changes than just a simple function, you'll have to encapsulate the modifications to functionality in your own class:

trait Transform { self =>
  def apply(w: Waveform): Waveform
  def time: Double
  def andThen(t: Transform) = new Transform {
    def apply(w: Waveform) = t(self(w))
    def time = self.time + t.time
  }
}
val reverse = new Transform { def time = 0.0; def apply ... }
val echo    = new Transform { def time = 1.0; def apply ... }
// Same deal after here
like image 91
Rex Kerr Avatar answered Oct 28 '25 23:10

Rex Kerr



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!