Scalaで既存クラスに対し再帰を利用したメソッドを定義したい

投稿者: Anonymous

ScalaのSeqクラスに対し、以下のように新しいメソッドを追加しようと考えております。

implicit def convert(xs:Seq[Int]) = new {
  def mmap(g: Int => Int): Seq[Int] = {
    xs match {
      case m if m.isEmpty => Seq()
      case xs: Seq[Int] => g(xs.head) +: (xs.tail).mmap(g)
    }
  }
}

println((1 to 5).mmap((x => x * 2)))

再帰が含まれているのが原因だと思われますが、以下のようなエラーになりました。

recursion.scala:5: error: value mmap is not a member of Seq[Int]
Note: implicit method convert is not applicable here because it comes after the application point and it lacks an explicit result type
case xs: Seq[Int] => g(xs.head) +: (xs.tail).mmap(g)
^
one error found

このエラーを回避し、再帰を利用したメソッドを新規追加する方法をご教示いただけますでしょうか。

どうぞよろしくお願いいたします。

解決

まず、正解の一つを示すと以下です。

implicit def convert(xs:Seq[Int]): {def mmap(g: Int => Int): Seq[Int]} = new{
  def mmap(g: Int => Int): Seq[Int] = {
    xs match {
      case m if m.isEmpty => Seq()
      case xs: Seq[Int] => g(xs.head) +: xs.tail.mmap(g)
    }
  }
}

エラーメッセージに it lacks an explicit result type とあるように、convertの戻り値型を明示的に書く必要があります。それは、convertが再帰的だからです。
単純な再帰ではなく、convertmmapの2つが関連した再帰ですが、いずれにせよ
「再帰してるメソッドの場合は戻り値型を明示しないといけない」
というのは、現状のScala言語の仕様です。

ここから先は、少し余談的な話になります。
正解の一つ、といいましたが、コンパイル自体は通って上記で一応動きますが、このままでは以下のようにいくつかの問題があり

  • structural subtypingを使っている(内部実装にリフレクションを使うので遅いという短所がある)
  • 再帰が末尾再帰になっていないのでstack overflowの可能性がある

前者の問題点の解決としてはimplicit classを利用、もしくは、多少面倒でもきちんとclass定義とimplicit def を分けて記述、などをしたほうがよいと思います。
末尾再帰については、詳細は触れません(が、ググれば簡単に出てくると思います)

回答者: Anonymous

Leave a Reply

Your email address will not be published. Required fields are marked *