Scalaタイプクラスの小さなアプリケーションのFunctor Foldable
7861 ワード
byジャンパー
以前のブログ「Scalaタイプクラスの小さなアプリケーションのCSV Encoder」には、次のコードがあります.
この2つの方法は、Encoder[List][:anyval="]anyref=""">List[(Int,Int,Int)]をStringに変換するタイプクラスインスタンスをそれぞれ提供する.
では、Vector[Int]タイプを処理したい場合、どうすればいいのでしょうか.もちろんVector[A<:anyval="""""">を構築することができます.
Enocder[Vector[A<:anyval="""">をテストします.
listValEncoderとvectorValEncoderの方法をよく比較すると、リストとVectorのタイプに違いがあるほか、他の実装も同じですが、2つの方法を統合することができますか?Scalaタイプのシステムを熟知していればListとVectorの両方がSeqに継承されていることが分かるので,ListとVectorの代わりにSeqを用いることができ,Encoder[Seq[A<:anyval="""""">を実現するだけでよい.
Encoder[Vector[A]]とEncoder[List[A]:
Vector[Int]もList[Int]も変換に失敗していることがわかりますが、Seq[Int]変換に成功しただけで、サブタイプマルチステート(Subtype polymorphism)はここでは通用しません.どちらもSeqのサブクラスではありません.listValEncoderとvectorValEncoderの実装を見てみると、どちらもmap操作をしてList[A]とVector[A]をList[String]とVector[String]に変換し、mkStringメソッドを呼び出してList[String]とVector[String]をStringに変換することが分かった.このうちmap変換操作とmkStringの折りたたみ操作が行われていることがわかりますが、この2つの操作はタイプクラスに抽象化できますか?もちろん、変換操作のタイプクラスを抽象化します.
折りたたみ操作のタイプクラスを抽象化します.
ではlistValEncoderとvectorValEncoderメソッドを削除し、FunctorとFoldableを使用して、Encoder[List[A]]とEncoder[Vector[A]、さらにEncoder[Set[A]]とEncoder[Option[A]などのタイプのより一般的なメソッドvalEncoderを書くことができます.コードを見てみましょう.
では、Encoder[Vector[A]]をテストします.
コンパイルエラーが発生しました.vectorとlistのFunctorインスタンスとFoldableインスタンスは実装されていません.実装方法を見てみましょう.
次に、Encoder[Vector[A]]とEncoder[List[A]:
同様に、Functor[Set]、Functor[Option]およびFoldable[Set]およびFoldable[Option]のインスタンスを追加できます.
Encoder[Set[A]]とEncoder[Option[A]:
上記のタイプはA<:anyval=""anyref="">
では、Encoder[Option[List[Int]]とEncoder[Vector[Person]]をテストします.
FunctorとFoldableを用いて,refEncoder法はList,VectorにもSet,Option,さらに多くのタイプにも適用できるが,タイプに対応するFunctorとFoldableのインスタンスを実装すれば,これもAd-hoc polymorphismと呼ばれ,Subtype polymorphismとは異なるマルチステートである.Decoderタイプクラスでは、同じスキームを使用して、より一般的なコードを抽象化することもできます.これは後述のブログで書かれています.Functor、Foldableこれらはすべて数学の分岐範疇論の中の概念で、Scalaコミュニティの中のScalaZとCatsはこれらのtypeclassに対してすべて実現して、私の後続の博文も紹介して、注目してください.
以前のブログ「Scalaタイプクラスの小さなアプリケーションのCSV Encoder」には、次のコードがあります.
implicit def listValEncoder[A <: anyval="" encoder:="" encoder="" new="" override="" def="" apply="" list="" string="a.map(encoder.apply).mkString(",")" implicit="" listrefencoder="" anyref=""/>
この2つの方法は、Encoder[List][:anyval="]anyref=""">List[(Int,Int,Int)]を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]タイプを処理したい場合、どうすればいいのでしょうか.もちろんVector[A<:anyval="""""">を構築することができます.
implicit def vectorValEncoder[A <: anyval="" encoder:="" encoder="" new="" override="" def="" apply="" vector="" 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の方法をよく比較すると、リストとVectorのタイプに違いがあるほか、他の実装も同じですが、2つの方法を統合することができますか?Scalaタイプのシステムを熟知していればListとVectorの両方がSeqに継承されていることが分かるので,ListとVectorの代わりにSeqを用いることができ,Encoder[Seq[A<:anyval="""""">を実現するだけでよい.
implicit def seqValEncoder[A <: anyval="" encoder:="" encoder="" new="" override="" def="" apply="" seq="" string="a.map(encoder.apply).mkString(",")"/>
Encoder[Vector[A]]とEncoder[List[A]:
scala> Encoder[Vector[Int]](Vector(1, 2, 3))
: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))
: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]も変換に失敗していることがわかりますが、Seq[Int]変換に成功しただけで、サブタイプマルチステート(Subtype polymorphism)はここでは通用しません.どちらもSeqのサブクラスではありません.listValEncoderとvectorValEncoderの実装を見てみると、どちらもmap操作をしてList[A]とVector[A]をList[String]とVector[String]に変換し、mkStringメソッドを呼び出してList[String]とVector[String]をStringに変換することが分かった.このうちmap変換操作とmkStringの折りたたみ操作が行われていることがわかりますが、この2つの操作はタイプクラスに抽象化できますか?もちろん、変換操作のタイプクラスを抽象化します.
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]]
}
ではlistValEncoderとvectorValEncoderメソッドを削除し、FunctorとFoldableを使用して、Encoder[List[A]]とEncoder[Vector[A]、さらにEncoder[Set[A]]とEncoder[Option[A]などのタイプのより一般的なメソッドvalEncoderを書くことができます.コードを見てみましょう.
implicit def valEncoder[F[_]: Functor: Foldable, A <: anyval:="" encoder="" new="" override="" def="" apply="" f="" string="{" val="" fs="Functor[F].map(a)(implicitly[Encoder[A]].apply)" foldable="" b=""> if (a.isEmpty) a + b else a + "," + b
}
}
}
では、Encoder[Vector[A]]をテストします.
scala> Encoder[Vector[Int]](Vector(1, 2, 3))
: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))
コンパイルエラーが発生しました.vectorとlistのFunctorインスタンスとFoldableインスタンスは実装されていません.実装方法を見てみましょう.
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)
}
}
次に、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=""anyref="">
implicit def refEncoder[F[_]: Functor: Foldable, A <: anyref:="" encoder="" new="" override="" def="" apply="" f="" string="{" val="" fs="implicitly[Functor[F]].map(a)(implicitly[Encoder[A]].apply)" implicitly="" b=""> if (a.isEmpty) a + b else a + "
" + 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,さらに多くのタイプにも適用できるが,タイプに対応するFunctorとFoldableのインスタンスを実装すれば,これもAd-hoc polymorphismと呼ばれ,Subtype polymorphismとは異なるマルチステートである.Decoderタイプクラスでは、同じスキームを使用して、より一般的なコードを抽象化することもできます.これは後述のブログで書かれています.Functor、Foldableこれらはすべて数学の分岐範疇論の中の概念で、Scalaコミュニティの中のScalaZとCatsはこれらのtypeclassに対してすべて実現して、私の後続の博文も紹介して、注目してください.