先回顾以下几点:
- scala的类型投影
- 类型别名
- scala中的函数柯里化
scala的类型投影
同java类似,scala中也存在内部类和外部类。
但是与java的内部类不同的时,scala的内部类实例类型与其路径相关,比如
class Outer {
thisOuter =>
class Inner {
thisInner =>
override def toString: String = s"[${thisOuter}].[${thisInner.hashCode()}]"
}
override def toString: String = s"[${thisOuter.hashCode()}]"
}
在以下代码中,如何描述i0
和i1
的类型?
val o0 = new Outer()
val i0 = new o0.Inner()
val o1 = new Outer()
val i1 = new o1.Inner()
对于i0
的类型是o0.Inner
,而i1
的类型是o1.Inner
,此时,将该形式的类型称为路径依赖类型
。
在具体编程实践中,需要代码更加通用,不可以为每一个Inner
实例都写一遍其具体的路径依赖类型,,于是就有了投影类型
,关键字为#
,使用方式为A#B
,表示每一个类型A实例下的B类型,而不需要区分哪一个实例A。
val o0 = new Outer()
val i0: o0.Inner = new o0.Inner()
val o1 = new Outer()
val i1: o1.Inner = new o1.Inner()
val i0_0: Outer#Inner = i0
val i1_0: Outer#Inner = i1
可以类比于从上海坐飞机、走铁路、走高速都可以去北京,也可以简洁的说从上海去北京
仅此而已,不需要讲具体的方式。
注意:以上所说的都是关于类型,而不是类。
类型别名
在scala中源码中,已经存在大量的类型别名的预定义,比如
type String = java.lang.String
type Class[T] = java.lang.Class[T]
type Function[-A, +B] = Function1[A, B]
type Map[A, +B] = immutable.Map[A, B]
在编程实践中,可以使用类型别名来赋予一般类型业务含义。
scala中的函数柯里化
函数柯里化,作用是给函数传递一部分参数,使其返回新函数来处理剩下的参数
。
使用豆子和水做成豆浆举例,豆子Bean
和水Water
两个参数,进过某些函数作用生成豆浆BeanMilk
。原函数如下表示
def makeBeanMilk1(b: Bean, w: Water): BeanMilk = {
b.doSomeThingWith(w)
}
以上函数可以写成以下柯里化形式:
def makeBeanMilk2(b: Bean)(w: Water): BeanMilk = {
b.doSomeThingWith(w)
}
当函数makeBeanMilk2
仅一个Bean参数时,可以得到新函数Water => BeanMilk
,表示已经接收到了一个Bean
参数,现在只缺一个Water
参数。
val makeBeanMilk2WithBean: Water => BeanMilk = makeBeanMilk2(Bean(1))
以上的柯里化形式的函数makeBeanMilk2
等价于
def makeBeanMilk3: Bean => Water => BeanMilk = {
b => w => b.doSomeThingWith(w)
}
Type Lambda
Type Lambda
以下简称为类型lambda
,其作用简而言之为构造器类型参数的柯里化
。
(x:Int)=>println(x)
该语句的类型为Int=>Unit
或者Function1[Int,Unit]
,其中设计到泛型参数为Int
和Unit
。
以函子Functor
举例,函子是对函数式编程中map
转换函数的抽象。
trait Functor[F[_]] {
def map[A, B](fa: F[A])(fun: A => B): F[B]
}
以上代码,泛型参数F[_]
表示类型F还有一个高阶类型参数,这里把F类比为Option、List、Set等,而这里的_
用法与存在类型或者偏应用函数并不同,仅仅表示泛型参数F还有泛型参数。
type F1 = Functor[Option]
type F2 = Functor[List]
以上可以编译,但是对于Map来说编译不通过
type F3 = Functor[Map]
//Map takes two type parameters, expected: one
那么该如何解决呢?
思路为将两个类型参数变为一个类型参数即可编译通过。
解决:将Map类型再次声明一个新的类型,由两个泛型参数变为一个。
type IntAMap[A] = Map[Int, A]
type F3 = Functor[IntAMap]
以上产生了一个不需要的新类型IntAMap,将其省略进而写为
type F3 = Functor[({type IntAMap[A] = Map[Int, A]})#IntAMap]
类型lambda与函数柯里化的相似点是,函数柯里化设置了部分参数后返回新函数,新函数只需要传入另一部分的参数。类型lambda设置了一部分的泛型参数,返回了只需要另外一部分泛型参数的构造器。
注意{type IntAMap[A] = Map[Int, A]}
的语法,使用大括号{}
表明为结构类型,至于该结构类型的具体类型名称不需要关注,再使用#
将其内部的类型别名投影出来。
val instance = new {
type AA = String
val aa: AA = "sd"
type T[A] = Map[Int, A]
}