Scalaz(6)- typeclass:Functor-just map

 
Functor是范畴学(Category theory)里的定义。不过不用担忧,大家在scala
FP编制程序里并不需求先明白范畴学知识的。在scalaz里,Functor正是贰个普通的typeclass,具备map
over性子。笔者的掌握中,Functor的主要用途是在FP进度中更新包嵌在容器(高阶类)F[T]兰秋素T值。标准事比方:List[String],
Option[Int]等。大家曾经介绍过FP与OOP的里边一项优异分化在于FP会尽量制止中间变量(temp
variables)。FP的变量V是以F[V]这种样式存在的,如:List[Int]里贰个Int变量是包嵌在容器List里的。所以FP须求独特的秘籍来更新变量V,那正是Functor
map over的情趣。scalaz提供了Functor typeclass不但使客户能map
over自定义的高阶类型F[T],并且客户通过提供自定义类型的Functor实例就足防止费应用scalaz
Functor typeclass提供的一多元组件函数(combinator functions)。

 
scalaz中Functor的trait是这么定义的:scalaz/Functor.scala

1 trait Functor[F[_]] extends InvariantFunctor[F] { self =>
2   ////
3   import Liskov.<~<
4 
5   /** Lift `f` into `F` and apply to `F[A]`. */
6   def map[A, B](fa: F[A])(f: A => B): F[B]
7 
8 ...

另外类型的实例只须求完结那么些抽象函数map就足以应用scalaz
Functor
typeclass的这一个注入方法了:scalaz/syntax/FunctorSyntax.scala

 1 final class FunctorOps[F[_],A] private[syntax](val self: F[A])(implicit val F: Functor[F]) extends Ops[F[A]] {
 2   ////
 3   import Leibniz.===
 4   import Liskov.<~<
 5 
 6   final def map[B](f: A => B): F[B] = F.map(self)(f)
 7   final def distribute[G[_], B](f: A => G[B])(implicit D: Distributive[G]): G[F[B]] = D.distribute(self)(f)
 8   final def cosequence[G[_], B](implicit ev: A === G[B], D: Distributive[G]): G[F[B]] = D.distribute(self)(ev(_))
 9   final def cotraverse[G[_], B, C](f: F[B] => C)(implicit ev: A === G[B], D: Distributive[G]): G[C] = D.map(cosequence)(f)
10   final def ∘[B](f: A => B): F[B] = F.map(self)(f)
11   final def strengthL[B](b: B): F[(B, A)] = F.strengthL(b, self)
12   final def strengthR[B](b: B): F[(A, B)] = F.strengthR(self, b)
13   final def fpair: F[(A, A)] = F.fpair(self)
14   final def fproduct[B](f: A => B): F[(A, B)] = F.fproduct(self)(f)
15   final def void: F[Unit] = F.void(self)
16   final def fpoint[G[_]: Applicative]: F[G[A]] = F.map(self)(a => Applicative[G].point(a))
17   final def >|[B](b: => B): F[B] = F.map(self)(_ => b)
18   final def as[B](b: => B): F[B] = F.map(self)(_ => b)
19   final def widen[B](implicit ev: A <~< B): F[B] = F.widen(self)
20   ////
21 }

如上的注入方法中除了map外其余情势的行使场景笔者还一贯不确切的主张,不过那不会妨碍大家演示它们的用法。Functor必得依照一些定律:

1、map(fa)(x => x)
=== fa

2、map(map(fa)(f1))(f2) ===
map(fa)(f2 compose f1)

scalaz/Functor.scala

 1   trait FunctorLaw extends InvariantFunctorLaw {
 2     /** The identity function, lifted, is a no-op. map(fa)(x => x*/
 3     def identity[A](fa: F[A])(implicit FA: Equal[F[A]]): Boolean = FA.equal(map(fa)(x => x), fa)
 4 
 5     /**
 6      * A series of maps may be freely rewritten as a single map on a
 7      * composed function.
 8      */
 9     def composite[A, B, C](fa: F[A], f1: A => B, f2: B => C)(implicit FC: Equal[F[C]]): Boolean = FC.equal(map(map(fa)(f1))(f2), map(fa)(f2 compose f1))
10   }

大家得以用List来证实:map(fa)(x
=> x) === fa

1 scala> List(1,2,3).map(x => x) assert_=== List(1,2,3)
2 
3 scala> List(1,2,3).map(identity) assert_=== List(1,2)
4 java.lang.RuntimeException: [1,2,3] ≠ [1,2]
5   at scala.sys.package$.error(package.scala:27)
6   at scalaz.syntax.EqualOps.assert_$eq$eq$eq(EqualSyntax.scala:16)
7   ... 43 elided

map(map(fa)(f1))(f2)
=== map(fa)(f2 compose f1)

1 scala> Functor[List].map(List(1,2,3).map(i => i + 1))(i2 => i2 * 3) assert_=== List(1,2,3).map(((i2:Int) => i2 * 3) compose ((i:Int) => i + 1))
2 
3 scala> Functor[List].map(List(1,2,3).map(i => i + 1))(i2 => i2 * 3) assert_=== List(1,2,3).map(((i:Int) => i + 1) compose ((i2:Int) => i2 * 3))
4 java.lang.RuntimeException: [6,9,12] ≠ [4,7,10]
5   at scala.sys.package$.error(package.scala:27)
6   at scalaz.syntax.EqualOps.assert_$eq$eq$eq(EqualSyntax.scala:16)
7   ... 43 elided

注意:compose对f1,f2的应用是沟通的。

针对大家自定义的项目,大家只要完结map函数就能够收获这些类其余Functor实例。一旦落到实处了那些类型的Functor实例,我们就能够使用上述scalaz提供的有着Functor组件函数了。

大家先试着成立二个门类然后推算它的Functor实例:

1 case class Item3[A](i1: A, i2: A, i3: A)
2 val item3Functor = new Functor[Item3] {
3     def map[A,B](ia: Item3[A])(f: A => B): Item3[B] = Item3(f(ia.i1),f(ia.i2),f(ia.i3))
4 }                                                 //> item3Functor  : scalaz.Functor[scalaz.functor.Item3] = scalaz.functor$$anonf
5                                                   //| un$main$1$$anon$1@5e265ba4

scalaz同有的时候间在scalaz-tests下提供了一套scalacheck测量试验库。大家能够对Item3的Functor实例实行测量试验:

1 scala> functor.laws[Item3].check
2 <console>:27: error: could not find implicit value for parameter af: org.scalacheck.Arbitrary[Item3[Int]]
3               functor.laws[Item3].check
4                           ^

总的来讲大家要求提供自定义类型Item3的肆意产生器(Generator):

 1 scala> implicit def item3Arbi[A](implicit a: Arbitrary[A]): Arbitrary[Item3[A]] = Arbitrary {
 2      | def genItem3: Gen[Item3[A]]  = for {
 3      | b <- Arbitrary.arbitrary[A]
 4      | c <- Arbitrary.arbitrary[A]
 5      | d <- Arbitrary.arbitrary[A]
 6      | } yield Item3(b,c,d)
 7      | genItem3
 8      | }
 9 item3Arbi: [A](implicit a: org.scalacheck.Arbitrary[A])org.scalacheck.Arbitrary[Item3[A]]
10 
11 scala> functor.laws[Item3].check
12 + functor.invariantFunctor.identity: OK, passed 100 tests.
13 + functor.invariantFunctor.composite: OK, passed 100 tests.
14 + functor.identity: OK, passed 100 tests.
15 + functor.composite: OK, passed 100 tests.

Item3的Functor实例是说的有道理的。

实际上map就是(A =>
B) => (F[A] => F[B]),就是把(A => B)升格(lift)成(F[A]
=> F[B]):

 1 case class Item3[A](i1: A, i2: A, i3: A)
 2 implicit val item3Functor = new Functor[Item3] {
 3     def map[A,B](ia: Item3[A])(f: A => B): Item3[B] = Item3(f(ia.i1),f(ia.i2),f(ia.i3))
 4 }                                                 //> item3Functor  : scalaz.Functor[scalaz.functor.Item3] = scalaz.functor$$anonf
 5                                                   //| un$main$1$$anon$1@5e265ba4
 6 val F = Functor[Item3]                            //> F  : scalaz.Functor[scalaz.functor.Item3] = scalaz.functor$$anonfun$main$1$$
 7                                                   //| anon$1@5e265ba4
 8 F.map(Item3("Morning","Noon","Night"))(_.length)  //> res0: scalaz.functor.Item3[Int] = Item3(7,4,5)
 9 F.apply(Item3("Morning","Noon","Night"))(_.length)//> res1: scalaz.functor.Item3[Int] = Item3(7,4,5)
10 F(Item3("Morning","Noon","Night"))(_.length)      //> res2: scalaz.functor.Item3[Int] = Item3(7,4,5)
11 F.lift((s: String) => s.length)(Item3("Morning","Noon","Night"))
12                                                   //> res3: scalaz.functor.Item3[Int] = Item3(7,4,5)

虽说函数升格(function
lifting (A => B) => (F[A] =>
F[B])是Functor的器重作用,但大家说过:一旦能够获得Item3类型的Functor实例大家就能够无需付费应用具有的流入方法:

scalaz提供了Function1的Functor实例。Function1
Functor的map便是 andThen 相当于操作方沟通的compose:

 1 scala> (((_: Int) + 1) map((k: Int) => k * 3))(2)
 2 res20: Int = 9
 3 
 4 scala> (((_: Int) + 1) map((_: Int) * 3))(2)
 5 res21: Int = 9
 6 
 7 scala> (((_: Int) + 1) andThen ((_: Int) * 3))(2)
 8 res22: Int = 9
 9 
10 scala> (((_: Int) * 3) compose ((_: Int) + 1))(2)
11 res23: Int = 9

小编们也足以对Functor实行compose:

1 scala> val f = Functor[List] compose Functor[Item3]
2 f: scalaz.Functor[[α]List[Item3[α]]] = scalaz.Functor$$anon$1@647ce8fd
3 
4 scala> val item3 = Item3("Morning","Noon","Night")
5 item3: Item3[String] = Item3(Morning,Noon,Night)
6 
7 scala> f.map(List(item3,item3))(_.length)
8 res25: List[Item3[Int]] = List(Item3(7,4,5), Item3(7,4,5))

扭动操作:

1 scala> val f1 = Functor[Item3] compose Functor[List]
2 f1: scalaz.Functor[[α]Item3[List[α]]] = scalaz.Functor$$anon$1@5b6a0166
3 
4 scala> f1.map(Item3(List("1"),List("22"),List("333")))(_.length)
5 res26: Item3[List[Int]] = Item3(List(1),List(2),List(3))

咱俩再试着在Item3类型上调用这几个无需付费的流入方法:

 1 scala> item3.fpair
 2 res28: Item3[(String, String)] = Item3((Morning,Morning),(Noon,Noon),(Night,Night))
 3 
 4 scala> item3.strengthL(3)
 5 res29: Item3[(Int, String)] = Item3((3,Morning),(3,Noon),(3,Night))
 6 
 7 scala> item3.strengthR(3)
 8 res30: Item3[(String, Int)] = Item3((Morning,3),(Noon,3),(Night,3))
 9 
10 scala> item3.fproduct(_.length)
11 res31: Item3[(String, Int)] = Item3((Morning,7),(Noon,4),(Night,5))
12 
13 scala> item3 as "Day"
14 res32: Item3[String] = Item3(Day,Day,Day)
15 
16 scala> item3 >| "Day"
17 res33: Item3[String] = Item3(Day,Day,Day)
18 
19 scala> item3.void
20 res34: Item3[Unit] = Item3((),(),())

自壬戌来还未有想到这一个函数的求实用处。但是从运算结果来看,用这么些函数来发出一些数据模型用在娱乐或许测验的上行下效(simulation)倒是大概的。

scalaz提供了多数现有的Functor实例。大家先看看一些大约直接的实例:

 1 scala> Functor[List].map(List(1,2,3))(_ + 3)
 2 res35: List[Int] = List(4, 5, 6)
 3 
 4 scala> Functor[Option].map(Some(3))(_ + 3)
 5 res36: Option[Int] = Some(6)
 6 
 7 scala> Functor[java.util.concurrent.Callable]
 8 res37: scalaz.Functor[java.util.concurrent.Callable] = scalaz.std.java.util.concurrent.CallableInstances$$anon$1@4176ab89
 9 
10 scala> Functor[Stream]
11 res38: scalaz.Functor[Stream] = scalaz.std.StreamInstances$$anon$1@4f5374b9
12 
13 scala> Functor[Vector]
14 res39: scalaz.Functor[Vector] = scalaz.std.IndexedSeqSubInstances$$anon$1@4367920a

对那么些三个类型变量的项目大家能够运用部分使用情势:即type
lambda来代表。三个超人的花色:Either[E,A],大家能够把Left[E]固化下来:
Either[String, A],咱们能够用type lambda来如此表述:

1 scala> Functor[({type l[x] = Either[String,x]})#l].map(Right(3))(_ + 3)
2 res41: scala.util.Either[String,Int] = Right(6)

与上述同类作者能够对Either类型进行map操作了。

函数类型的Functor是针对再次回到类型的:

1 scala> Functor[({type l[x] = String => x})#l].map((s: String) => s + "!")(_.length)("Hello")
2 res53: Int = 6
3 
4 scala> Functor[({type l[x] = (String,Int) => x})#l].map((s: String, i: Int) => s.length + i)(_ * 10)("Hello",5)
5 res54: Int = 100
6 
7 scala> Functor[({type l[x] = (String,Int,Boolean) => x})#l].map((s: String,i: Int, b: Boolean)=> s + i.toString + b.toString)(_.toUpperCase)("Hello",3,true)
8 res56: String = HELLO3TRUE

tuple类型的Functor是针对性最终一个要素类型的: 

 

1 cala> Functor[({type l[x] = (String,x)})#l].map(("a",1))(_ + 2)
2 res57: (String, Int) = (a,3)
3 
4 scala> Functor[({type l[x] = (String,Int,x)})#l].map(("a",1,"b"))(_.toUpperCase)
5 res58: (String, Int, String) = (a,1,B)
6 
7 scala> Functor[({type l[x] = (String,Int,Boolean,x)})#l].map(("a",1,true,Item3("a","b","c")))(i => i.map(_.toUpperCase))
8 res62: (String, Int, Boolean, Item3[String]) = (a,1,true,Item3(A,B,C))