[小ネタ] Functorの合成によるBiFunctorの構成
Category Theory for Programmersで紹介されていた既存のbifunctorと2つのfunctorからbifunctorを構成するサンプルをScalaで実装してみました。
[小ネタ] Functorの合成によるBiFunctorの構成
はじめに
前回に引き続きCategory Theory for Programmersで紹介されていた既存のbifunctorと2つのfunctorからbifunctorを構成するサンプルをScalaで実装してみました。
CTPでの実装
-- bfはbifunctor, fuおよびguはfunctor new type BiComp bf fu gu a b = BiComp (bf (fu a) (gu b)) instance (BiFunctor bu, Functor fu, Functor gu) => BiFunctor (BiComp bu fu gu) where bimap f1 f2 (BiComp x) = BiComp (bimap (fmap f1) (fmap f2) x)
Scalaでの実装
package example import cats.data.Const import cats.implicits._ import cats.laws.discipline.BifunctorTests import cats.{Bifunctor, Eq, Functor} import org.scalatest.funsuite.AnyFunSuiteLike import org.typelevel.discipline.scalatest.FunSuiteDiscipline import org.scalacheck.ScalacheckShapeless._ class BiCompTest extends AnyFunSuiteLike with org.scalatest.prop.Configuration with FunSuiteDiscipline { final case class BiComp[BF[_, _]: Bifunctor, FU[_]: Functor, GU[ _ ]: Functor, A, B]( bf: BF[FU[A], GU[B]] ) object BiComp { implicit def eq[BU[_, _], FU[_], GU[_], A: Eq, B: Eq] : Eq[BiComp[BU, FU, GU, A, B]] = Eq.allEqual implicit def bf[BF[_, _]: Bifunctor, FU[_]: Functor, GU[_]: Functor] : Bifunctor[BiComp[BF, FU, GU, *, *]] = new Bifunctor[BiComp[BF, FU, GU, *, *]] { override def bimap[A, B, C, D]( fab: BiComp[BF, FU, GU, A, B] )(f: A => C, g: B => D): BiComp[BF, FU, GU, C, D] = BiComp( Bifunctor[BF].bimap(fab.bf)( Functor[FU].fmap(_)(f), Functor[GU].fmap(_)(g) ) ) } } }
CTP中で言及されている内容にそってConst, Identity, Sum type(coproduct), Either( product) からなるBifunctorを構成してlaw tesingしてみます。
final case class Identity[A](a: A) object Identity { implicit def eq[A: Eq]: Eq[Identity[A]] = Eq.allEqual implicit val identityFunctor: Functor[Identity] = new Functor[Identity] { override def map[A, B](fa: Identity[A])(f: A => B): Identity[B] = Identity(f(fa.a)) } } import BiComp._ import Identity._ // Coproduct type SumBF[A, B] = BiComp[Either, Const[Unit, *], Identity, A, B] // Product type ProductBF[A, B] = BiComp[Tuple2, Option, List, A, B] // Law testing checkAll( "SumBF.BiFunctorTests", BifunctorTests[SumBF] .bifunctor[String, Long, Int, String, Long, Int] ) checkAll( "ProductBF.BiFunctorTests", BifunctorTests[ProductBF] .bifunctor[String, Long, Int, String, Long, Int] ) /* [info] BiCompTest: [info] - SumBF.BiFunctorTests.Bifunctor.Bifunctor Identity [info] - SumBF.BiFunctorTests.Bifunctor.Bifunctor associativity [info] - SumBF.BiFunctorTests.Bifunctor.Bifunctor leftMap Identity [info] - SumBF.BiFunctorTests.Bifunctor.Bifunctor leftMap associativity [info] - ProductBF.BiFunctorTests.Bifunctor.Bifunctor Identity [info] - ProductBF.BiFunctorTests.Bifunctor.Bifunctor associativity [info] - ProductBF.BiFunctorTests.Bifunctor.Bifunctor leftMap Identity [info] - ProductBF.BiFunctorTests.Bifunctor.Bifunctor leftMap associativity */
まとめ
Scalaだと意外と実装するものが多くて時間がかかりました。