先回顾以下几点:

  • 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()}]"
}

在以下代码中,如何描述i0i1的类型?

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],其中设计到泛型参数为IntUnit

以函子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]
}

相关文章:

  • 2021-12-08
  • 2021-11-15
  • 2021-06-22
  • 2022-01-22
  • 2021-11-22
  • 2022-12-23
  • 2021-10-06
  • 2021-11-20
猜你喜欢
  • 2022-12-23
  • 2021-08-09
  • 2021-05-31
  • 2022-12-23
  • 2022-01-17
  • 2021-05-17
  • 2021-12-12
相关资源
相似解决方案