Scalaタイプクラスの小さなアプリケーションのFunctor Foldable

7861 ワード

byジャンパー
以前のブログ「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に対してすべて実現して、私の後続の博文も紹介して、注目してください.