Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

define default named arguments in terms of other arguments in scala

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?

like image 655
ayvango Avatar asked Aug 12 '13 21:08

ayvango


People also ask

What is named arguments in Scala?

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.

What are default arguments where are the default arguments assigned?

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.

What are default arguments explain with example?

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.

How do I set default value in Scala?

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.


2 Answers

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()
like image 99
gzm0 Avatar answered Sep 21 '22 15:09

gzm0


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.

like image 28
tkroman Avatar answered Sep 23 '22 15:09

tkroman