w: W, a: A): WriterT[Id, W, A] = WriterT[Id, W, A]((w, a)) 15 } 16
17 object Unwriter { 18 def apply[U, A](u: U, a: A): UnwriterT[Id, U, A] = UnwriterT[Id, U, A]((u, a)) 19 }
type ReaderT[F[_], E, A] = Kleisli[F, E, A] >>> type Reader[E,A] = ReaderT[Id,E,A]
好了,说了半天还是回到如何使用Kleisli进行函数组合的吧:
1 //Kleisli款式函数kf,kg
2 val kf: Int => Option[String] = (i: Int) => Some((i + 3).shows) 3 //> kf : Int => Option[String] = <function1>
4 val kg: String => Option[Boolean] = { case "3" => true.some; case _ => false.some } 5 //> kg : String => Option[Boolean] = <function1> 6 //Kleisli函数组合操作
7 import Kleisli._ 8 val kfg = kleisli(kf) >=> kleisli(kg) //> kfg : scalaz.Kleisli[Option,Int,Boolean] = Kleisli(<function1>)
9 kfg(1) //> res5: Option[Boolean] = Some(false)
10 kfg(0) //> res6: Option[Boolean] = Some(true)
例子虽然很简单,但它说明了很多重点:上一个函数输入的运算值是下一个函数的输入值 Int=>String=>Boolean。输出Monad一致统一,都是Option。
那么,Kleisli到底用来干什么呢?它恰恰显示了FP函数组合的真正意义:把功能尽量细分化,通过各种方式的函数组合实现灵活的函数重复利用。也就是在FP领域里,我们用Kleisli来组合FP函数。
下面我们就用scalaz自带的例子scalaz.example里的KleisliUsage.scala来说明一下Kleisli的具体使用方法吧:
下面是一组地理信息结构:
1 // just some trivial data structure , 2 // Continents contain countries. Countries contain cities.
3 case class Continent(name: String, countries: List[Country] = List.empty) 4 case class Country(name: String, cities: List[City] = List.empty) 5 case class City(name: String, isCapital: Boolean = false, inhabitants: Int = 20)
分别是:洲(Continent)、国家(Country)、城市(City)。它们之间的关系是层级的:Continent(Country(City))
下面是一组模拟数据:
1 val data: List[Continent] = List( 2 Continent("Europe"), 3 Continent("America", 4 List( 5 Country("USA", 6 List( 7 City("Washington"), City("New York"))))), 8 Continent("Asia", 9 List( 10 Country("India", 11 List(City("New Dehli"), City("Calcutta"))))))
从上面的模拟数据也可以看出Continent,Country,City之间的隶属关系。我们下面设计三个函数分别对Continent,Country,City进行查找:
1 def continents(name: String): List[Continent] =
2 data.filter(k => k.name.contains(name)) //> continents: (name: String)List[Exercises.kli.Continent] 3 //查找名字包含A的continent
4 continents("A") //> res7: List[Exercises.kli.Continent] = List(Continent(America,List(Country(U 5 //| SA,List(City(Washington,false,20), City(New York,false,20))))), Continent(A 6 //| sia,List(Country(India,List(City(New Dehli,false,20), City(Calcutta,false,2 7 //| 0)))))) 8 //找到两个:List(America,Asia)
9 def countries(continent: Continent): List[Country] = continent.countries 10 //> countries: (continent: Exercises.kli.Continent)List[Exercises.kli.Country] 11 //查找America下的国家
12 val america =
13 Continent("America", 14 List( 15 Country("USA", 16 List( 17 City("Washington"), City("New York"))))) 18 //> america : Exercises.kli.Continent = Continent(America,List(Country(USA,Lis 19 //| t(City(Washington,false,20), City(New York,false,20)))))
20 countries(america) //> res8: List[Exercises.kli.Country] = List(Country(USA,List(City(Washington,f 21 //| alse,20), City(New York,false,20))))
22 def cities(country: Country): List[City] = country.cities 23 //> cities: (country: Exercises.kli.Country)List[Exercises.kli.C |