1. ホーム
  2. scala

[解決済み] Scalaのタイプラムダとその利点とは?

2022-04-27 19:05:55

質問

という半信半疑の表記でつまずくことがある。

def f[T](..) = new T[({type l[A]=SomeType[A,..]})#l] {..} 

Scala のブログ記事で、"quot; we used that type-lambda trick" handwave "と書かれています。

これについては直感的に理解できるのですが(無名型パラメータを獲得する A しかし、このタイプ・ラムダ・トリックがどのようなもので、どのような利点があるのかを説明している明確なソースは見つかりませんでした。単なる構文上の工夫なのか、それとも新しい次元を切り開くものなのか。

どのように解決するのですか?

タイプラムダは、高次の型を扱うときにかなり重要です。

Either[A, B]の右射影のためのモナドを定義する簡単な例を考えてみよう。モナドの型クラスは次のようなものである。

trait Monad[M[_]] {
  def point[A](a: A): M[A]
  def bind[A, B](m: M[A])(f: A => M[B]): M[B]
}

さて、Eitherは2つの引数のタイプ・コンストラクタですが、Monadを実装するには、1つの引数のタイプ・コンストラクタを与える必要があります。これを解決するには、タイプ・ラムダを使うことだ。

class EitherMonad[A] extends Monad[({type λ[α] = Either[A, α]})#λ] {
  def point[B](b: B): Either[A, B]
  def bind[B, C](m: Either[A, B])(f: B => Either[A, C]): Either[A, C]
}

EitherMonadのインスタンスを生成するときに、どちらかの型を指定する必要があります。

型ラムダのトリックは、型の位置に空のブロックがあると匿名の構造型が生成されることを利用する。そして、#構文を使って型メンバを取得する。

場合によっては、インラインで書き出すのが面倒な、より洗練されたタイプ・ラムダが必要になるかもしれません。以下は、今日の私のコードからの例です。

// types X and E are defined in an enclosing scope
private[iteratee] class FG[F[_[_], _], G[_]] {
  type FGA[A] = F[G, A]
  type IterateeM[A] = IterateeT[X, E, FGA, A] 
}

このクラスは、FG[F, G]#IterateeM のような名前を使って、IterateeT モナドの型を、第3のモナドに特化した第2のモナドの変換バージョンに特化して参照できるようにするためだけに存在する。スタックを始めると、この種の構成は非常に必要になります。もちろん、私はFGをインスタンス化することはありません。FGは、私が望むものを型システムで表現するためのハックとして存在するだけです。