Search code examples
scalamixinstraits

Family Polymorphism + Mixins?


I have a FAMILY of types that I want to modularly "enrich" using mixins. For example:

trait Family {
  self =>
  trait Dog {
    def dogname:String
    def owner:self.Person
  }
  trait Person {
    def name:String
    def pet:self.Dog
  }
}

trait SerializableFamily extends Family {
  trait Dog extends super.Dog {
    def toSimpleString:String = "Dog(" + dogname + ")"
  }
  trait Person extends super.Person {
    def toSimpleString:String = "Person(" + name + ") and his pet " + pet.toSimpleString
  }
}

trait SerializableFamily2 extends Family {
  trait Dog extends super.Dog {
    def toLoudString:String = "Dog(" + dogname.toUpperCase + ")"
  }
  trait Person extends super.Person {
    def toLoudString:String = "Person(" + name.toUpperCase + ") and his pet " + pet.toLoudString
  }
}

However, the above does not work ( Scala 2.9.1 ). The last expression fails to compile ( pet.toSimpleString ).

This is just a random strategy I picked out of several I tried: self typing, abstract types, super[...], etc.

I want to be able to do something like this, eventually:

val family = new Family with SerializableFamily with TraversableFamily with FooFamily {}

Where each mixin adds a set of cooperating methods to one or more types within the family.

This is a common pattern that I have seen solved by using implicit wrappers, pattern-matching based visitors, etc. But since it is just a recursive application of the regular mixin pattern I wonder if there might be a simpler way to achieve it.


Solution

  • The error in your case is expected, since Dog and Person in mixins don't override Dog and Person in Family, so that self.Person still refers to Family.Person.

    This may be closer to what you want

    trait Family {
      // type DogType = Dog won't work because then two different mixins 
      // have incompatible DogType implementations
      type DogType <: Dog
      type PersonType <: Person
    
      trait Dog {
        def dogname:String
        def owner:PersonType 
      }
      trait Person {
        def name:String
        def pet:DogType 
      }
    }
    
    trait SerializableFamily extends Family {
      type DogType <: Dog
      type PersonType <: Person
    
      trait Dog extends super.Dog {
        def toSimpleString:String = "Dog(" + dogname + ")"
      }
      trait Person extends super.Person {
        def toSimpleString:String = "Person(" + name + ") and his pet " + pet.toSimpleString
      }
    }
    

    But then you have something yucky like

    new Family with SerializableFamily with TraversableFamily with FooFamily {
      type DogType = super[SerializableFamily].Dog with super[TraversableFamily].Dog with super[FooFamily].Dog
    }