Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Tupled method for case class having a type parameter

When having a case class with a type parameter, I don't see how I can call the tupled method. It seems to be find with apply and unapply.

scala> case class Foo[T](x:T, y:T)
defined class Foo

scala> Foo.apply[Int] _
res1: (Int, Int) => Foo[Int] = <function2>

scala> Foo.unapply[Int] _
res2: Foo[Int] => Option[(Int, Int)] = <function1>

scala> Foo.tupled[Int] _
<console>:10: error: value tupled is not a member of object Foo
              Foo.tupled[Int] _
              ^

Any idea on what's going on?

like image 711
n1r3 Avatar asked Aug 17 '14 00:08

n1r3


1 Answers

tl;dr

Companion objects of case classes cannot extend FunctionN (which defines tupled, with N >= 2) when they have type parameters. Use

(Foo[Int] _).tupled

discussion

When you have a vanilla class such as

case class Bar(x: Int, y: Int)

its constructor is effectively a Function2[Int, Int, Bar], hence when the compiler generates the companion object Bar it can conveniently make it extend Function2.

The generated code will then be

class Bar extends AnyRef with Product with Serializable { ... }
object Bar extends Function2[Int, Int, Bar] with Serializable { ... }

Now consider

case class Foo[T](x: T, y: T)

If you try to apply the same trick, you'll find yourself in trouble very soon:

// this can't compile, what's T?
object Foo extends Function2[T, T, Bar] with Serializable { ... }

Since T is unknown, the compiler can't make Foo a subclass of Function2 and it can't do much better than make it extend AnyRef:

class Foo[T] extends AnyRef with Product with Serializable { ... }
object Foo extends AnyRef with Serializable { ... }

Here's a quick proof of what discussed above (using scala -Xprint:typer):

scala> case class Bar(x: Int, y: Int)
...
<synthetic> object Bar extends scala.runtime.AbstractFunction2[Int,Int,Bar] with Serializable {
...

scala> case class Foo[T](x: T, y: T)
...
<synthetic> object Foo extends AnyRef with Serializable {
...

To wrap it up, when you have type parameters, you have to obtain a Function2 first

val f: Function2[Int, Int, Foo] = Foo[Int] _

and then you can call tupled on it

f.tupled // ((Int, Int)) => Foo[Int]
like image 78
Gabriele Petronella Avatar answered Oct 12 '22 01:10

Gabriele Petronella