score:1

Accepted answer

The shortest, simplest way.

Define a case class:

case class Options(
  option1: Int,
  option2: String
  /* ... */
) extends Opts

and implicit conversion from Opts to your Options

object OptsConverter {

  implicit def toOptions(opts: Opts) = Options(
    option1 = opts.option1,
    option2 = opts.option2
    /* ... */
  )
}

That way you get all copy methods (generated by compiler) for free. You can use it like that:

import OptsConverter.toOptions

def usage(opts: Opts) = {
  val improvedOpts = opts.copy(option2 = "improved")
  /* ... */
}

Note, that Options extends Opts, so you can use it whenever Opts is required. You'll be able to call copy to obtain a modified instance of Opts in every place where you import the implicit conversion.

score:1

The simplest solution is to allow the trait to define it's own "copy" method, and allow it's subclasses (or even base class) to work with that. However, the parameters can really only match the base class unless you recast it later. Incidentally this doesn't work as "mixed in" so your root might as well be an abstract class, but it works the same way. The point of this is that the subclass type keeps getting passed along as it's copied.

(Sorry I typed this without a compiler so it may need some work)

    trait A {

    type myType<:A

    def option1: Int
    def option2: String

    def copyA(option1_:Int=option1, option2_String=option2):myType = new A {
      def option1 = option_1
      def option2 = option_2
    }

}

    trait B extends A { me=>
      type myType = B
      def option3: Double

    //callable from A but properly returns B

      override def copyA(option1_:Int=option1, option2_:String=option2):myType = new B {
          def option1 = option_1
          def option2 = option_2
          def option3 = me.option3            
        }   



//this is only callable if you've cast to type B 
           def copyB(option1_:Int=option1, option2_:String=option2, option3_:Double=option3):myType = new B {
              def option1 = option_1
              def option2 = option_2 
              def option3 = option_3         
            }


        }

score:2

I would first check if using the Opts trait (solely) is an absolute necessity. Hopefully it's not and then you just extend the trait, overriding defs with vars, like you said.

When Opts is mandatory and you have its instance that you want to copy modifying some fields, here's what you could do:

Write a wrapper for Opts, which extends Opts, but delegates every call to the wrapped Opts excluding the fields that you want to modify. Set those fields to values you want. Writing the wrapper for a broad-interface trait can be boring task, therefore you may consider using http://www.warski.org/blog/2013/09/automatic-generation-of-delegate-methods-with-macro-annotations/ to let macros generate most of it automatically.


Related Query

More Query from same tag