Scalaz(6)- typeclass:Functor-just map

时间:2019-09-22 01:10来源: 操作系统
Functor是范畴学(Category theory)里的定义。可是不用顾虑,大家在scalaFP编制程序里并无需先精通范畴学知识的。在scalaz里,Functor便是二个习感觉常的typeclass,具有mapover本性。笔者的知情

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](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 7   final def distribute[G[_], B](f: A => G[B])(implicit D: Distributive[G]): G[F[B]] = D.distribute 8   final def cosequence[G[_], B](implicit ev: A === G[B], D: Distributive[G]): G[F[B]] = D.distribute 9   final def cotraverse[G[_], B, C](f: F[B] => C)(implicit ev: A === G[B], D: Distributive[G]): G[C] = D.map(cosequence)10   final def ∘[B](f: A => B): F[B] = F.map11   final def strengthL[B]: F[] = F.strengthL12   final def strengthR[B]: F[] = F.strengthR13   final def fpair: F[] = F.fpair14   final def fproduct[B](f: A => B): F[] = F.fproduct15   final def void: F[Unit] = F.void16   final def fpoint[G[_]: Applicative]: F[G[A]] = F.map(a => Applicative[G].point17   final def >|[B](b: => B): F[B] = F.map(_ => b)18   final def as[B](b: => B): F[B] = F.map(_ => b)19   final def widen[B](implicit ev: A <~< B): F[B] = F.widen20   ////21 }

如上的注入方法中除去map外别的格局的使用场景我还未曾确切的主张,不过那不会妨碍我们演示它们的用法。Functor必需比照一些定律:

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

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

scalaz/Functor.scala

 1   trait FunctorLaw extends InvariantFunctorLaw { 2     /** The identity function, lifted, is a no-op. map(x => x*/ 3     def identity[A](implicit FA: Equal[F[A]]): Boolean = FA.equal(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(f2 compose f1))10   }

我们能够用List来验证:map(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 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(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 * 3) compose  => 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 + 1) compose  => 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,f4 }                                                 //> item3Functor  : scalaz.Functor[scalaz.functor.Item3] = scalaz.functor$$anonf5                                                   //| un$main$1$$anon$1@5e265ba4

scalaz同有时间在scalaz-tests下提供了一套scalacheck测量检验库。我们得以对Item3的Functor实例实行测量试验:

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

总的来讲我们须求提供自定义类型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 7      | genItem3 8      | } 9 item3Arbi: [A](implicit a: org.scalacheck.Arbitrary[A])org.scalacheck.Arbitrary[Item3[A]]10 11 scala> functor.laws[Item3].check12 + 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)升格成(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,f 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"))  //> res0: scalaz.functor.Item3[Int] = Item3 9 F.apply(Item3("Morning","Noon","Night"))//> res1: scalaz.functor.Item3[Int] = Item310 F(Item3("Morning","Noon","Night"))      //> res2: scalaz.functor.Item3[Int] = Item311 F.lift((s: String) => s.length)(Item3("Morning","Noon","Night"))12                                                   //> res3: scalaz.functor.Item3[Int] = Item3

尽管函数升格(function lifting (A => B) => (F[A] => F[B])是Functor的首要成效,但大家说过:一旦可以赢得Item3类型的Functor实例我们就能够无偿使用全部的注入方法:

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

 1 scala>  + 1) map => k * 3))(2) 2 res20: Int = 9 3  4 scala>  + 1) map * 3))(2) 5 res21: Int = 9 6  7 scala>  + 1) andThen  * 3))(2) 8 res22: Int = 9 9 10 scala>  * 3) compose  + 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@647ce8fd3 4 scala> val item3 = Item3("Morning","Noon","Night")5 item3: Item3[String] = Item3(Morning,Noon,Night)6 7 scala> f.map(List(item3,item3))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@5b6a01663 4 scala> f1.map(Item3(List("1"),List("22"),List("333")))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.fproduct11 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.void20 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@4f5374b912 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 + "!")("Hello")2 res53: Int = 63 4 scala> Functor[({type l[x] = (String,Int) => x})#l].map((s: String, i: Int) => s.length + i)(_ * 10)("Hello",5)5 res54: Int = 1006 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] = })#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

编辑: 操作系统 本文来源:Scalaz(6)- typeclass:Functor-just map

关键词: