Scalaz(6)- typeclass:Functor-just mapScalaz(7)- typeclass:Applicative-idomatic function application

 
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)。

   
Applicative,正而它的名号所示,就是FP模式的函数施用(function
application)。我们以面前的座谈中不止提到FP模式之操作一般还当管道里开展的,因为FP的变量表达形式是这般的:F[A],即变量A是包嵌在F结构里的。Scalaz的Applicative
typeclass提供了各种类型的函数施用(function
application)和升级(lifting)方法。与另scalaz
typeclass使用方式相同,我们只是需要贯彻了针对于定义类型的Applicative实例就可下这些点子了。以下是Applicative
trait的一些概念:scalaz/Applicative.scala

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

1 trait Applicative[F[_]] extends Apply[F] { self =>
2   ////
3   def point[A](a: => A): F[A]
4 
5   // alias for point
6   final def pure[A](a: => A): F[A] = point(a)
7 。。。
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 ...

咱俩先是得贯彻抽象函数point,然后由Applicative继承了Apply,我们看看Apply
trait有什么抽象函数需要实现的;scalaz/Apply.scala

其余类型的实例只待贯彻这个抽象函数map就足以下scalaz
Functor
typeclass的这些注入方法了:scalaz/syntax/FunctorSyntax.scala

1 trait Apply[F[_]] extends Functor[F] { self =>
2   ////
3   def ap[A,B](fa: => F[A])(f: => F[A => B]): F[B]
4 。。。
 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 }

俺们尚待贯彻抽象函数ap。注意Apply又累了Functor,所以我们尚亟需贯彻map,一旦落实了Applicative实例就能够而且获得了Functor实例。

如上之流方法被除map外其它方式的利用场景我还并未确切的想法,不过就不见面伤我们演示它们的用法。Functor必须依照一些定律:

现行咱们先行规划一个自定义类型作为下的范例:

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

1 trait Configure[+A] {
2     def get: A
3 }
4 object Configure {
5     def apply[A](data: => A) = new Configure[A] { def get = data }
6 }
7 Configure("env string")                           //> res0: Exercises.ex4.Configure[String] = Exercises.ex4$Configure$$anon$1@6bf2
8                                                   //| d08e

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

Configure[+A]凡是只卓越的FP类型。通过实现特殊命名apply的函数作为项目构建器,我们得这么构建实例:Configure(“some
string”)。现在我们仍scalaz隐式解析(implicit
resolution)惯例在伴生对象(companion
object)里定义隐式Applicative实例:

scalaz/Functor.scala

 1 import scalaz._
 2 import Scalaz._
 3 object ex4 {
 4 trait Configure[+A] {
 5     def get: A
 6 }
 7 object Configure {
 8     def apply[A](data: => A) = new Configure[A] { def get = data }
 9     implicit val configFunctor = new Functor[Configure] {
10         def map[A,B](ca: Configure[A])(f: A => B): Configure[B] = Configure(f(ca.get))
11     }
12     implicit val configApplicative = new Applicative[Configure] {
13         def point[A](a: => A) = Configure(a)
14         def ap[A,B](ca: => Configure[A])(cfab: => Configure[A => B]): Configure[B] = cfab map {fab => fab(ca.get)}
15     }
16 }
 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   }

由Apply继承了Functor,我们不能不先行抱Configure的Functor实例。现在我们可本着Configure类型使用Applicative
typeclass的法力函数了。Applicative
typeclass的零部件函数可以分成几种重大品种:

俺们好为此List来验证:map(fa)(x
=> x) === fa

1、Applicative实例构建函数,point:

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
1 "abc".point[Configure]                            //> res1: Exercises.ex4.Configure[String] = Exercises.ex4$Configure$$anon$3@3c41
2                                                   //| 9631
3 12.point[Configure]                               //> res2: Exercises.ex4.Configure[Int] = Exercises.ex4$Configure$$anon$3@6302168
4                                                   //| 9
5 5.point[Option]                                   //> res3: Option[Int] = Some(5)

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

关押款式应是经隐式转换实现之:scalaz/syntax/ApplicativeSyntax.scala

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
 1 trait ToApplicativeOps extends ToApplicativeOps0 with ToApplyOps {
 2   implicit def ToApplicativeOps[F[_],A](v: F[A])(implicit F0: Applicative[F]) =
 3     new ApplicativeOps[F,A](v)
 4 
 5   ////
 6   implicit def ApplicativeIdV[A](v: => A) = new ApplicativeIdV[A] {
 7     lazy val self = v
 8   }
 9 
10   trait ApplicativeIdV[A] extends Ops[A] {
11     def point[F[_] : Applicative]: F[A] = Applicative[F].point(self)
12     def pure[F[_] : Applicative]: F[A] = Applicative[F].point(self)
13     def η[F[_] : Applicative]: F[A] = Applicative[F].point(self)
14   }  ////
15 }

瞩目:compose对f1,f2的利用是换的。

是通过implicit def
ApplicativeIDV[A](v: => A)实现的。

针对我们于定义之品种,我们而实现map函数就可得此类型的Functor实例。一旦实现了这个路的Functor实例,我们虽足以以上述scalaz提供的有所Functor组件函数了。

 

咱俩先试着创造一个门类然后推算其的Functor实例:

2、对F[T}类型进行F[A
=>B]典礼的函数施用(从管道里提供作用函数)。施用函数款式是这般的:

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
1   def ap[A,B](fa: => F[A])(f: => F[A => B]): F[B]

scalaz同时以scalaz-tests下提供了一致模拟scalacheck测试库。我们得针对Item3的Functor实例进行测试:

对比Functor函数map:map[A,B](fa:
F[A])(f: A => B]): F[B],
分别就于供操作函数A=>B的法子:ap在F结构中供,又要换句话说ap提供的凡高阶函数F[A=>B]。从函数款式看来,ap要较map功能更加强。因为咱们得以就此ap实现map,
反之不足:

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                           ^
1         def map[A,B](fa: Configure[A])(f: A => B) = ap(fa)(point(f))

