实现Typeclass
Java中常见的一阶参数多态,这是我们经常说的泛型。利用泛型多态,在很大程度上能减少大量相同代码,但是需要高阶抽象的时候,还是需要很多冗余代码。
如标准库List,Set 等都实现了Iterabale接口,他们都有相同的方法,如filter,remove .
interface Iterable<T> {
fun filter(p:(T)->Boolean):Iterator<T>
fun remove(p:(T)->Boolean):Iterator<T> = filter{x->!p(x)}
}
interface List<T>:Iterable<T>{
override fun filter(p: (T) -> Boolean): Iterator<T>
override fun remove(p: (T) -> Boolean): Iterator<T> = filter{x->!p(x)}
}
interface Set<T>:Iterable<T>{
override fun filter(p: (T) -> Boolean): Iterator<T>
override fun remove(p: (T) -> Boolean): Iterator<T> = filter{x->!p(x)}
}
我们需要用高阶类型消除泛型设计冗余
高阶类型:用类型构造新类型
我们要了解什么是“类型构造器”。
通常我们熟悉的是“值构造器",给一个函数传递一个参数值,构造出来一个新的值。
(x:Int)->x
类型构造器,可以传递一个类型变量,然后构造出一个新的类型。
比如List[T],当我们传入Int,就可以构造出List<Int>
- 一阶值构造器: 通过传入一个具体的值,然后构造出另一个具体的值。
- 一阶类型构造器:通过一个具体的类型变量,然后构造出另一个具体类型
假设有一个类型构造器Container<X>,我们就可以消除泛型上面例子的冗余
interface Iterable<T,Container<X>> {
fun filter(p:(T)->Boolean):Container<T>
fun remove(p:(T)->Boolean):Container<T> = filter{x->!p(x)}
}
interface List<T>:Container<T,List>
interface Set<T>:Iterable<T,Set>
高阶类型和Typeclass
所谓的高阶函数,Kotlin并没有真正的支持,但是Kotlin的扩展语法可以替换Typeclass语言特性,代替高阶类型这个特性。
函子:高阶类型之间的映射。
由于函数式编程非常类似数学领域,是范畴论的理论思想。不必理会这些专业术语。
理解函子
可以理解,高阶类型的参数之间的映射。
目前我们还不知道Kotlin如何支持,先看看Scala的支持例子,在考虑Kotlin的方式
trait Functor[F[_]]{
def fmap[A , B](fa : F[A],f : A => B): F[B]
}
- trait 相当与kotlin中的interface,Functor支持高阶类型传入F,也是一个高阶类型
- Functor 实现了fmap方法,接收一个类型F[A]的变量fa,以及一个函数f,通过函数f我把fa中的A映射为B,fmap的返回类型F[B]
应用例子,把List[T] 集成Functor
implicit val listFunctor = new Functor[List]{
def fmap(fa:List[A])(f:A=>B) = fa.fmap(f)
}
Kotlin 用扩展方法实现Typeclass
前面铺垫这么久就是为了理解Kotlin的用法
我们还用Functor这个例子模拟
// 模拟高阶类型
interface Kind<out F, out A>
interface Functor<F> {
fun <A, B> Kind<F, A>.map(f: (A) -> B): Kind<F, B>
}
构造器F和应用类型A产生新的类型
自定义一个List类型,去应用
sealed class List<out A> : Kind<List.K, A> {
object K
}
object Nil : List<Nothing>()
data class Cons<A>(val head: A, val tail: List<A>) : List<A>()
List 两种状态,一种Nil空列表,另一种Const由head和tail连接成的列表
List 实现了Kind<List.K,A>,带入上面的Kind的定义,我们得到List<A>是类构造器List.K应用类型参数A之后得到的类型。
由此我们可以用List.K 代表List这个高阶类型
@Suppress("UNCHECK_CAST","NOTHING_TO_INLINE")
inline fun <A> Kind<List.K, A>.unwrap(): List<A> = this as List<A>
object ListFunctor : Functor<List.K> {
override fun <A, B> Kind<List.K, A>.map(f: (A) -> B): Kind<List.K, B> {
return when (this) {
is Cons -> {
val t = this.tail.map(f).unwrap()
Cons<B>(f(this.head), t)
}
else -> Nil
}
}
}
我们无法直接将Kotlin的object的扩展方法导入
XXXX错误XXXXX
import ListFunctor.*
Cons(1,Nil).map{it+1}
Kotlin中的receiver机制可以将object成员引入作用域,我们使用run函数即可
ListFunctor.run{
Cons(1,Nil).map{it+1}
}
可以绕回前面文章,看下
run,also,let,takeIf,apply 函数介绍
Kotlin(六)多态和扩展
Typeclass 设计常见功能
- 利用类型的扩展语法定义通用的Typeclass接口
- 通过object定义预提类型的Typeclass
- 在实例函数run的闭包中,目标类型对象或值就支持Typeclass类型
实现几个例子
- Eq
interface Eq<F> {
fun F.eq(that: F): Boolean
}
object IntEq : Eq<Int> {
override fun Int.eq(that: Int): Boolean {
return this == that
}
}
IntEq.run {
val a = 1
println(a.eq(1))
println(a.eq(2))
}
Eq的高阶支持。利用之前的Functor,实现ListEq
abstract class ListEq<A>(val a: Eq<A>) : Eq<Kind<List.K, A>> {
override fun Kind<List.K, A>.eq(that: Kind<List.K, A>): Boolean {
val curr = this
return if (curr is Cons && that is Cons) {
val headEq = a.run {
curr.head.eq(that.head)
}
if (headEq) curr.tail.eq(that.tail) else false
} else curr is Nil && that is Nil
}
}
IntListEq.run {
val a = Cons(1, Cons(2, Nil))
println(a.eq(Cons(1, Cons(2, Nil))))
println(a.eq(Cons(1, Nil)))
}
- Show 和 Fodable
类似Java的toString方法,我们实现一个Show展示详细信息的Typeclass
interface Show<F> {
fun F.show(): String
}
class Book(val name: String)
object BookShow : Show<Book> {
override fun Book.show(): String = this.name
}
BookShow.run {
println(Book("David is studing dive into kotloin").show())
}
如果让List像Eq一样支持Show如何做?需要讲元素打印出来在拼装,List类型增加一个fold操作
设计一个Fodable 的Typeclass类型
interface Fodable<F> {
fun <A, B> Kind<F, A>.fold(init: B): ((B, A) -> B) -> B
}
//inline fun <A> Kind<List.K,A>.unwrap():List<A> = this as List<A>
object ListFoldable : Fodable<List.K> {
override fun <A, B> Kind<List.K, A>.fold(init: B): ((B, A) -> B) -> B = { f ->
fun fold0(l: List<A>, v: B): B {
return when (l) {
is Cons ->
fold0(l.tail, f(v, l.head))
else -> v
}
}
fold0(this.unwrap(), init)
}
}
abstract class ListShow<A>(val a: Show<A>) : Show<Kind<List.K, A>> {
override fun Kind<List.K, A>.show(): String {
val fa = this
return "[" +
ListFoldable.run {
fa.fold(listOf<String>())({ r, i ->
r + a.run { i.show() }
}).joinToString() + "]"
}
}
}
object BookListShow : ListShow<Book>(BookShow)
Foldable 为Kind<List.K,A> 类型扩展了fold操作,所以可以实现ListShow,类似ListEq这里需要Foldable的额外辅助操作
测试
BookListShow.run {
println(
Cons(
Book("David study dive into kotlin"),
Cons(Book("Thinking in Java"), Nil)
).show()
)
}
结果打印:
[David study dive into kotlin, Thinking in Java]