@MiloXia
2015-11-01T21:00:02.000000Z
字数 14587
阅读 1756
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 f
case class MoneyCents(cents: Int)
def moneyToInt(m: MoneyCents): Int = m.cents * 100
implicit 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 gt
Person("John",23) ?|? Person("Joe",24) //LT
Person("John",23) lt Person("Joe",24) //true
Person("John",23) gt Person("Joe",24) //false
//或Order.order
case class Meat(cat: String, weight: Int)
implicit val meatWeightOrder: Order[Meat] = Order.order(_.weight ?|? _.weight)
Meat("Pork",13) lt Meat("Pork",14) //true
Meat("Beef",13) gt Meat("Pork",14) //false
//或orderBy 逆变构建函数
case class Money(amount: Int)
val moneyToInt: Money => Int = money => money.amount
implicit 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 old
Person("Harry",24).println // Harry,24 years old 真打印不是返回String
3.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 }
//实现Functor
implicit 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-value
implicit 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/pure
1.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 // None
1.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.DriverManager
val sqlConnect = Apply[Configure] lift3 java.sql.DriverManager.getConnection
sqlConnect(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)) // None
sequenceA(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.typeSignature
case ExistentialType(q, TypeRef(pre, sym, args)) => sym.typeSignature
case 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 Double
sealed trait KiloGram
def KiloGram[A](a: A): A @@ KiloGram = Tag[A, KiloGram](a) // scalaz.@@[Double,KiloGram] = 20.0
val mass = KiloGram(20.0)
2 * mass // Double = 40.0
定义:scalaz/Monoid.scala & scalaz/Semigroup.scala
要求:实现zero & append
注入方法:|+|
0 |+| 30 // 50
20.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] = false
Tags.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] = true
Monoid[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") // LT
strlenCompare("abd","abc") // GT
可以用来计算任何有序的(有规则的)比较(Ordering就是用来比较的),再此之前你可以实现自己的Order typeclass
定义: scalaz/Foldable.scala
要求: Monoid[B]
注入方法:scalaz/syntax/FoldableSyntax.scala
scalaz为大多数标准库中的集合类型提供了Foldable实例
List(1,2,3) foldMap {x => x} // 6
List(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 = 1
def 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的Monoid
def 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) // 2
Monoid[List[Int]].applicative.ap2(List(1), List(1))(Nil) // List(1, 1)
http://www.eed3si9n.com/learning-scalaz/7.0/Functor+Laws.html