@MiloXia
2015-09-15T09:54:53.000000Z
字数 2994
阅读 2108
函数式编程
来自于haskell,意思相 当于”class of types”,可以理解为对某一系列类型的抽象(即高阶类型)
scala里的type classes模式主要通过隐式参数来实现
但需要注意的是,并不是所有的隐式参数都可以理解为type classes模式,隐式参数的类型必须是泛型的(高阶)
它表示某种抽象行为,这种行为的具体实现 要由它的具体类型参数决定
[ http://ropas.snu.ac.kr/~bruno/papers/TypeClasses.pdf ]
//已有类
class A
class 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 class
trait 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的伴生对象
//test
val x = new A
val y = new A
println(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 Matrix
trait Vector
trait 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 trix
scala> implicitly[MultDep[Matrix, Vector, Vector]] // OK: Matrix * Vector -> Ve ctor
scala> 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{}) // Compiles
val r2 : Vector = mult(new Matrix {}, new Vector{}) // Compiles
val r3 : Matrix = mult(new Matrix {}, 2) // Compiles
val r4 : Matrix = mult(2, new Matrix {}) // Compiles
val 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))))