なぜ
scalaのライブラリを読んでると、F[_]
、G[F[_]]
のような記述が多くて、よく分からんかったので調べた。
ソース
以下の記事を自分なりにまとめただけなので、以下の記事をまずは参照されると。
https://qiita.com/KtheS/items/649fbffcef18c74430f8
valueとは
以下のような純粋な値のことをvalueという。
これが低階層の値である。
val name = "high kind"
val number = 1
proper type
先ほどの例であげると、nameのString
型や、numberのInt
型、List[String]
などをproper typeという。
先ほどのvalueよりも一段、抽象度が上がった表現である。
val name: String = "high kind"
val number: Int = 1
val listStr: List[String] = List("1", "2")
val listInt: List[Int] = List(1, 2)
first-order type
List, Option, Mapのような型をfirst-order typeという。
これらfirst-order type型は、1つ以上のスロットを持つ。(List, Optionなら1つ、Mapなら2つ)
List[_]
, Option[_]
, Map[_, _]
のことを、type constructorという。
val list: List[_] = List(1, 2)
val opt: Option[_] = Option(1)
val map: Map[_, _] = Map(a -> "1")
さらに一段と抽象度が上がった。
second-order type
F[_]
と型を抽象化した型をさらに抽象化したもの
下でいうと、WithMap
型コンストラクタ(F[_]
)を取る型コンストラクタ(WithMap[]
)ということで、WithMap[F[_]]
を高カインド型という・
trait WithMap[F[_]] {
}
F[_]の良い点
F[_]
は、1つのスロットを持つ型を抽象化したもの
これによって、メソッドなどを共通化できる。
trait WithMap[F[_]] {
def map[A, B](fa: F[A])(f: A => B): F[B]
def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B]
def OrElse[A, B](fa: F[B]): F[B]
def filter[A](fa: F[A])(f: A => Boolean): F[A]
}
上記例のようにメソッドを定義できる。
実態(Listなのか、Optionなのか)によらず、何個のパラメータをとる型なのかで、型を判断できる。
使ってみた
object TypeClassExercise {
def executeUnwrapped[F[_], A](value: F[A])(implicit objectUnwrapper: ObjectUnwrapper[F]): A = {
objectUnwrapper.unwrape(value)
}
// executeUnwrappedのimplicitの糖衣構文
def executeUnwrapped2[F[_]: ObjectUnwrapper, A](value: F[A]): A = {
implicitly[ObjectUnwrapper[F]].unwrape(value)
}
}
trait ObjectUnwrapper[F[_]] {
def unwrape[A](x: F[A]): A
}
object ObjectUnwrapper {
implicit def listUnwrapper = new ObjectUnwrapper[List] {
override def unwrape[A](list: List[A]): A = list.head
}
implicit def optionUnwrapper = new ObjectUnwrapper[Option] {
override def unwrape[A](option: Option[A]): A = option.get
}
}
object Run extends App {
val objectUnwrappedListResult = TypeClassExercise.executeUnwrapped(List(1, 2))
val objectUnwrappedOptionResult = TypeClassExercise.executeUnwrapped(Option(33))
println(objectUnwrappedListResult)
println(objectUnwrappedOptionResult)
}