7

I have a case class that stores three tied parameters. I'd like to define companion object that may build the class from any two parameters, something that looks like the sample below, that is obviously incorrect:

def test(start : Float = end - duration, duration : Float = end - start, end : Float = start + duration) {
  require( abs(start + duration - end) < epsilon )
  ...
}
val t1 = test(start = 0f, duration = 5f)
val t2 = test(end = 4f, duration = 3f)
val t3 = test(start = 3f, end = 5f)

What tricks I may use to get similar usage syntax?

ayvango
  • 5,867
  • 3
  • 34
  • 73

2 Answers2

26

You can use type-classes:

// Represents no argument
object NoArg

// Resolves start, duration, stop
trait DurationRes[A,B,C] {
  def resolve(s: A, d: B, e: C): (Float, Float, Float)
}

object DurationRes {
  implicit object startEndRes extends DurationRes[Float, NoArg.type, Float] {
    def resolve(s: Float, d: NoArg.type, e: Float) = (s, e-s, e)
  }
  implicit object startDurRes extends DurationRes[Float, Float, NoArg.type] {
    def resolve(s: Float, d: Float, e: NoArg.type) = (s, d, s+d)
  }
  // etc.
}

def test[A,B,C](start: A = NoArg, dur: B = NoArg, end: C = NoArg)
               (implicit res: DurationRes[A,B,C]) {
  val (s,d,e) = res.resolve(start, dur, end)
  // s is start, d duration, e end
}

test(start = 1f, end = 2f)

This way it is even type-safe and you cannot call something like:

test(start = 1f)

or even

test()
gzm0
  • 14,752
  • 1
  • 36
  • 64
3

After a little thinking I've come with another solution (I don't claim it's better, just would like to know if it's even acceptable approach). The essence is to define a class: class Klass(val x: Int, val y: Int, val z: Int) and a companion object:

object Klass {
  def apply(x: Int, y: Int)(z: Int = x + y) = {
    new Klass(x, y, z)
  }
  // and so on
}

So you can then do val k = Klass(x = 5, y = 6)() and get val k to refer to Klass(5, 6, 11) instance.

And because of small code amount one can probably define a macros to do the job, but that's a little difficult for me as for now, but is an interesting exercise though.

Update

After some time, I'd like to note to you that there are only three combinations of parameters in your case, so wouldn't it be just easier to provide 3 apply() methods by hand? apply(s, d), apply(s, e), apply(d, e) should suffice your needs. And that will save you some typing, because with other approaches you basically have to code all that cases, too.

tkroman
  • 4,811
  • 1
  • 26
  • 46
  • Note that this works only if your arguments have different types. You cannot overload `apply` like this due to Java-compatibility. (In Java the argument names are not part of the public interface). – gzm0 Sep 16 '13 at 12:14