Techvenience

Technology × Convenience - Vue / React / Next / Nuxt / ChatGPTなどのIT技術がもたらす便利さをお伝えします。最近はChatGPTなどのAI技術を使ってブログを書いています。

【エラー】Too many elements for Tuple: xx, allowed: 22【scala】

【エラー】Too many elements for Tuple: xx, allowed: 22【scala】

f:id:duo-taro100:20160218004611p:plain

Scalaでは関数やTupleに渡せる要素が最大で22となっているため、22以上の要素を渡した場合にこのようなエラーが出ます。
Scala 3では「渡せる要素が最大で22」の制限は撤廃されているようです。(未確認)

scalapedia.com

問題となった実装

object TestDao {
  class TargetTable(tag: Tag) extends Table[TestData](tag, "test") {
    def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
    def a = column[Long]("a")
    def b = column[String]("b")
    def c = column[String]("c")
    def d = column[Boolean]("d")
    def e = column[Boolean]("e")
    def f = column[Long]("f")
    def g = column[Long]("g")
    def h = column[Long]("h")
    def i = column[Option[Long]]("i")
    def j = column[Option[Long]]("j")
    def k = column[Option[Long]]("k")
    def l = column[Option[Long]]("l")
    def m = column[Option[Long]]("m")
    def n = column[Option[Long]]("n")
    def o = column[Option[Long]]("o")
    def p = column[Option[String]]("p")
    def q = column[Option[String]]("q")
    def updateUser = column[String]("update_user")
    def updateDate = column[Timestamp]("update_date")
    def inputUser = column[String]("input_user")
    def inputDate = column[Timestamp]("input_date")
    def activeFlag = column[String]("active_flag")

    def * = (
    	id, a, b, c, d, e,f, g, h, 
    	i, j, k, l, m, n, o, p, q,
    	updateUser, updateDate, inputUser, inputDate, activeFlag) <> (TestData.tupled, TestData.unapply)
  }

ここではidとa〜q、そしてupdateUser, updateDate, inputUser, inputDate, activeFlagの計23の要素を渡して、TestData.tupledとしています。
なので前述のエラーが発生します。

解決策

この問題の解決策としてよく提示されているものとしてHListsを使うというものがありました。
underscore.io

以下のような感じにするみたいです。

object TestDao {
  class TargetTable(tag: Tag) extends Table[TestData](tag, "test") {
    def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
    def a = column[Long]("a")
    def b = column[String]("b")
    def c = column[String]("c")
    def d = column[Boolean]("d")
    def e = column[Boolean]("e")
    def f = column[Long]("f")
    def g = column[Long]("g")
    def h = column[Long]("h")
    def i = column[Option[Long]]("i")
    def j = column[Option[Long]]("j")
    def k = column[Option[Long]]("k")
    def l = column[Option[Long]]("l")
    def m = column[Option[Long]]("m")
    def n = column[Option[Long]]("n")
    def o = column[Option[Long]]("o")
    def p = column[Option[String]]("p")
    def q = column[Option[String]]("q")
    def updateUser = column[String]("update_user")
    def updateDate = column[Timestamp]("update_date")
    def inputUser = column[String]("input_user")
    def inputDate = column[Timestamp]("input_date")
    def activeFlag = column[String]("active_flag")

    def * = id :: a :: b :: c :: d :: 省略 :: activeFlag
  }

ですが、この方法では自分はうまく動かなかったので別の方法を探ってみました。
結論としては「shaped」を使う以下の方法で実装しました。
本来CommonDataは別ファイルで定義していますが、ここでは1ファイルで完結できるようにcase classで定義しています。
TestDataは面倒なので省略しました。

case class CommonData
(
  var updateUser: String,
  var updateDate: Timestamp,
  var inputUser: String,
  var inputDate: Timestamp,
  var activeFlag: String
)


object TestDao {
  class TargetTable(tag: Tag) extends Table[TestData](tag, "test") {
    def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
    def a = column[Long]("a")
    def b = column[String]("b")
    def c = column[String]("c")
    def d = column[Boolean]("d")
    def e = column[Boolean]("e")
    def f = column[Long]("f")
    def g = column[Long]("g")
    def h = column[Long]("h")
    def i = column[Option[Long]]("i")
    def j = column[Option[Long]]("j")
    def k = column[Option[Long]]("k")
    def l = column[Option[Long]]("l")
    def m = column[Option[Long]]("m")
    def n = column[Option[Long]]("n")
    def o = column[Option[Long]]("o")
    def p = column[Option[String]]("p")
    def q = column[Option[String]]("q")
    def updateUser = column[String]("update_user")
    def updateDate = column[Timestamp]("update_date")
    def inputUser = column[String]("input_user")
    def inputDate = column[Timestamp]("input_date")
    def activeFlag = column[String]("active_flag")

    private val shapedValue = (
      id, a, b, c, d, e,
      f, g, h,
      i, j, k, l, m, n, o,p, q,
      (updateUser, updateDate, inputUser, inputDate, activeFlag)
    ).shaped

    def * = shapedValue <> ( 
    	{
	      case (
	        id, a, b, c, d, e,
	        f, g, h,
	        i, j, k, l, m, n, o,p, q,
	        commonData
	        ) =>
	        TestData(
	          id, a, b, c, d, e,
	          f, g, h,
	          i, j, k, l, m, n, o,p, q,
	          CommonData.tupled.apply(commonData)
	        )
	    }, { u: TestData =>
	      Some(
	        (
	          u.id,
	          u.a,
	          u.b,
	          u.c,
	          u.d,
	          u.e,
	          u.f,
	          u.g,
	          u.h,
	          u.i,
	          u.j,
	          u.k,
	          u.l,
	          u.m,
	          u.n,
	          u.o,
	          u.p,
	          u.q,
	          CommonData.unapply(u.commonData).get
	        )
	      )
	    }
    )
  }

なんとかできました。