@MiloXia
2015-09-15T01:54:53.000000Z
字数 2994
阅读 2318
函数式编程
来自于haskell,意思相 当于”class of types”,可以理解为对某一系列类型的抽象(即高阶类型)
scala里的type classes模式主要通过隐式参数来实现
但需要注意的是,并不是所有的隐式参数都可以理解为type classes模式,隐式参数的类型必须是泛型的(高阶)
它表示某种抽象行为,这种行为的具体实现 要由它的具体类型参数决定
[ http://ropas.snu.ac.kr/~bruno/papers/TypeClasses.pdf ]
//已有类class Aclass B(val i: Int)//type class的一般使用范式def cmp[T: Comparable2](x:T, y:T) = {implicitly[Comparable2[T]].comp(x,y)}//or//def cmp[T](x: T, y: T) (implicit c : Comparable2[T]) {// c.comp(x, y)//}//type classtrait Comparable2[T] { def comp(x: T, y: T) = x.hashCode - y.hashCode }//定义扩展实现object Comparable2 {implicit object ACmp extends Comparable2[A] {override def comp(x: A, y: A) = super.comp(x, y)}implicit object BCmp extends Comparable2[B] {override def comp(x:B, y:B) = x.i - y.i}}//or//object A {// implicit val cmpA = new Comparable2[A] {// override def comp(x: A, y: A) = super.comp(x, y)// }//}//object B {// implicit val cmpB = new Comparable2[B] {// override def comp(x:B, y:B) = x.i - y.i// }//}//* 推荐上面一种,因为有可能修改不到A和B的伴生对象//testval x = new Aval y = new Aprintln(cmp(x, y) > 0)val x2 = new B(1)val y2 = new B(2)println(cmp(x2, y2) > 0)
把A的结构与行为(方法)给分离开了(解耦)
或者说对某个类型,在不改动其本身的情况下,具备了扩展其行为的能力
type classes模式通过泛型来描述某种通用行为
对每个想要用到这种行为的具体类型,在实现时行为部分并不放在类型自身中,而是通过实现一个type class实例(对泛型具化),最后在调用时(通过隐 式参数)让编译器自动寻找行为的实现。
依赖类型是用在type class模式中的一种场景,表达的是type class优点第二条:可组合性,也是源自haskell的一种扩展
由shapeless作者提出
假设矩阵类型与矢量类型以及Int类型在做笛卡尔乘积运算时,结果类型取决于这两者的组合:
Matrix * Matrix ->Matrix //矩阵 *矩阵 得到 矩阵Matrix * Vector ->Vector //矩阵 *矢量 得到 矢量Matrix * Int ->Matrix //矩阵 *Int得到 矩阵Int * Matrix ->Matrix //Int*矩阵 得到 矩阵
因为结果类型受入参类型的约束,我们在设计函数时,可以把结果类型约束为满足这种组合的情况,用 scala来实现
trait Matrixtrait Vectortrait MultDep[A, B, C] //定义一个笛卡尔运算时需要依赖类型,用于约束结果类型
我们通过隐式对象提供各种类型的组合时的依赖类型:
implicit object mmm extends MultDep[Matrix, Matrix, Matrix]implicit object mvv extends MultDep[Matrix, Vector, Vector]implicit object mim extends MultDep[Matrix, Int, Matrix]implicit object imm extends MultDep[Int, Matrix, Matrix]
检验一下隐式对象:
scala> implicitly[MultDep[Matrix, Matrix, Matrix]] // OK: Matrix * Matrix -> Ma trixscala> implicitly[MultDep[Matrix, Vector, Vector]] // OK: Matrix * Vector -> Ve ctorscala> implicitly[MultDep[Matrix, Vector, Matrix]] // Error 没有定义过这种组合
现在在笛卡尔积的方法上增加这个隐式参数,来限制参数类型和结果类型必须符合我们的组合要求:
def mult[A, B, C](a : A, b : B)(implicit instance : MultDep[A, B, C]) : C = {null.asInstanceOf[ C ]}
这样我们在运行mult方法时,当传入的参数类型不符合我们用隐式对象提供的组合时,编译器直接会帮我 们检查出来:
val r1 : Matrix = mult(new Matrix {}, new Matrix{}) // Compilesval r2 : Vector = mult(new Matrix {}, new Vector{}) // Compilesval r3 : Matrix = mult(new Matrix {}, 2) // Compilesval r4 : Matrix = mult(2, new Matrix {}) // Compilesval r5 : Matrix = mult(new Matrix {}, new Vector{}) // 错误,结果类型应该是Vector
在scala的集合库中,当从一种类型的集合转换到另一种类型的集合时,比如执行map或to方法时会调 用CanBuildFrom这个隐式参数来判断当前类型是否可转换为目标类型:
def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That = {val b = bf(repr)b.sizeHint(this)for (x <- this) b += f(x)b.result}
type class有点像clojure的基于class分类的多重方法
(defmulti comp class :default :base)(defmethod comp A [x, y](:base x y))(defmethod comp B [x, y](- x.i y.i))(defmethod :base [x, y](- (.hashCode x) (.hashCode y))))