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 はこれを Cartesian
、Apply
、 Applicative
に分けている。以下が 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 とも呼ばれる。
次にへ行く前に、Optiona
値を作るために Cats が導入する syntax をみてみる。
scala> 9.some
res7: Option[Int] = Some(9)
scala> none[Int]
res8: Option[Int] = None
これで (Some(9): Option[Int])
を 9.some
と書ける。
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
が形成可能であることを示す。
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
には結合則という法則が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))
}