この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
[小ネタ] 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だと意外と実装するものが多くて時間がかかりました。