Runner in the High

技術のことをかくこころみ

Scalaでは無名関数は型パラメータをとれない

こんなコードを書いた

object App {
  type Value[A] = Seq[(A, A)]
  
  sealed trait Status {
    def map[A](f: Value[A] => Value[A]) =
      this match {
        case Valid(value) => Valid(f(value))
        case Invalid() => Invalid()
      }
  }
  
  case class Valid[A](value: Value[A]) extends Status
  case class Invalid() extends Status
}

このコードをコンパイルすると、7行目の関数引数fを適用する部分で以下のエラーになる。

type mismatch;
 found   : Playground.App.Value[Any]
    (which expands to)  Seq[(Any, Any)]
 required: Playground.App.Value[A]
    (which expands to)  Seq[(A, A)]

これはパターンマッチにおけるValid型のAの型が決まらないからなので、mapメソッドが所属しているStatusトレイトそのものをStatus[A]みたいな形でジェネリックにすれば解決する。

一方で「なぜ無名関数をジェネリックにできないのか」は少しおもしろいトピックだと思う。これに関しては、このStackoverflowの回答でScalaの言語仕様と共に詳しく説明されていた。 stackoverflow.com

つまり、無名関数がジェネリックになれない(型パラメータをとれない)のは「メソッド」と「関数」の違いに由来するもの。

Scalaにおいてメソッドはジェネリックになれるが、関数は内部ではFunctionNとして表現されるため定義時点で型が決まりジェネリックにはなれない。したがって、無名関数もジェネリックになれないということのようだ。

「じゃあ他の言語はどうなんだ」というハナシになるが、例えばTypeScriptだと無名関数もこんな感じでジェネリックにできる。

const foo = <T>(x: T) => x;