by 壯衣
在之前的博文《Scala類型類的小應用之CSV Encoder》中有一段代碼:
implicit def listValEncoder[A <: AnyVal](implicit encoder: Encoder[A]): Encoder[List[A]] = new Encoder[List[A]] {
override def apply(a: List[A]): String = a.map(encoder.apply).mkString(",")
}
implicit def listRefEncoder[A <: AnyRef](implicit encoder: Encoder[A]): Encoder[List[A]] = new Encoder[List[A]] {
override def apply(a: List[A]): String = a.map(encoder.apply).mkString("\n")
}
這兩個方法分別提供了類型類實例:Encoder[List[A <: AnyVal]] 和Encoder[List[A <: AnyRef]],有了這兩個類型類實例就可以完成List[Int]和
List[(Int, Int, Int)]轉(zhuǎn)換成String:
scala> Encoder[List[Int]](List(1, 2, 3))
res1: String = 1,2,3
scala> Encoder[List[List[Int]]](List(List(1, 2, 3), List(4, 5, 6)))
res2: String =
1,2,3
4,5,6
那么我們想處理Vector[Int]類型時,該何如處理?當然我們可以構(gòu)建Vector[A <: AnyVal的Encoder實例:
implicit def vectorValEncoder[A <: AnyVal](implicit encoder: Encoder[A]): Encoder[Vector[A]] = new Encoder[Vector[A]] {
override def apply(a: Vector[A]): String = a.map(encoder.apply).mkString(",")
}
測試一下Enocder[Vector[A <: AnyVal]]:
scala> Encoder[Vector[Int]](Vector(1, 2, 3))
res0: String = 1,2,3
仔細對比listValEncoder和vectorValEncoder方法會發(fā)下兩者實現(xiàn)除了List和Vector類型有區(qū)別,其它的實現(xiàn)都是一樣的,那我們是否可以將兩個方法合并呢?熟悉Scala類型系統(tǒng)的話可以知道List和Vector都是繼承與Seq,那么可以用Seq來代替List和Vector,只需要實現(xiàn)Encoder[Seq[A <: AnyVal]]就好了:
implicit def seqValEncoder[A <: AnyVal](implicit encoder: Encoder[A]): Encoder[Seq[A]] = new Encoder[Seq[A]] {
override def apply(a: Seq[A]): String = a.map(encoder.apply).mkString(",")
}
讓我們來測試下Encoder[Vector[A]]和Encoder[List[A]]:
scala> Encoder[Vector[Int]](Vector(1, 2, 3))
<console>:16: error: could not find implicit value for evidence parameter of type org.forcestudy.csvz.Encoder[Vector[Int]]
Encoder[Vector[Int]](Vector(1, 2, 3))
^
scala> Encoder[List[Int]](List(1, 2, 3))
<console>:16: error: could not find implicit value for evidence parameter of type org.forcestudy.csvz.Encoder[List[Int]]
Encoder[List[Int]](List(1, 2, 3))
^
scala> Encoder[Seq[Int]](Seq(1, 2, 3))
res2: String = 1,2,3
可以看到Vector[Int]和List[Int]都轉(zhuǎn)換失敗了,只有Seq[Int]轉(zhuǎn)換成功,可見子類型多態(tài)(Subtype polymorphism)在這里是行不通的,就算可以如何處理Set[Int]或者Option[Int]?兩者可都不是Seq的子類。我們再看下listValEncoder和vectorValEncoder的實現(xiàn),發(fā)現(xiàn)兩者都是做了一個map操作把List[A]和Vector[A]轉(zhuǎn)換成List[String]和Vector[String],然后調(diào)用mkString方法把List[String]和Vector[String]轉(zhuǎn)換成String。可以看到這其中就是做了一個map轉(zhuǎn)換操作和一個mkString的折疊操作,這兩個操作能否抽象成類型類呢?當然是可以,我們先抽象一個轉(zhuǎn)換操作的類型類:
trait Functor[F[_]] {
def map[A, B](fa: F[A])(f: A => B): F[B]
}
object Functor {
def apply[F[_]: Functor]: Functor[F] = implicitly[Functor[F]]
}
再來抽象一個折疊操作的類型類:
trait Foldable[F[_]] {
def foldRight[A, B](fa: F[A], b: B)(f: (A, B) => B): B
def foldLeft[A, B](fa: F[A], b: B)(f: (B, A) => B): B
}
object Foldable {
def apply[F[_]: Foldable]: Foldable[F] = implicitly[Foldable[F]]
}
好了現(xiàn)在我們可以去掉listValEncoder和vectorValEncoder方法了,利用Functor和Foldable可以寫一個更加通用的方法valEncoder來實現(xiàn)Encoder[List[A]]和Encoder[Vector[A]],甚至是Encoder[Set[A]]和Encoder[Option[A]]等類型, 來看下代碼:
implicit def valEncoder[F[_]: Functor: Foldable, A <: AnyVal: Encoder]: Encoder[F[A]] = new Encoder[F[A]] {
override def apply(a: F[A]): String = {
val fs = Functor[F].map(a)(implicitly[Encoder[A]].apply)
Foldable[F].foldLeft(fs, "") {
(a, b) => if (a.isEmpty) a + b else a + "," + b
}
}
}
好了, 我們來測試下Encoder[Vector[A]]:
scala> Encoder[Vector[Int]](Vector(1, 2, 3))
<console>:16: error: could not find implicit value for evidence parameter of type org.forcestudy.csvz.Encoder[Vector[Int]]
Encoder[Vector[Int]](Vector(1, 2, 3))
出現(xiàn)了編譯錯誤, 顯然我們沒有實現(xiàn)vector和list的Functor實例和Foldable實例,來看下如何實現(xiàn)
object Functor {
def apply[F[_]: Functor]: Functor[F] = implicitly[Functor[F]]
implicit val vectorFuncor: Functor[Vector] = new Functor[Vector] {
override def map[A, B](fa: Vector[A])(f: (A) => B): Vector[B] = fa.map(f)
}
implicit val listFuncor: Functor[List] = new Functor[List] {
override def map[A, B](fa: List[A])(f: (A) => B): List[B] = fa.map(f)
}
}
object Foldable {
def apply[F[_]: Foldable]: Foldable[F] = implicitly[Foldable[F]]
implicit val vectorFoldable: Foldable[Vector] = new Foldable[Vector] {
override def foldRight[A, B](fa: Vector[A], b: B)(f: (A, B) => B): B =
fa.foldRight(b)(f)
override def foldLeft[A, B](fa: Vector[A], b: B)(f: (B, A) => B): B =
fa.foldLeft(b)(f)
}
implicit val listFoldable: Foldable[List] = new Foldable[List] {
override def foldRight[A, B](fa: List[A], b: B)(f: (A, B) => B): B =
fa.foldRight(b)(f)
override def foldLeft[A, B](fa: List[A], b: B)(f: (B, A) => B): B =
fa.foldLeft(b)(f)
}
}
現(xiàn)在再來測試下Encoder[Vector[A]]和Encoder[List[A]]:
scala> Encoder[Vector[Int]](Vector(1, 2, 3))
res0: String = 1,2,3
scala> Encoder[List[Int]](List(1, 2, 3))
res1: String = 1,2,3
同理我們可以添加Functor[Set]、Functor[Option]和Foldable[Set]和Foldable[Option]的實例:
implicit val setFuncor: Functor[Set] = new Functor[Set] {
override def map[A, B](fa: Set[A])(f: (A) => B): Set[B] = fa.map(f)
}
implicit val optionFuncor: Functor[Option] = new Functor[Option] {
override def map[A, B](fa: Option[A])(f: (A) => B): Option[B] = fa.map(f)
}
implicit val setFoldable: Foldable[Set] = new Foldable[Set] {
override def foldRight[A, B](fa: Set[A], b: B)(f: (A, B) => B): B =
fa.foldRight(b)(f)
override def foldLeft[A, B](fa: Set[A], b: B)(f: (B, A) => B): B =
fa.foldLeft(b)(f)
}
implicit val optionFoldable: Foldable[Option] = new Foldable[Option] {
override def foldRight[A, B](fa: Option[A], b: B)(f: (A, B) => B): B =
fa.foldRight(b)(f)
override def foldLeft[A, B](fa: Option[A], b: B)(f: (B, A) => B): B =
fa.foldLeft(b)(f)
}
測試一下Encoder[Set[A]]和Encoder[Option[A]]:
scala> Encoder[Set[Int]](Set(1, 2, 3))
res4: String = 1,2,3
scala> Encoder[Option[Int]](Some(1))
res5: String = 1
上面針對的類型都是A <: AnyVal,對于類型A <: AnyRef可同樣抽象出一個通用的refEncoder方法,代碼如下:
implicit def refEncoder[F[_]: Functor: Foldable, A <: AnyRef: Encoder]: Encoder[F[A]] = new Encoder[F[A]] {
override def apply(a: F[A]): String = {
val fs = implicitly[Functor[F]].map(a)(implicitly[Encoder[A]].apply)
implicitly[Foldable[F]].foldLeft(fs, "") {
(a, b) => if (a.isEmpty) a + b else a + "\n" + b
}
}
}
這樣我們來測試下Encoder[Option[List[Int]]]和Encoder[Vector[Person]]:
scala> Encoder[Option[List[Int]]](Some(List(1, 2, 3)))
res6: String = 1,2,3
scala> Encoder[Vector[Person]](Vector(Person("zdx", 29, 145.0), Person("ygy", 28, 185.0)))
res8: String =
zdx,29,145.0
ygy,28,185.0
可見利用Functor和Foldable,我們可以寫出更加通用的方法,例如refEncoder方法既可以適用于List、Vector,也可以適用于Set和Option甚至更多的類型只要我們實現(xiàn)類型對應的Functor和Foldable的實例,其實這也是一種多態(tài)稱之為Ad-hoc polymorphism,區(qū)別于Subtype polymorphism。對于Decoder類型類我們也可以使用相同方案來抽象更為通用的代碼,這個在后面的博文會寫到。Functor、Foldable這些都是數(shù)學分支范疇論中的概念,Scala社區(qū)中的ScalaZ和Cats對這些typeclass都有做實現(xiàn),我后續(xù)博文也會展開介紹,敬請關注。