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?
Named Arguments allow you to call a function without passing the arguments in the order as they are defined in the function. In a normal function call, we need to pass the arguments with reference to the function's parameters.
A default argument is a value provided in a function declaration that is automatically assigned by the compiler if the calling function doesn't provide a value for the argument. In case any value is passed, the default value is overridden.
A default argument is a value in the function declaration automatically assigned by the compiler if the calling function does not pass any value to that argument.
You can assign a default value for a parameter using an equal to symbol. I gave a default value for the first parameter. If the caller doesn't pass any value for the first parameter, Scala will take the default value as println.
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()
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With