case class にコンパニオンオブジェクトを定義すると、case classのtupledメソッドが使えなくなる？

# 事象

最近コンパニオンオブジェクトを少し使うようになってきたのだが、少しつまづいた。case classにコンパニオンオブジェクトを定義すると、どうもtupledメソッドが使えなくなるのだ。

コンパニオンオブジェクトを定義した、case classを使って、Slickのテーブル定義を書いていた時に気づいた

普段だと下記のように書く

```scala
case class User(id: Int, name: String)

class UserTable(tag: Tag) extends Table[User](tag, "User") {
  val id = column[Int]("id")
  val name = column[String]("name")
  def * : ProvenShape[User] = (id, name) <> (User.tupled, User.unapply)
}
```

case class をデータの入れ物くらいに扱っていて、特に問題も起きなかった。

今回は少し、メソッドをつけておきたかったので、下記のように書いた

```scala
case class User(id: Int, name: String)

object User {
  def method = println("todo")
}

class UserTable(tag: Tag) extends Table[User](tag, "User") {
  val id = column[Int]("id")
  val name = column[String]("name")
  def * : ProvenShape[User] = (id, name) <> (User.tupled, User.unapply) // value tupled is not a member of object User
```

~~となり、どうも `object User` が優先？されて、case class によって生成されるメソッドが使えなくなっていそうだ。~~ → コンパニオンオブジェクトがない場合にcase classによって自動的に生成されるコンパニオンオブジェクトが、明示的に定義したことによって自動的に使えなくなっている感じみたい（解決策4参照）
※細かいところは分からない。


`unapply` や `copy`などのメソッドは特に消えておらず使えるようなので、観測範囲だと`tupled` だけの模様

# 解決策

### 1. tupled メソッドを自前で定義する

参考

* [Using .tupled method when companion object is in class](https://stackoverflow.com/questions/22367092/using-tupled-method-when-companion-object-is-in-class)

```scala
case class User(id: Int, name: String)

object User {
  def tupled = (this.apply _).tupled
}
```

### 2. object の中に case class を移動する

参考 ※というわけでもないが、一応下記を見た

* [case classはコンパニオンオブジェクトのなかで定義すべき理由](http://scalang.hatenablog.com/entry/2015/10/01/195256)

コンパニオンオブジェクトが参照されて、メソッドが使えなくなるならば、中で定義すればいいのでは？という感じで

```scala
object User {
  case class User(id: Int, name: String)

  def method = println("todo")
}
``` 

これで使うところで

```scala
import models.User.User

...(色々)
class UserTable(tag: Tag) extends Table[User](tag, "User") {
  val id = column[Int]("id")
  val name = column[String]("name")
  def * : ProvenShape[User] = (id, name) <> (User.tupled, User.unapply)
}
```

としてあげれば、`object User` の中の `case class User` を使うことになるので、通常の場合と同じように利用できる。

### 3. 1で使っているやつをそのまま、Slickのテーブル定義に使う

Slickのテーブル定義で使いたいだけなので、わざわざメソッド化せずに、

```scala
class UserTable(tag: Tag) extends Table[User](tag, "User") {
  val id = column[Int]("id")
  val name = column[String]("name")
  def * : ProvenShape[User] = (id, name) <> ((User.apply _).tupled, User.unapply)
}
```

と書いてもいいなと思った。

---
### ※4(追記) FunctionNをobjectにミックスインする

Twitterで下記の指摘をいただいた。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">tupledはFunctionNのメソッドですね。case class定義時に明示的にコンパニオンオブジェクトを定義しない場合、自動で作られるコンパニオンオブジェクトはFunctionNトレイトをmixinしています<br>なのでこの場合<br>class UserTable(..) extends Table[User](..) with ((Int, String) =&gt; User)<br>とすればOKです</p>&mdash; がくぞ (@gakuzzzz) <a href="https://twitter.com/gakuzzzz/status/1301729587525685248?ref_src=twsrc%5Etfw">September 4, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

case class を定義した時に、コンパニオンオブジェクトを定義しない場合に自動的に作られる、コンパニオンオブジェクトにFunctionNがミックスインされているから `tupled` 使えるんだよとのこと。

明示的に書く場合は、自前でFunctionNをミックスインする必要がありそう。

というわけで下記のように書いてみた。

```scala
case class User(id: Int, name: String)

object User extends Function2[Int, String, User] {...}
// もしくは
object User extends ((Int, String) => User) {...} // こっちがおすすめらしい（Function2で書くとIntelliJに警告される）
```

これが一番自然な気がしますね。

がくぞさん、ありがとうございましたm(_ _)m

---


# 今回採用したもの

今回は「1. tupled メソッドを自前で定義する」を採用した。

普段Slickでテーブル定義する際はtupledでマッピングの部分を記述しているので統一したいというのが一番理由。

3は結構ありだと思った。現状使いたいのは、Slickのテーブル定義のところのみなので、1のようにいちいち定義するのも無駄かなと思ったためだ。

2は形としては良く使われると、参考の記事には記載があったが、標準的な書き方ではないかなと思い（実際は分からない）、あまり使いたくない。
インナークラスを使いたい場合にこのような書き方をするらしいが、今のところ、インナークラスを使いたいと思う場面もなく、メリット、デメリットを把握し切れていないので、今回は利用しないことにした。