e[Exercises.deptype.TypeA,Exercises.deptype.T 23 //| ypeB,Exercises.deptype.TypeB] = Exercises.deptype$$anonfun$main$1$abb$2$@24 24 //| 3c4f91
25 implicitly[DepType[TypeA,TypeA,TypeA]] //> res4: Exercises.deptype.DepType[Exercises.deptype.TypeA,Exercises.deptype.T 26 //| ypeA,Exercises.deptype.TypeA] = Exercises.deptype$$anonfun$main$1$aaa$2$@29 27 //| 1ae 28 //implicitly[DepType[TypeA,TypeA,TypeB]] //无法通过编译 could not find implicit value for parameter e: Exercises.deptype.DepType[Exercises.deptype.TypeA,Exercises.deptype.TypeA,Exercises.deptype.TypeB]
29
30 def checkABC[A,B,C](a: A, b: B)(implicit instance: DepType[A,B,C]): C = error("TODO") 31 //> checkABC: [A, B, C](a: A, b: B)(implicit instance: Exercises.deptype.DepTyp 32 //| e[A,B,C])C
33 /*
34 val v_aaa: TypeA = checkABC(new TypeA{},new TypeA{}) 35 val v_iab: TypeB = checkABC(1,new TypeA{}) 36 val v_bbi: Int = checkABC(new TypeB{},new TypeB{}) 37 val v_aab: TypeB = checkABC(new TypeA{}, new TypeA{}) //ype mismatch; found : Exercises.deptype.TypeA required: Exercises.deptype.TypeB 38 */
以上例子利用依赖类型的类型关系实现了类型推导和验证。
函数式编程重视概括抽象以方便函数组合从而实现高度的代码重复使用。因为我们在进行函数式编程时最常遇到的类型款式是这样的:F[A],所以我们在设计函数时会尽量对函数的参数进行针对F[A]的概括。但这样也会对函数的使用者提出了苛刻要求:在调用函数时必须按照要求传人F[A]类型的参数,实际上又限制了函数的通用。Scalaz里的Unapply类型可以把许多不同款式的类型对应成抽离的F[],A和TC。其中TC是个typeclass,用来引导编译器进行类型推导。Unapply trait 如下:scalaz/Unapply.scala
trait Unapply[TC[_[_]], MA] { /** The type constructor */ type M[_] /** The type that `M` was applied to */ type A /** The instance of the type class */ def TC: TC[M] /** Evidence that MA =:= M[A] */ def leibniz: MA === M[A] /** Compatibility. */ @inline final def apply(ma: MA): M[A] = leibniz(ma) }
从定义上分析:Unapply把MA拆分出M[]和A,但使用者必须提供TC - 一个施用在A的typeclass。
好了,我们先用一个简单的例子来分析使用Unapply的背景和具体方式:
1 class TypeWithMap[F[_],A](fa: F[A])(implicit F: Functor[F]) { 2 def doMap[B](f: A => B) = F.map(fa)(f) 3 } 4
5 val mapList = new TypeWithMap(List(1,2,3)) //> mapList : Exercises.unapply.TypeWithMap[List,Int] = Exercises.unapply$$anon 6 //| fun$main$1$TypeWithMap$1@1d9b7cce
7 mapList.doMap {_ + 1} //> res2: List[Int] = List(2, 3, 4)
在这个例子里我们通过传入一个F[A]类型来创建一个TypeWithMap类型实例, F是个Functor。如果我们传入一个List, 因为List的类型款式是F[A]的,所以编译器顺利地把F[A]拆解成F[_]和A, 在例子里就是List和Int。那么如果我们试着传入一个Function1[Int,Int]呢?
1 val mapFunc = new TypeWithMap( (_: Int) * 2 ) 2 //- not enough arguments for constructor TypeWithMap: (implicit F: scalaz.Functor[Any])Exercises.unapply.TypeWithMap[Any,A]. Unspecified value parameter F. 3 //- could not find implicit value for parameter F: scalaz.Functor[Any]
这个东西根本过不了编译。主要是编译器不晓得如何把Function1[A,A]对应成F[A]。我们试试手工把类型款式对应关系提供给编译器:
1 val mapFunc2 = new TypeWithMap[({type l[x] = Function1[Int,x]})#l,Int]((_: Int) * 2) 2 //> mapFunc2 : Exercises.unapply.TypeWithMap[[x]Int => x,Int] = Exercises.unapp 3 //| ly$$anonfun$main$1$TypeWithMap$1@15ff3e9e
4 mapFunc2.doMap {_ + 1}(2) //> res3: Int = 5
看来没问题,不过手工写的还是有点复杂。Unapply是通过提供多种款式的类型隐式转换实例(implicit instance)来进行类型匹配再分拆的。在上面的例子里Unapply提供了这么个款式的类型实例:
/**Unpack a value of type `M0[A0, B0]` into types `[b]M0[A0, b]` and `B`, given an instance of `TC` */
implicit def unapplyMAB2[TC[_[_]], M0[_, _], A0, B0](implicit TC0: TC[({type λ[α] = M0[A0, α]})#λ]): Unapply[TC, M0[A0, B0]] { type M[X] = M0[A0, X] type A = B0 } = new Unapply[TC, M0[A0, B0]] { type M[X] = M0[A |