Magnolia Tech

いつもコードのことばかり考えている人のために。

Scala3学習メモ: Scala3におけるType Lambda

Scala2におけるType Lambdaについては、下記のブログエントリが詳しい。

underscore.io

最初に、型パラメータの数を一つとして定義していると、型パラメータを二つ取る物を渡せない。

scala> trait Functor[F[_]]
trait Functor

scala> type F1 = Functor[Option]
type F1

scala> type F2 = Functor[List]
type F2

scala> type F3 = Functor[Map]
                         ^
       error: Map takes 2 type parameters, expected: 1

Mapが取る二つの型のうち、一つが決まれば、残るは一つなので大丈夫。

scala> type IntKeyMap[A] = Map[Int, A]
type IntKeyMap

scala> type F3 = Functor[IntKeyMap]
type F3

ただし、いちいち途中の階層の型を定義するのは面倒くさい。

そこで、無名の型を定義して、その中で一つの型を確定させ、その型を返す方法が発明された。これなら1行で済むし、余計な命名も不要になる。

scala> type F5 = Functor[({ type T[A] = Map[Int, A] })#T]
type F5

でも何をやっているのかわかりづらい。

そこで、Scala3には専用の構文(=>>)が導入された。だいぶ謎記号が減ったScalaだけど、ここに来て一つ増えた。

scala> type F6 = Functor[ [A] =>> Map[Int, A] ]
// defined alias type F6 = Functor[[A] =>> Map[Int, A]]

Scala2の時のtype lambdaの書き方をすると、対応が分かりやすい。

scala> type F7 = Functor[({ type T[A] = Map[Int, A] })#T]
// defined alias type F7 = Functor[[A] =>> Map[Int, A]]

型のカリー化もできるそうだ。

type TL = [X] =>> [Y] =>> (X, Y)

型パラメータが得られば、こんな指定もできる。

scala> def foo[A[_,_],B](functor: Functor[({type AB[C] = A[B,C]})#AB]) = ???
def foo[A[_$3, _$4], B](functor: Functor[[C] =>> A[B, C]]): Nothing

scala> def foo[A[_,_],B](functor: Functor[ [C] =>> A[B, C]]) = ???
def foo[A[_$1, _$2], B](functor: Functor[[C] =>> A[B, C]]): Nothing

ライブラリを作る人でなければ、このような汎用性のための機能はいらないかもしれないけど。

あと、これらのコードを動かしている最中に、aliasの挙動がよく分からなくなったけど、良い記事を見つけた。

blog.shibayu36.org