总的看我们要提供由定义类型Item3的任意产生器(Generator):

通过ap2,ap3,ap4
…款式的函数我们得以将
F[A],F[B],F[C],F[D]…基本上独价值连接起来:scalaz/Apply.scala

 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.
1  def ap2[A,B,C](fa: => F[A], fb: => F[B])(f: F[(A,B) => C]): F[C] =
2     ap(fb)(ap(fa)(map(f)(_.curried)))
3   def ap3[A,B,C,D](fa: => F[A], fb: => F[B], fc: => F[C])(f: F[(A,B,C) => D]): F[D] =
4     ap(fc)(ap2(fa,fb)(map(f)(f => ((a:A,b:B) => (c:C) => f(a,b,c)))))
5   def ap4[A,B,C,D,E](fa: => F[A], fb: => F[B], fc: => F[C], fd: => F[D])(f: F[(A,B,C,D) => E]): F[E] =
6     ap2(fc, fd)(ap2(fa,fb)(map(f)(f => ((a:A,b:B) => (c:C, d:D) => f(a,b,c,d)))))
7   def ap5[A,B,C,D,E,R](fa: => F[A], fb: => F[B], fc: => F[C], fd: => F[D], fe: => F[E])(f: F[(A,B,C,D,E) => R]): F[R] =
8     ap2(fd, fe)(ap3(fa,fb,fc)(map(f)(f => ((a:A,b:B,c:C) => (d:D, e:E) => f(a,b,c,d,e)))))
9 ...

Item3的Functor实例是合情之。

尝试着当Configure类型上应用ap:

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

1 Apply[Configure].ap2(Configure(1),Configure(2))(((_: Int) + (_: Int)).point[Configure])
2                                                   //> res4: Exercises.ex4.Configure[Int] = Exercises.ex4$Configure$$anon$3@64cd705
3                                                   //| f或者用注入方法(injected method)<*>:scalaz/Syntax/ApplySyntax.scala
 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)

抑或用注入方法(injected
method)<*>:scalaz/Syntax/ApplySyntax.scala

则函数升格(function
lifting (A => B) => (F[A] =>
F[B])是Functor的要害职能,但咱说了:一旦能够获取Item3类型的Functor实例我们就能免费使用有的流方法:

1 (Configure(1) <*> {Configure(2) <*> {Configure(3) <*> {(((_:Int)+(_:Int)+(_:Int)).curried).point[Configure]}}}).get
2                                                   //> res5: Int = 6

scalaz提供了Function1的Functor实例。Function1
Functor的map就是 andThen 也尽管是操作方调换的compose:

以上的Apply[Configure]举凡由此Apply
typeclass的构建函数apply实现之:scalaz/Apply.scala

 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
1 object Apply {
2   @inline def apply[F[_]](implicit F: Apply[F]): Apply[F] = F

咱吧堪对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))

