@MiloXia
2015-11-01T13:00:02.000000Z
字数 14587
阅读 1990
scala 函数式编程
真:http://docs.typelevel.org/api/scalaz/stable/7.0.4/doc/
提供类型安全的判等
源码:core/.../scalaz/Equal.scala
要求:实现equal(a1,a2) / equalBy / Equal.equal
注入方法:scalaz/syntax/EqualSyntax.scala
case class Person(name: String, age: Int)implicit val personEqual: Equal[Person] =Equal.equal{(a,b) => a.name == b.name && a.age == b.age}//或implicit val personEqual = new Equal[Person] {def equal(a1: Person, a2: Person): Boolean =a1.name == a2.name && a1.age == a2.age}Person("Jone",23) === Person("Jone",23) //true//其它方法: =/= 、assert_=== (会报异常的)
//逆变函数 通过G=>F的函数(同构函数??)(Equal[F]已知)得到Equal[G],同态?? 委托??def contramap[G](f: G => F): Equal[G] = new Equal[G] {def equal(a1: G, a2: G) = self.equal(f(a1), f(a2))}//Equal伴生对象提供def equalBy[A, B: Equal](f: A => B): Equal[A] = Equal[B] contramap fcase class MoneyCents(cents: Int)def moneyToInt(m: MoneyCents): Int = m.cents * 100implicit val moneyEqual: Equal[MoneyCents] = Equal.equalBy(moneyToInt)MoneyCents(120) === MoneyCents(120) //true
类型安全的比较大小?
要求:实现order(a1,a2) / Order.order / orderBy
注入方法:scalaz/syntax/OrderSyntax.scala
case class Person(name: String, age: Int)implicit val personAgeOrder = new Order[Person] {def order(a1: Person, a2: Person): Ordering =if (a1.age < a2.age) Ordering.LT else if (a1.age > a2.age) Ordering.GT else Ordering.EQ}//多用lt gtPerson("John",23) ?|? Person("Joe",24) //LTPerson("John",23) lt Person("Joe",24) //truePerson("John",23) gt Person("Joe",24) //false//或Order.ordercase class Meat(cat: String, weight: Int)implicit val meatWeightOrder: Order[Meat] = Order.order(_.weight ?|? _.weight)Meat("Pork",13) lt Meat("Pork",14) //trueMeat("Beef",13) gt Meat("Pork",14) //false//或orderBy 逆变构建函数case class Money(amount: Int)val moneyToInt: Money => Int = money => money.amountimplicit val moneyOrder: Order[Money] = Order.orderBy(moneyToInt)
就是toString, 不是所有类型的toString都有意义,但实现show的都是有意义的
要求:
注入方法:scalaz/Syntax/ShowSyntax.scala
case class Person(name: String, age: Int)implicit val personShow: Show[Person] = Show.show {p => p.name + "," + p.age + " years old" }Person("Harry",24).shows // Harry,24 years oldPerson("Harry",24).println // Harry,24 years old 真打印不是返回String3.show // scalaz.Cord = 3 //Cord表示可能很长的字符串3.shows // String = 3
要求:实现succ,pred 以及order(a1,a2) (继承自Order)
注入方法:class EnumOps[F]
'a' |-> 'e' // List(a, b, c, d, e)'a'.succ // b'a' -+- 2 // c'd' --- 2 // b
implicitly[Enum[Char]].min //res43: Option[Char] = Some(?)implicitly[Enum[Char]].max //res44: Option[Char] = Some( )implicitly[Enum[Double]].max //res45: Option[Double] = Some(1.7976931348623157E308)implicitly[Enum[Int]].min //res46: Option[Int] = Some(-2147483648)implicitly[Enum[(Boolean, Int, Char)]].max<console>:14: error: could not find implicit value for parameter e: scalaz.Enum[(Boolean, Int, Char)]implicitly[Enum[(Boolean, Int, Char)]].max
定义:scalaz/Functor.scala
要求:实现map
注入方法:scalaz/syntax/FunctorSyntax.scala
case class Item3[A](i1: A, i2: A, i3: A)val item3Functor = new Functor[Item3] {def map[A,B](ia: Item3[A])(f: A => B): Item3[B] = Item3(f(ia.i1),f(ia.i2),f(ia.i3))}val F = Functor[Item3]//前方高能。。。F.map(Item3("Morning","Noon","Night"))(_.length) //> res0: scalaz.functor.Item3[Int] = Item3(7,4,5)F.apply(Item3("Morning","Noon","Night"))(_.length)//> res1: scalaz.functor.Item3[Int] = Item3(7,4,5)//这种不错F(Item3("Morning","Noon","Night"))(_.length) //> res2: scalaz.functor.Item3[Int] = Item3(7,4,5)//map (A => B) => (F[A] => F[B]),就是把(A => B)升格(lift)成(F[A] => F[B])F.lift((s: String) => s.length)(Item3("Morning","Noon","Night")) //> res3: scalaz.functor.Item3[Int] = Item3(7,4,5)
1、map(fa)(x => x) === fa //id
2、map(map(fa)(f1))(f2) === map(fa)(f2 compose f1) //结合律
//定义产生器implicit def item3Arbi[A](implicit a: Arbitrary[A]): Arbitrary[Item3[A]] = Arbitrary {def genItem3: Gen[Item3[A]] = for {b <- Arbitrary.arbitrary[A]c <- Arbitrary.arbitrary[A]d <- Arbitrary.arbitrary[A]} yield Item3(b,c,d)genItem3}functor.laws[Item3].check
前方高能
(((_: Int) + 1) map((k: Int) => k * 3))(2) // 9(((_: Int) + 1) map((_: Int) * 3))(2) // 9(((_: Int) + 1) andThen ((_: Int) * 3))(2) //9(((_: Int) * 3) compose ((_: Int) + 1))(2) //9
val f = Functor[List] compose Functor[Item3]val item3 = Item3("Morning","Noon","Night")f.map(List(item3,item3))(_.length) // List(Item3(7,4,5), Item3(7,4,5))//直接对内层Functor进行map//反过来val f1 = Functor[Item3] compose Functor[List]f1.map(Item3(List("1"),List("22"),List("333")))(_.length) // Item3(List(1),List(2),List(3))
val fmap = Functor[List].lift {(_: Int) * 2} // fmap: List[Int] => List[Int] = <function1> lift成List[Int] => List[Int]fmap(List(3)) // List(6)
List(1, 2, 3) >| "x" // List(x, x, x)List(1, 2, 3) as "x" // List(x, x, x)List(1, 2, 3).fpair // List((1,1), (2,2), (3,3))List(1, 2, 3).strengthL("x") // List((x,1), (x,2), (x,3))List(1, 2, 3).strengthR("x") // List((1,x), (2,x), (3,x))List(1, 2, 3).void // List((), (), ())
能想到的都有
Functor[List].map(List(1,2,3))(_ + 3) // List(4, 5, 6)Functor[Option].map(Some(3))(_ + 3) // Some(6)Functor[java.util.concurrent.Callable] // scalaz.Functor[java.util.concurrent.Callable]Functor[Stream] // scalaz.Functor[Stream]Functor[Vector] // scalaz.Functor[Vector]
用type lambda国定E, 反正留最后的A (一般规律)
Functor[({type l[x] = Either[String,x]})#l].map(Right(3))(_ + 3) // Right(6)
针对返回类型--这是语义
//1参Functor[({type l[x] = String => x})#l].map((s: String) => s + "!")(_.length)("Hello") // 6//2参Functor[({type l[x] = (String,Int) => x})#l].map((s: String, i: Int) => s.length + i)(_ * 10)("Hello",5) // 100//3参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) // HELLO3TRUE
针对最后一个元素类型
Functor[({type l[x] = (String,x)})#l].map(("a",1))(_ + 2) // (a,3)Functor[({type l[x] = (String,Int,x)})#l].map(("a",1,"b"))(_.toUpperCase) // (a,1,B)Functor[({type l[x] = (String,Int,Boolean,x)})#l].map(("a",1,true,Item3("a","b","c")))(i => i.map(_.toUpperCase)) // (a,1,true,Item3(A,B,C))
定义: scalaz/Applicative.scala & scalaz/Apply.scala
要求:实现 point,ap,map
一旦实现了Applicative实例就能同时获取了Functor实例
注入方法:scalaz/Apply.scala & scalaz/syntax/ApplicativeSyntax.scala
trait Configure[+A] { def get: A }object Configure {def apply[A](data: => A) = new Configure[A] { def get = data }//实现Functorimplicit val configFunctor = new Functor[Configure] {def map[A,B](ca: Configure[A])(f: A => B): Configure[B] = Configure(f(ca.get))}//实现Applicative//def ap[A,B](fa: => F[A])(f: => F[A => B]): F[B]//: => 的好处可传任何的对象/函数/方法,可实现lazy//call-by-name 一般是call-by-valueimplicit val configApplicative = new Applicative[Configure] {def point[A](a: => A) = Configure(a)def ap[A,B](ca: => Configure[A])(cfab: => Configure[A => B]): Configure[B] =cfab map {fab => fab(ca.get)}}}
又可叫pure
//一般方式:val fapply = List(1, 2, 3, 4) map {(_: Int) * (_:Int)}.curried // List[Int => Int] = List(<function1>, <function1>, <function1>, <function1>)fapply map {_(9)} // List(9, 18, 27, 36)//point/pure1.point[List] // List(1)1.point[Option] // Some(1)1.point[Option] map {_ + 2} // Some(3)1.point[List] map {_ + 2} // List(3)"abc".point[Configure] // Configure[String]12.point[Configure] // Configure[String]
通过Apply的ap2,ap3,ap4 ...款式的函数我们可以把 F[A],F[B],F[C],F[D]...多个值连接起来
Apply[Configure].ap2(Configure(1),Configure(2))(((_: Int) + (_: Int)).point[Configure]) //Configure(3)
(Configure(1) <*> {Configure(2) <*> {Configure(3) <*> {(((_:Int)+(_:Int)+(_:Int)).curried).point[Configure]}}}).get // 6
apply2,apply3..只用提供f:(A,B) => C 基本函数 (无需point)
(Apply[Configure].apply2(Configure(1),Configure(2))(((_: Int) + (_: Int)))).get(^(Configure(1),Configure(2))((_:Int)+(_:Int))).get(^^(Configure(1),Configure(2),Configure(3))((_:Int)+(_:Int)+(_:Int))).get
通过ApplicativeBuilder typeclass实现的注入方法
((Configure(1) |@| Configure(2) |@| Configure(3))((_:Int)+(_:Int)+(_:Int))).get // 6
def configName(name: String): Configure[String] = Configure(name)def configID(userid: String): Configure[String] = Configure(userid)def configPwd(pwd: String): Configure[String] = Configure(pwd)case class WebLogForm(name:String, id: String, pwd: String)def logOnWeb(name: String, userid: String, pwd: String) =^^(configName(name),configID(userid), configPwd(pwd))(WebLogForm(_,_,_))def logOnWeb1(name: String, userid: String, pwd: String) =(configName(name) |@| configID(userid) |@| configPwd(pwd))(WebLogForm(_,_,_))
用Applicative施用configName,configID,configPwd时,这三个函数之间没有依赖关系。特别适合并行运算或fail-fast,因为无论如何这三个函数都一定会运行。这种Applicative的函数施用体现了它在并行运算中的优势。
只返回右手边或左手边
1.some <* 2.some // Some(1)none <* 2.some // None1.some *> 2.some // Some(2)none *> 2.some // None
(F[A] |@| F[B] |@| F[C])((A,B,C) => D)
|@|操作并不是一种操作函数而是一种层级式持续函数施用模式:t (<*>)
|@|定义scalaz/ApplicativeBuilder.scala
(F[A], F[B]) 合并成 F[(A, B)] :scalaz/Apply.scala
tuple2 == apply2(fa, fb)((_, _))
(List(F[A], F[A]) => F[List[A]] 是sequeue)
Apply[Configure].tuple2(Configure("abc"),Configure(123)) // Configure[(String, Int)]Apply[Configure].tuple3(Configure("abc"),Configure(123),Configure(true)) // Configure[(String, Int, Boolean)]
比functor的lift 要多参数
def lift2[A, B, C](f: (A, B) => C): (F[A], F[B]) => F[C] = apply2(_, _)(f)
val of2 = Apply[Option].lift2((_: Int) + (_: Int))of2(Some(1),Some(2)) // Some(3)val of3 = Apply[List].lift3((s1: String, s2: String, s3: String) => s1 + " "+s2+" "+s3)of3(List("How"),List("are"),List("you?")) // List(How are you?)//前方高能import java.sql.DriverManagerval sqlConnect = Apply[Configure] lift3 java.sql.DriverManager.getConnectionsqlConnect(Configure("Source"),Configure("User"),Configure("Password")) // Configure[java.sql.Connection]//使我们继续在FP模式中工作
希望[(+3),(2)] <> [1,2] 得到[4,4], 对list中的元素施用不同的函数
streamZipApplicative.ap(Tags.Zip(Stream(1, 2))) (Tags.Zip(Stream({(_: Int) + 3}, {(_: Int) * 2}))).toList // List(4, 4)
(Applicative f) => [f a] -> f [a] 把Applicative的List变成List的Applicative, 和基于Monad的sequence是一样的
def sequenceA[F[_]: Applicative, A](list: List[F[A]]): F[List[A]] = list match {case Nil => (Nil: List[A]).point[F]case x :: xs => (x |@| sequenceA(xs)) {_ :: _}}sequenceA(List(1.some, 2.some)) // Some(List(1, 2))sequenceA(List(3.some, none, 1.some)) // NonesequenceA(List(List(1, 2, 3), List(4, 5, 6))) // List(List(1, 4), List(1, 5), List(1, 6), List(2, 4), List(2, 5), List(2, 6), List(3, 4), List(3, 5), List(3, 6))type Function1Int[A] = ({type l[A]=Function1[Int, A]})#l[A]//等于type Function1Int[A] = Function1[Int, A]val f: Int => List[Int] = sequenceA(List((_: Int) + 3, (_: Int) + 2, (_: Int) + 1): List[Function1Int[Int]])//Int => List[Int] = <function1>f(3) // List(6, 5, 4)
//product 产生元组Applicative[List].product[Option].point(1) // (List(1),Some(1))//对元组操作((List(1), 1.some) |@| (List(1), 1.some)) {_ |+| _} // (List(1, 1),Some(2))((List(1), 1.success[String]) |@| (List(1), "boom".failure[Int])) {_ |+| _} // (List(1, 1),Failure(boom))//生成嵌套Applicative[List].compose[Option].point(1) // List(Some(1))
Scala木有:k,这是一个手写版
def kind[A: scala.reflect.runtime.universe.TypeTag]: String = {import scala.reflect.runtime.universe._def typeKind(sig: Type): String = sig match {case PolyType(params, resultType) =>(params map { p =>typeKind(p.typeSignature) match {case "*" => "*"case s => "(" + s + ")"}}).mkString(" -> ") + " -> *"case _ => "*"}def typeSig(tpe: Type): Type = tpe match {case SingleType(pre, sym) => sym.companionSymbol.typeSignaturecase ExistentialType(q, TypeRef(pre, sym, args)) => sym.typeSignaturecase TypeRef(pre, sym, args) => sym.typeSignature}val sig = typeSig(typeOf[A])val s = typeKind(sig)sig.typeSymbol.name + "'s kind is " + s + ". " + (s match {case "*" =>"This is a proper type."case x if !(x contains "(") =>"This is a type constructor: a 1st-order-kinded type."case x =>"This is a type constructor that takes type constructor(s): a higher-kinded type."})}
使用:
scala> kind[Int]res0: String = Int's kind is *.This is a proper type.scala> kind[Option.type]res1: String = Option's kind is * -> *.This is a type constructor: a 1st-order-kinded type.scala> kind[Either.type]res2: String = Either's kind is * -> * -> *.This is a type constructor: a 1st-order-kinded type.scala> kind[Equal.type]res3: String = Equal's kind is * -> *.This is a type constructor: a 1st-order-kinded type.scala> kind[Functor.type]res4: String = Functor's kind is (* -> *) -> *.This is a type constructor that takes type constructor(s): a higher-kinded type.
//如果要抽象出kg,用case class 太麻烦,每次要解值case class KiloGram(value: Double)//newtype KiloGram = KiloGram Doublesealed trait KiloGramdef KiloGram[A](a: A): A @@ KiloGram = Tag[A, KiloGram](a) // scalaz.@@[Double,KiloGram] = 20.0val mass = KiloGram(20.0)2 * mass // Double = 40.0
定义:scalaz/Monoid.scala & scalaz/Semigroup.scala
要求:实现zero & append
注入方法:|+|
0 |+| 30 // 5020.some |+| 30.some // Some(50)List(1,2,3) |+| List(4,5,6) // List(1, 2, 3, 4, 5, 6)//Int的乘法群Tags.Multiplication(3) |+| Monoid[Int @@ Tags.Multiplication].zero // scalaz.@@[Int,scalaz.Tags.Multiplication] = 3//boolean的monoid是有方向的(&& 和 ||)Tags.Conjunction(true) |+| Tags.Conjunction(false) // scalaz.@@[Boolean,scalaz.Tags.Conjunction] = falseTags.Disjunction(true) |+| Tags.Disjunction(false) // scalaz.@@[Boolean,scalaz.Tags.Disjunction] = true//看来boolean的monoid的幺元是true或false(不太严谨,不过也对)Monoid[Boolean @@ Tags.Conjunction].zero // scalaz.@@[Boolean,scalaz.Tags.Conjunction] = trueMonoid[Boolean @@ Tags.Disjunction].zero // scalaz.@@[Boolean,scalaz.Tags.Disjunction] = false
scalaz/Ordering.scala
两个Ordering类型值f1,f2的append操作结果:假如f1是EQ就是f2,否则是f1
(Ordering.EQ: Ordering) |+| (Ordering.GT: Ordering) // scalaz.Ordering = GT(Ordering.EQ: Ordering) |+| (Ordering.LT: Ordering) // scalaz.Ordering = LT(Ordering.GT: Ordering) |+| (Ordering.EQ: Ordering) // scalaz.Ordering = GT(Ordering.LT: Ordering) |+| (Ordering.EQ: Ordering) // scalaz.Ordering = LT(Ordering.LT: Ordering) |+| (Ordering.GT: Ordering) // scalaz.Ordering = LT(Ordering.GT: Ordering) |+| (Ordering.LT: Ordering) // scalaz.Ordering = GT
用以上的特性来比较两个String的长度:如果长度相等则再比较两个String的字符顺序
def strlenCompare(lhs: String, rhs: String): Ordering = (lhs.length ?|? rhs.length) |+| (lhs ?|? rhs)strlenCompare("abc","aabc") // LTstrlenCompare("abd","abc") // GT
可以用来计算任何有序的(有规则的)比较(Ordering就是用来比较的),再此之前你可以实现自己的Order typeclass
定义: scalaz/Foldable.scala
要求: Monoid[B]
注入方法:scalaz/syntax/FoldableSyntax.scala
scalaz为大多数标准库中的集合类型提供了Foldable实例
List(1,2,3) foldMap {x => x} // 6List(1,2,3) foldMap {x => (x + 3).toString} // "456"
def productMonoid[A,B](ma: Monoid[A], mb: Monoid[B]): Monoid[(A,B)] =new Monoid[(A,B)] {def zero = (ma.zero, mb.zero)def append(x: (A,B), y: => (A,B)): (A,B) =(ma.append(x._1, y._1), mb.append(x._2, y._2))}val pm = productMonoid(Monoid[Int],Monoid[List[Int]])//我们可以在游览一个List[Int]时同时统计长度(list length)及乘积(product)val intMultMonoid = new Monoid[Int] {def zero = 1def append(a1: Int, a2: => Int): Int = a1 * a2}val pm = productMonoid(Monoid[Int @@ Tags.Multiplication],Monoid[Int])List(1,2,3,4,6).foldMap(i => (i, 1))(productMonoid(intMultMonoid,Monoid[Int])) // (144,5)//合并多层map的Monoiddef mapMergeMonoid[K,V](V: Monoid[V]): Monoid[Map[K, V]] =new Monoid[Map[K, V]] {def zero = Map[K,V]()def append(a: Map[K, V], b: => Map[K, V]) =(a.keySet ++ b.keySet).foldLeft(zero) { (acc,k) =>acc.updated(k, V.append(a.getOrElse(k, V.zero),b.getOrElse(k, V.zero)))}}val m1 = Map("o1" -> Map("i1" -> 1, "i2" -> 2))val m2 = Map("o1" -> Map("i2" -> 3))val m3 = M.append(m1, m2) //Map(o1 -> Map(i1 -> 1, i2 -> 5))
可为如任意对象的任意计算抽象出Monoid(符合 Monoid law)
还可以用这个Monoid来统计一段字串内字符发生的频率
def frequencyMap[A](as: List[A]): Map[A, Int] =as.foldMap((a: A) => Map(a -> 1))(mapMergeMonoid[A, Int](Monoid[Int]))frequencyMap("the brown quik fox is running quikly".toList)
Monoid必须在可折叠数据结构(Foldable)内才能正真发挥作用
Monoid[Int].applicative.ap2(1, 1)(0) // 2Monoid[List[Int]].applicative.ap2(List(1), List(1))(Nil) // List(1, 1)
http://www.eed3si9n.com/learning-scalaz/7.0/Functor+Laws.html