gwenzek
gwenzek

Reputation: 2944

Scala: add optionnal arguments to super method

I'm trying to implement a trait as follow: (1)

trait FooLike {
  def foo(x: Int): Int
}

class Foo extends FooLike {
  def foo(x: Int, y: Int = 0): Int = x + y
}

But the compiler complains that the method foo(x: Int): Int is not implemented.

I can do: (2)

class Foo extends FooLike {
  def foo(x: Int): Int = foo(x, 0)
  def foo(x: Int, y: Int = 0): Int = x + y
}

But it feels like Java, and I don't like it ! Is there a way to avoid this boilerplate ?

I thought that def foo(x: Int, y: Int = 0) would define two methods in the background but apparently it's not the case. What's actually happening ?

--- EDIT : more weirdness ---

Also the following is perfectly legit: (3)

class Foo extends FooLike {
  def foo(x: Int): Int = x - 1
  def foo(x: Int, y: Int = 0): Int = x + y
}

while it doesn't seems reasonable (foo(4) = 3 while foo(4, 0) = 4).

I think that authorizing (1) and forbidding (3) would have been the most reasonable choice, but instead they made the opposite choice. So why did Scala make those choices ?

Upvotes: 2

Views: 73

Answers (2)

Dan Getz
Dan Getz

Reputation: 9134

It is not possible to override a method of a different type signature in Scala by using default arguments. This is because of how default arguments are implemented. Default arguments are inserted when and where the method is applied, so there is only one version of the method being defined.

According to SID-1: Named and Default Arguments, when a method foo() with default arguments is compiled, only one method foo() is defined, which takes all the arguments. A call with default arguments like f.foo(xValue) is transformed at compile time into code equivalent to the following:

{
  val x = xValue
  val y = f.foo$default$2
  f.foo(x, y)
}

The foo$default$2 method is a hidden method which takes no arguments and returns the default value of argument #2 to the method foo().

So while you can write the same exact functional application foo(xValue) for a method foo(x: Int) or a method foo(x: Int, y: Int = 0), the methods being called "behind the scenes" do not have the same type signature.

Upvotes: 3

Ende Neu
Ende Neu

Reputation: 15783

You are extending a trait which has a non implemented method, you must implement it in the extending classes or you can implement it in the trait, if you don't need the foo with a single variable you can do:

trait FooLike {
  def foo(x: Int, y: Int): Int
}

class Foo extends FooLike {
  def foo(x: Int, y: Int = 0): Int = x + y
}

But I suppose you do and so you have to give the compiler an implementation for it, to compare this case to Java, it's like when you extend an abstract class and you don't implement a method, the compiler will complain that either you implement the method or you declare the class abstract.

One other approach which came to mind and is fairly reasonable is to implement the method in the trait so:

trait FooLike {
  def foo(x: Int): Int = x
}

class Foo extends FooLike {
  def foo(x: Int, y: Int = 0): Int = x + y
}

Then if you want to add a new class with the trait mixed in but with a different method implementation just override the method:

class AnotherFoo extends FooLike {
  override def foo(x: Int): Int = x + 1
}

Upvotes: 1

Related Questions