11

I have a simple class and a companion object with apply methods overloaded:

case class A(val s: String,
         val x: Int,
         val y: Int,
         val z: Int,
         val foo: Int => Int,
         val l: Option[List[String]])

object A {
    def apply(s: String, x: Int, y: Int, z: Int, foo: Int => Int) =
        new A(s, x, y, z, foo, None)

    def apply(s: String, x: Int, y: Int, z: Int, foo: Int => Int, l: List[String]) =
        new A(s, x, y, z, foo, Some(l))
}

Let's also define a function:

def foo(x: Int): Int = x + 1

Using the first constructor works:

scala> val a1 = A("a1", 1, 2, 3, foo)
a1: A = A(a1,1,2,3,$$Lambda$1842/2112068307@598e02f0,None)

However using the second does not:

val a2 = A("a1", 1, 2, 3, foo, List("b1", "b2"))
<console>:24: error: missing argument list for method foo
Unapplied methods are only converted to functions when a function type is expected.
You can make this conversion explicit by writing `foo _` or `foo(_)` instead of `foo`.
       val a2 = A("a1", 1, 2, 3, foo, List("b1", "b2"))

The question(s): What is the reason I need to pass foo _ or foo(_) instead of just foo as in the a1 example? Also, can I redefine my class to make it possible to just use foo?

toni057
  • 582
  • 1
  • 4
  • 10
  • 1
    I wrap it up in `Some(l)` part in the body of the second `apply`. – toni057 Jan 18 '18 at 12:13
  • 1
    Sorry I missed that and deleted my old comment when I realised – airudah Jan 18 '18 at 12:39
  • 1
    @toni057 I managed to dig out a question that describes the same problem: https://stackoverflow.com/questions/17324247/eta-expansion-between-methods-and-functions-with-overloaded-methods-in-scala (you just have `apply`s instead of `r`s) – slouc Jan 18 '18 at 22:14

2 Answers2

2

I cannot explain this. My first thought was that it has something to do with type erasure, as slouc said, but then I looked at the signatures more closely, and neither of them have the same type signature even after the erasure:

Case class apply:

String, Int, Int, Int, Function1, Option

Companion object apply 1:

String, Int, Int, Int, Function1

Companion object apply 2:

String, Int, Int, Int, Function1, List

So, I don't see how that could be a case.

Trying to make it work I've noticed that it will work fine if you rename the second apply method to something else:

scala> val a1 = A("a1", 1, 2, 3, foo)
a1: A = A(a1,1,2,3,$$Lambda$1286/1904652802@6b649efa,None)

scala> val a2 = A.apply2("a1", 1, 2, 3, foo, List("b1", "b2"))
a2: A = A(a1,1,2,3,$$Lambda$1287/866073173@2dd63e3,Some(List(b1, b2)))

Or define both apply methods not in a companion object:

object B {

    def apply(s: String, x: Int, y: Int, z: Int, foo: Int => Int): A =
      new A(s, x, y, z, foo, None)

    def apply(s: String, x: Int, y: Int, z: Int, foo: Int => Int, l: List[String]): A =
      new A(s, x, y, z, foo, Some(l))
}

REPL:

scala> val a1 = B("a1", 1, 2, 3, foo)
a1: A = A(a1,1,2,3,$$Lambda$1351/814507488@50fa5938,None)

scala> val a2 = B("a1", 1, 2, 3, foo, List("b1", "b2"))
a2: A = A(a1,1,2,3,$$Lambda$1352/613231852@5d77be8e,Some(List(b1, b2)))

A third method, as Robert Udah mentioned, defining foo as a function literal (x: Int) => x + 1 also solves the issue. However, it doesn't have to be val, could be def, as well:

def foo = (x: Int) => x + 1
  • It's very peculiar why defining an object B helps to what I intended to do in the first place. Any clue why it doesn't work as a companion object? – toni057 Jan 18 '18 at 13:04
1

It works fine if foo is declared as a function val

scala> val foo = (x: Int) => x + 1
foo: Int => Int = <function1>

scala> val a1 = A("a1", 1, 2, 3, foo)
a1: A = A(a1,1,2,3,<function1>,None)

scala> val a2 = A("a1", 1, 2, 3, foo, List("b1", "b2"))
a2: A = A(a1,1,2,3,<function1>,Some(List(b1, b2)))

Why def foo works fine for a1 I'm not sure, but it might have something to do with case class A expecting a function instead of a method as these apparently are two different things in scala.

Here is a post that might explain better

airudah
  • 1,169
  • 12
  • 19
  • Just reading the post and it seems that it is the case that `A` expects a function instead of a method. Is there anyone who can corroborate more on this? – toni057 Jan 18 '18 at 13:14