0
点赞
收藏
分享

微信扫一扫

Kotlin(十七)函数式编程<2>

实现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]
}
  1. trait 相当与kotlin中的interface,Functor支持高阶类型传入F,也是一个高阶类型
  2. 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类型
    实现几个例子
  1. 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)))
            }
  1. 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]
举报

相关推荐

0 条评论