Cartesian 

Functors, Applicative Functors and Monoids:

ここまではファンクター値を写すために、もっぱら 1 引数関数を使ってきました。では、2 引数関数でファンクターを写すと何が起こるでしょう?

scala> import cats._, cats.data._, cats.implicits._
import cats._
import cats.data._
import cats.implicits._

scala> val hs = Functor[List].map(List(1, 2, 3, 4)) ({(_: Int) * (_:Int)}.curried)
hs: List[Int => Int] = List(<function1>, <function1>, <function1>, <function1>)

scala> Functor[List].map(hs) {_(9)}
res6: List[Int] = List(9, 18, 27, 36)

LYAHFGG:

では、ファンクター値 Just (3 *) とファンクター値 Just 5 があったとして、 Just (3 *) から関数を取り出して Just 5 の中身に適用したくなったとしたらどうしましょう?

Control.Applicative モジュールにある型クラス Applicative に会いに行きましょう!型クラス Applicative は、2つの関数 pure<*> を定義しています。

Cats はこれを CartesianApplyApplicative に分けている。以下が Cartesian のコントラクト:

/**
 * [[Cartesian]] captures the idea of composing independent effectful values.
 * It is of particular interest when taken together with [[Functor]] - where [[Functor]]
 * captures the idea of applying a unary pure function to an effectful value,
 * calling `product` with `map` allows one to apply a function of arbitrary arity to multiple
 * independent effectful values.
 *
 * That same idea is also manifested in the form of [[Apply]], and indeed [[Apply]] extends both
 * [[Cartesian]] and [[Functor]] to illustrate this.
 */
@typeclass trait Cartesian[F[_]] {
  def product[A, B](fa: F[A], fb: F[B]): F[(A, B)]
}

Cartesian は product 関数を定義して、これは F[A]F[B] から、効果 F[_] に包まれたペア (A, B) を作る。product のシンボリックなエイリアスは |@| で、これは applicative style とも呼ばれる。

Option syntax 

次にへ行く前に、Optiona 値を作るために Cats が導入する syntax をみてみる。

scala> 9.some
res7: Option[Int] = Some(9)

scala> none[Int]
res8: Option[Int] = None

これで (Some(9): Option[Int])9.some と書ける。

Applicative Style 

LYAHFGG:

Applicative 型クラスでは、<*> を連続して使うことができ、 1つだけでなく、複数のアプリカティブ値を組み合わせて使うことができます。

以下は Haskell で書かれた例:

ghci> pure (-) <*> Just 3 <*> Just 5
Just (-2)

Cats には CartesianBuilder 構文というものがある。

scala> (3.some |@| 5.some) map { _ - _ }
res9: Option[Int] = Some(-2)

scala> (none[Int] |@| 5.some) map { _ - _ }
res10: Option[Int] = None

scala> (3.some |@| none[Int]) map { _ - _ }
res11: Option[Int] = None

これは Option から Cartesian が形成可能であることを示す。

Cartesian としての List 

LYAHFGG:

リスト(正確に言えばリスト型のコンストラクタ [])もアプリカティブファンクターです。意外ですか?

CartesianBuilder 構文で書けるかためしてみよう:

scala> (List("ha", "heh", "hmm") |@| List("?", "!", ".")) map {_ + _}
res12: List[String] = List(ha?, ha!, ha., heh?, heh!, heh., hmm?, hmm!, hmm.)

> と < 演算子 

Cartesian<**> という 2つの演算子を可能とし、 これらも Apply[F].product の特殊形だと考えることができる:

abstract class CartesianOps[F[_], A] extends Cartesian.Ops[F, A] {
  def |@|[B](fb: F[B]): CartesianBuilder[F]#CartesianBuilder2[A, B] =
    new CartesianBuilder[F] |@| self |@| fb

  def *>[B](fb: F[B])(implicit F: Functor[F]): F[B] = F.map(typeClassInstance.product(self, fb)) { case (a, b) => b }

  def <*[B](fb: F[B])(implicit F: Functor[F]): F[A] = F.map(typeClassInstance.product(self, fb)) { case (a, b) => a }
}

定義はシンプルに見えるけども、面白い効果がある:

scala> 1.some <* 2.some
res13: Option[Int] = Some(1)

scala> none[Int] <* 2.some
res14: Option[Int] = None

scala> 1.some *> 2.some
res15: Option[Int] = Some(2)

scala> none[Int] *> 2.some
res16: Option[Int] = None

どちらか一方が失敗すると、None が返ってくる。

Cartesian 則 

Cartesian には結合則という法則が1つのみある:

trait CartesianLaws[F[_]] {
  implicit def F: Cartesian[F]

  def cartesianAssociativity[A, B, C](fa: F[A], fb: F[B], fc: F[C]): (F[(A, (B, C))], F[((A, B), C)]) =
    (F.product(fa, F.product(fb, fc)), F.product(F.product(fa, fb), fc))
}

Contents