zoukankan      html  css  js  c++  java
  • 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))

     

     

  • 相关阅读:
    leetcode108 Convert Sorted Array to Binary Search Tree
    leetcode98 Validate Binary Search Tree
    leetcode103 Binary Tree Zigzag Level Order Traversal
    leetcode116 Populating Next Right Pointers in Each Node
    Python全栈之路Day15
    Python全栈之路Day11
    集群监控
    Python全栈之路Day10
    自动部署反向代理、web、nfs
    5.Scss的插值
  • 原文地址:https://www.cnblogs.com/tiger-xc/p/4840597.html
Copyright © 2011-2022 走看看