1

I m trying to declare function in trait that takes variable number of argument and during implementation of the trait I would expand the number of arguments. How can this done in Scala

I am expecting to come up with code like below.

trait Column {
  def rule
}

case object FirstColumn extends Column{
 def rule(s: String) : String
}

case object SecondColumn extends Column{
 def rule(s1: String, s2: String) : String
}

I have tried using Strings* , but it is not allowing me to expand my number of arguments during implementation. I understand there are various way to handle this problem but i am specifically looking to have above signature for my team to write functions.

6
  • 1
    What "above signature"? You have three methods with three different signatures. Commented Mar 25, 2019 at 15:50
  • 1
    If you declare rule to take variable arguments in the trait, then you're promising that the implementation works for any number of arguments, not just some specific implementation-dependent number of arguments. So a FirstColumn which provides a 1-ary version of the function does not correctly implement the trait. Commented Mar 25, 2019 at 15:50
  • 2
    To put it another way, if I have a Column, how would I call rule on it? If I pass one arg and it turns out to be a SecondColumn, that's an error. And vice versa if I pass two and it's a FirstColumn. So if you have a Column then you can't do anything type-safe with it, which defeats the point of it even being a trait. Commented Mar 25, 2019 at 15:51
  • 1
    Thanks Silvio.. I think type safe will address my problem.. I was more thinking to have like "any class that implements this trait should always have function named "rule" and let the user specify the arguments they wish" but that defeats the purpose of trait and I will never be able to call it with "Column".. Thanks for the comment Commented Mar 25, 2019 at 16:01
  • 1
    Thanks Luis.. That was clear.. Commented Mar 25, 2019 at 16:05

1 Answer 1

2

This is primarily expanding on my comment on the question. This answer gets you about as close as Scala lets you get to what you want, but it also shows why it's probably not a good idea to do what you're doing.

You can express (something close to) the type you want, but I'm not sure what you intend to gain. First, if you want to take different arglist types, then Column needs to be generic.

trait Column[-A] {
  def rule(arg: A): String
}

Then we can implement your case objects as subclasses of an appropriate parameterization of this.

case object FirstColumn extends Column[String] {
  def rule(arg: String): String =
    "stub implementation"
}

case object SecondColumn extends Column[(String, String)] {
  def rule(arg: (String, String)): String =
    "stub implementation"
}

Note that FirstColumn and SecondColumn do not inherit from the same Column[A] as they don't implement the same method. We can get them to have a common type, but... not in a very useful way.

One option is to find a common supertype of Column[String] and Column[(String, String)], which (since the argument is contravariant) is akin to finding a common subtype of String and (String, String). The closest common subtype is... Null. That's not helpful unless you're only ever planning to pass null to your rule.

Instead, we can use existentials.

val foo: Column[_] = FirstColumn
val bar: Column[_] = SecondColumn

Now we've lost all type information. You can access the foo.rule slot and you can print it, but you can't call it because we don't know what we need to pass it. You'll have to do a cast to get it back to a usable format.

The point that I'm making here is that, yes, it's doable, but once you've lost as much type information as you're giving up, there's not much point. The type system is correctly telling us that foo and bar have virtually nothing in common except the existence of a method named rule which takes... some kind of argument. From a type theory perspective, it's hard to get more uninteresting than that.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks Silvio.. This Helps.. My use case was iterate all case objects and get the run method and pass it as high order function to Spark UDF. My intention is to create a generic function which can take n number of arguments and return string.. I will pass that function as HO function. Please let me know if you have any thoughts on that and your response was very helpful to me.. Thanks..
@NareshKumarSundaramKrishnam So basically, you have a DF with an unknown number of String columns, and you want to do some row-centric operation on it?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.