3、简化一下ap的写法,只用提供f:(A,B)
=> C这样的基本操作函数:scalaz/Apply.scala

扭转操作:

1  def apply2[A, B, C](fa: => F[A], fb: => F[B])(f: (A, B) => C): F[C] =
2     ap(fb)(map(fa)(f.curried))
3   def apply3[A, B, C, D](fa: => F[A], fb: => F[B], fc: => F[C])(f: (A, B, C) => D): F[D] =
4     apply2(apply2(fa, fb)((_, _)), fc)((ab, c) => f(ab._1, ab._2, c))
5   def apply4[A, B, C, D, E](fa: => F[A], fb: => F[B], fc: => F[C], fd: => F[D])(f: (A, B, C, D) => E): F[E] =
6     apply2(apply2(fa, fb)((_, _)), apply2(fc, fd)((_, _)))((t, d) => f(t._1, t._2, d._1, d._2))
7   def apply5[A, B, C, D, E, R](fa: => F[A], fb: => F[B], fc: => F[C], fd: => F[D], fe: => F[E])(f: (A, B, C, D, E) => R): F[R] =
8     apply2(apply3(fa, fb, fc)((_, _, _)), apply2(fd, fe)((_, _)))((t, t2) => f(t._1, t._2, t._3, t2._1, t2._2))
9 ...
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))

用在Configure类型上:

咱们重尝试着以Item3类型上调用那些免费之流入方法:

1 (Apply[Configure].apply2(Configure(1),Configure(2))(((_: Int) + (_: Int)))).get
2                                                   //> res6: Int = 3
3 (^(Configure(1),Configure(2))((_:Int)+(_:Int))).get
4                                                   //> res7: Int = 3
5 (^^(Configure(1),Configure(2),Configure(3))((_:Int)+(_:Int)+(_:Int))).get
6                                                   //> res8: Int = 6
 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((),(),())

是^,^^是apply2,apply3的流方法:scalaz/syntax/ApplySyntax.scala

自己现在还尚无想到这些函数的切实可行用处。不过从运算结果来拘禁,用这些函数来产生部分数据模型用在打或者测试的仿(simulation)倒是可能的。

 1   def ^[A,B,C](fa: => F[A], fb: => F[B])(
 2                f: (A, B) => C): F[C] =
 3     F.apply2(fa, fb)(f)
 4 
 5   def ^^[A,B,C,D](fa: => F[A], fb: => F[B], fc: => F[C])(
 6                  f: (A, B, C) => D): F[D] =
 7     F.apply3(fa, fb, fc)(f)
 8 
 9   def ^^^[A,B,C,D,E](fa: => F[A], fb: => F[B], fc: => F[C], fd: => F[D])(
10                    f: (A,B,C,D) => E): F[E] =
11     F.apply4(fa, fb, fc, fd)(f)
12 
13   def ^^^^[A,B,C,D,E,I](fa: => F[A], fb: => F[B], fc: => F[C], fd: => F[D], fe: => F[E])(
14                      f: (A,B,C,D,E) => I): F[I] =
15     F.apply5(fa, fb, fc, fd, fe)(f)
16 ...

scalaz提供了成百上千现成的Functor实例。我们先瞧有简约直接的实例:

其他一样种植表达方式是由此ApplicativeBuilder
typeclass实现的流方法|@|:

 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
1 ((Configure(1) |@| Configure(2) |@| Configure(3))((_:Int)+(_:Int)+(_:Int))).get
2                                                   //> res9: Int = 6

针对那些多独品种变量的路我们好用局部应用方式:即type
lambda来表示。一个名列前茅的档次:Either[E,A],我们得以管Left[E]定点下来:
Either[String, A],我们得用type lambda来这样表述:

作用是一样的。我们为此一个实在的简便例子来演示一下Applicative的有血有肉函数施用:

1 scala> Functor[({type l[x] = Either[String,x]})#l].map(Right(3))(_ + 3)
2 res41: scala.util.Either[String,Int] = Right(6)
 1 def configName(name: String): Configure[String] = Configure(name)
 2                                                   //> configName: (name: String)Exercises.ex4.Configure[String]
 3 def configID(userid: String): Configure[String] = Configure(userid)
 4                                                   //> configID: (userid: String)Exercises.ex4.Configure[String]
 5 def configPwd(pwd: String): Configure[String] = Configure(pwd)
 6                                                   //> configPwd: (pwd: String)Exercises.ex4.Configure[String]
 7 case class WebLogForm(name:String, id: String, pwd: String)
 8 
 9 def logOnWeb(name: String, userid: String, pwd: String) =
10   ^^(configName(name),configID(userid), configPwd(pwd))(WebLogForm(_,_,_))
11                                                   //> logOnWeb: (name: String, userid: String, pwd: String)Exercises.ex4.Configur
12                                                   //| e[Exercises.ex4.WebLogForm]
13 def logOnWeb1(name: String, userid: String, pwd: String) =
14   (configName(name) |@| configID(userid) |@| configPwd(pwd))(WebLogForm(_,_,_))
15                                                   //> logOnWeb1: (name: String, userid: String, pwd: String)Exercises.ex4.Configu
16                                                   //| re[Exercises.ex4.WebLogForm]

这么我可针对Either类型进行map操作了。

值得注意的凡:用Applicative施用configName,configID,configPwd时,这三单函数之间没有因关系。特别吻合并行运算或fail-fast,因为无论如何这三独函数都一定会运作。这种Applicative的函数施用体现了它在竞相运算被之优势。

函数类型的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

4、Applicative style
函数施用。上面提到的|@|操作并无是如出一辙种植操作函数而是同种层级式持续函数施用模式。具体实现以ApplicativeBuilder
typeclass里:scalaz/ApplicativeBuilder.scala

tuple类型的Functor是指向最后一个元素类型的: 

 1 private[scalaz] trait ApplicativeBuilder[M[_], A, B] {
 2   val a: M[A]
 3   val b: M[B]
 4 
 5   def apply[C](f: (A, B) => C)(implicit ap: Apply[M]): M[C] = ap.apply2(a, b)(f)
 6 
 7   def tupled(implicit ap: Apply[M]): M[(A, B)] = apply(Tuple2.apply)
 8 
 9   def ⊛[C](cc: M[C]) = new ApplicativeBuilder3[C] {
10     val c = cc
11   }
12 
13   def |@|[C](cc: M[C]) = ⊛(cc)
14 
15   sealed trait ApplicativeBuilder3[C] {
16     val c: M[C]
17 
18     def apply[D](f: (A, B, C) => D)(implicit ap: Apply[M]): M[D] = ap.apply3(a, b, c)(f)
19 
20     def tupled(implicit ap: Apply[M]): M[(A, B, C)] = apply(Tuple3.apply)
21 
22     def ⊛[D](dd: M[D]) = new ApplicativeBuilder4[D] {
23       val d = dd
24     }
25 
26     def |@|[D](dd: M[D]) = ⊛(dd)
27 
28     sealed trait ApplicativeBuilder4[D] {
29       val d: M[D]
30 
31       def apply[E](f: (A, B, C, D) => E)(implicit ap: Apply[M]): M[E] = ap.apply4(a, b, c, d)(f)
32 
33       def tupled(implicit ap: Apply[M]): M[(A, B, C, D)] = apply(Tuple4.apply)
34 
35       def ⊛[E](ee: M[E]) = new ApplicativeBuilder5[E] {
36         val e = ee
37       }
38 
39       def |@|[E](ee: M[E]) = ⊛(ee)
40 ...

 

可看得出(F[A] |@|
F[B] |@| F[C])((A,B,C) =>
D)这个表达式中之蝇头独|@|符号分别代表ApplicativeBuilder2(F[B])及ApplicativeBuilder3(F[C])。

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))

顿时是任何一样种植通过函数施用实现连接Applicative类型值的计。

 

 

 

 

 

5、产生tuple:(F[A],F[B])合并成F[(A,B)]:scalaz/Apply.scala

 

 

 

1  def tuple2[A,B](fa: => F[A], fb: => F[B]): F[(A,B)] =
2     apply2(fa, fb)((_,_))
3   def tuple3[A,B,C](fa: => F[A], fb: => F[B], fc: => F[C]): F[(A,B,C)] =
4     apply3(fa, fb, fc)((_,_,_))
5   def tuple4[A,B,C,D](fa: => F[A], fb: => F[B], fc: => F[C], fd: => F[D]): F[(A,B,C,D)] =
6     apply4(fa, fb, fc, fd)((_,_,_,_))
7   def tuple5[A,B,C,D,E](fa: => F[A], fb: => F[B], fc: => F[C], fd: => F[D], fe: => F[E]): F[(A,B,C,D,E)] =
8     apply5(fa, fb, fc, fd, fe)((_,_,_,_,_))
9 ...

 

比如:

 

1 Apply[Configure].tuple2(Configure("abc"),Configure(123))
2                                                   //> res10: Exercises.ex4.Configure[(String, Int)] = Exercises.ex4$Configure$$an
3                                                   //| on$3@5ffead27
4 Apply[Configure].tuple3(Configure("abc"),Configure(123),Configure(true))
5                                                   //> res11: Exercises.ex4.Configure[(String, Int, Boolean)] = Ex

 

现实用来干啊,我今天尚说不上来。

 

 

 

 

 

6、把一个便函数升格(lift)成高阶函数,如:(A,B)
=> C 升格成 (F[A],F[B]) => F[C]: scalaz/Apply.scala

 

 

1  def lift2[A, B, C](f: (A, B) => C): (F[A], F[B]) => F[C] =
2     apply2(_, _)(f)
3   def lift3[A, B, C, D](f: (A, B, C) => D): (F[A], F[B], F[C]) => F[D] =
4     apply3(_, _, _)(f)
5   def lift4[A, B, C, D, E](f: (A, B, C, D) => E): (F[A], F[B], F[C], F[D]) => F[E] =
6     apply4(_, _, _, _)(f)
7   def lift5[A, B, C, D, E, R](f: (A, B, C, D, E) => R): (F[A], F[B], F[C], F[D], F[E]) => F[R] =
8     apply5(_, _, _, _, _)(f)
9 ...

这种函数升格方式于为此FP方式采取OOP库函数时更是便宜。最杰出的事例是Option类型在FP中结成OOP函数库底以。如果我们想当采取OOP库函数时行使Option类型的输入参数与归值,那我们即便可以通过函数升格(function
lifting)来兑现如此的效力。

1 val of2 = Apply[Option].lift2((_: Int) + (_: Int))//> of2  : (Option[Int], Option[Int]) => Option[Int] = <function2>
2 of2(Some(1),Some(2))                              //> res12: Option[Int] = Some(3)
3 val of3 = Apply[List].lift3((s1: String, s2: String, s3: String) => s1 + " "+s2+" "+s3)
4                                                   //> of3  : (List[String], List[String], List[String]) => List[String] = <functi
5                                                   //| on3>
6 of3(List("How"),List("are"),List("you?"))         //> res13: List[String] = List(How are you?)

咱们独家就此lift2,lift3把常备函数升格成Option和List高阶函数。

双重来只还实在一点底例证:在java.sql.DriverManager库里发只getConnection函数。它的函数款式是:getConnection(p1:String,p2:String,p3:String):
java.sql.Connection

则自己尚未她的源代码,但我还是想以我起定义之型Configure作为参数,我可这么:

1 import java.sql.DriverManager
2 
3 val sqlConnect = Apply[Configure] lift3 java.sql.DriverManager.getConnection
4                                                   //> sqlConnect  : (Exercises.ex4.Configure[String], Exercises.ex4.Configure[Str
5                                                   //| ing], Exercises.ex4.Configure[String]) => Exercises.ex4.Configure[java.sql.
6                                                   //| Connection] = <function3>
7 sqlConnect(Configure("Source"),Configure("User"),Configure("Password"))
8                                                   //> res12: Exercises.ex4.Configure[java.sql.Connection] = Exercises.ex4$Configu
9                                                   //| re$$anon$3@79924b

委这样可使我累在FP模式被工作。

 

总来说:Applicative
typeclass提供了相同模仿函数施用方式。它是经过一个包嵌在容器结构的高阶函数实现管道外之运。Applicative
typeclass还提供了措施以一般性函数升格到高阶函数而FP和OOP混合模式的函数施用更安全方便。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

相关文章