异构类型列表

2018-01-15

这里的异构类型列表(Heterogeneous Type List)是《scala in depth》一书在第七章第四节的一个关于Scala类型系统和隐式转换的一个例子,这里是此例子的对应笔记以及相应练习的实现。在项目shapeless和sbt里面都有类似的实现。如书中所提到的:

这是一种类型安全的不限定元素个数的列表。有点类似于Scala里的TupleN类,但是支持通过append操作来为列表添加更多的类型。

最简单的版本

最简单的版本是下面这样的实现,也就是书中给出来的实现。

sealed trait HList

final case class HCons[H, T <: HList](head: H, tail: T) extends HList {
  def ::[T](v: T) = HCons(v, this)
  override def toString: String = s"${head} :: ${tail}"
}

final class HNil extends HList {
  def ::[T](v: T) = HCons(v, this)
  override def toString: String = "HNil"
}

object HList {
  type ::[H, T <: HList] = HCons[H, T] // (1)
  val :: = HCons // (2)
  val HNil = new  HNil
}

注意一下HList里的=type ::=和=val ::=。(1)处的=::=表示类型=HCons=,(2)处的=::=表示类型=HCons=对应的伴生对象(由编译器生成)。这两个别名可以让使用更为简单一些,前者简化了异构类型列表对象的构造,后者简化了模式匹配的书写。使用效果如下:

scala> import HList._
import HList._

// (1)
scala> val a = 5 :: "hello world!" :: 3.6 :: true :: HNil
a: HCons[Int,HCons[String,HCons[Double,HCons[Boolean,HNil]]]] = 
  5 :: hello world! :: 3.6 :: true :: HNil

// (2)
scala> a.head
res0: Int = 5

// (3)
scala> a.tail
res1: HCons[String,HCons[Double,HCons[Boolean,HNil]]] = hello world! :: 3.6 :: true :: HNil

// (4)
scala> val b :: _ = a
b: Int = 5

// (5)
scala> val b :: c :: _ = a
b: Int = 5
c: String = hello world!

(1)处相当于定义了一个类型为=(Int, String, Double, Boolean)=的列表a(*注意类型在编译期就已确定*).(2)处和(3)处分别取列表的头元素,其对应类型为=Int=和尾部,其类型为=(String, Double, Boolean)=,*这里类型也是编译期就已经确定*。(4)处和(5)处使用模式匹配来提取列表中的元素。

如果没有

type ::[H, T <: HList] = HCons[H, T]
val :: = HCons

那就需要这么写:

val x = HCons("Hello", HCons(5, HCons(false, HNil)))
val HCons(y, _) = x

明显要繁杂很多。

作者关于此实现的一个注解:

WHY THE DUPLICATED :: METHOD? You may be wondering why, in the simple HList implementation, the :: method is defined in both the HCons and HNil classes with the same definition. The answer is the full type of the list is required when constructing a new HCons cell. If you placed this definition on HList, the captured type T in any new HCons cell would always be only HList. This negates the desired effect of preserving the type information in the list. The source code we include in this book, and describe later, has a solution to the problem by using a secondary trait, HListLike[FullListType], that captures the complete type of the current list and defines the :: method using this type.

实现InitAndLastView

上面的版本,异构类型列表只能够取出=head=和=tail=。在这里添加对应取出=init=和=last=的实现。

首先加入一个trait,用来表示InitAndLast方法返回的类型:

trait InitAndLastView {
  type init <: HList
  type last
  def last: last
  def init: init
}

这个trait是必须添加的,否则相应方法的返回类型写不出来。然后添加两个对应的实现,分别对应只有一个元素的情形和多于一个元素的情形。

class InitAndLastViewHNil[H](val x: H) extends InitAndLastView {
  import HList._
  type init = HNil
  type last = H
  def last = x
  def init = HNil
}

class InitAndLastViewHCons[H, NextLastView <: InitAndLastView](
    val x: H, val v: NextLastView) extends InitAndLastView {
  import HList._
  type init = H :: NextLastView#init
  type last = v.last
  def last = v.last
  def init = HCons(x, v.init) // 为何这里不能写 x :: v.init
}

然后在trait =HList=里面声明一个名为=AsInitAndLast=的抽象类型:

sealed trait HList {
  type AsInitAndLast <: InitAndLastView
}

接下来就是*使用类型来做条件判断*,在trait =HList=里面添加一个名为=Expand=的类型:

sealed trait HList {
  type Expand[IfHCons <: Up, IfHNil <: Up, Up] <: Up
  type AsInitAndLast <: InitAndLastView
}

先在HCons里和HNil里具体定义=Expand=类型:

final case class HCons[H, T <: HList](head: H, tail: T) extends HList {
    type Expand[IfHCons <: Up, IfHNil <: Up, Up] = IfHCons
    // 其他代码
}

final class HNil extends HList {
    type Expand[IfHCons <: Up, IfHNil <: Up, Up] = IfHNil
    // 其他代码
}

这样再定义具体的=AsInitAndLast=抽象类型:

final case class HCons[H, T <: HList](head: H, tail: T) extends HList {
  type Expand[IfHCons <: Up, IfHNil <: Up, Up] = IfHCons
  // (1)
  type AsInitAndLast = T#Expand[
    InitAndLastViewHCons[H, T#AsInitAndLast],
    InitAndLastViewHNil[H],
    InitAndLastView
    ]
  def ::[T](v: T) = HCons(v, this)
  override def toString: String = s"${head} :: ${tail}"
}

注意(1)处所定义的类型=AsInitAndLast=,如果类型=T=是=HCons=,那么类型投影将会选择

InitAndLastViewHCons[H, T#AsInitAndLast]

,然后=T#AsInitAndLast=这个类型接着按照这里的逻辑递归做出选择,如果类型=T=是=HNil=,则类型投影选择=InitAndLastViewHNil[H]=,这也是前一个递归过程的递归基。

=HNil=类中无需定义=type AsInitAndLast=,因为=HNil=不须支持这个功能。

至此其实类型相关的功能已经支持:

scala> import HList._
import HList._

scala> val a = 5 :: "hello world" :: 3.5 :: true :: HNil
a: HCons[Int,HCons[String,HCons[Double,HCons[Boolean,HNil]]]] = 
  5 :: hello world :: 3.5 :: true :: HNil

// (1)
scala> type X = a.AsInitAndLast#init
defined type alias X

// (2)
scala> val b: X = 4 :: "hello world" :: 3.7 :: HNil
b: X = 4 :: hello world :: 3.7 :: HNil

// (3)
scala> val c: X = 4 :: 3.7 :: "hello world" :: HNil
<console>:15: error: type mismatch;
 found   : HCons[Int,HCons[Double,HCons[String,HNil]]]
 required: X
    (which expands to)  HCons[Int,HCons[String,HCons[Double,HNil]]]
       val c: X = 4 :: 3.7 :: "hello world" :: HNil
                    ^

// (4)
scala> type Y = a.AsInitAndLast#last
defined type alias Y

// (5)
scala> val d: Y = true
d: Y = true

// (6)
scala> val e: Y = 3.5
<console>:15: error: type mismatch;
 found   : Double(3.5)
 required: Y
    (which expands to)  Boolean
       val e: Y = 3.5
                  ^

上例中(1)处获取了=a=中的=init=类型,(4)处获取了=a=中的=last=类型,(2)和(5)两处编译通过,(3)和(6)两处编译失败。

接着实现具体的init and last方法,先添加两个implicit方法:

implicit def initLast0[H](x : H :: HNil) = new InitAndLastViewHNil[H](x.head)
implicit def initLastN[H, T <: HList, Prev <: InitAndLastView](
      x: H :: T)(implicit prev: T => Prev) = {
  new InitAndLastViewHCons[H, Prev](x.head, prev(x.tail))
}

其中=initLast0=用于生成只含一个元素的列表的InitAndLastView,*=initLastN=会一步一步递归下去,直到递归至=initLast0=,这个也是一个很精妙的技巧*。

到了这里,应该尝试手动调用=initLastN=方法:

scala> import HList._
import HList._

scala> val x = "Hello world!" :: 42 :: true :: HNil
x: HCons[String,HCons[Int,HCons[Boolean,HNil]]] = 
    Hello world! :: 42 :: true :: HNil

scala> initLastN(x)
<console>:16: error: No implicit view available from 
    HCons[Int,HCons[Boolean,HNil]] => Prev.
       initLastN(x)
                ^

调用失败是因为上面的例子中推断不出=Prev=的具体类型,使用如下的例子:

scala> import HList._
import HList._

scala> val x = "Hello world!" :: 42 :: true :: HNil
x: HCons[String,HCons[Int,HCons[Boolean,HNil]]] = 
  Hello world! :: 42 :: true :: HNil

// (1)
scala> val y: x.AsInitAndLast = initLastN(x)
y: x.AsInitAndLast = InitAndLastViewHCons@2313db84

scala> y.init
res0: HCons[String,HCons[Int,HCons[Boolean,HNil]]#AsInitAndLast#init] = 
  Hello world! :: 42 :: HNil

scala> y.last
res1: y.v.last = true

此例和上例的区别就在于,(1)处制定了具体的返回类型,因此可以推断出=Prev=。

接下来为=HCons=添加类型变量=FullType=和方法=asInitAndLast=。

type FullType = H :: T
def asInitAndLast(implicit in: FullType => FullType#AsInitAndLast) = in(this)

此处=in=的类型可以完整的推断出=initLastN=方法中所需要的所有类型参数,因此整个部分便可以正常工作:

scala> import HList._
import HList._

scala> val x = "Hello world!" :: 42 :: true :: HNil
x: HCons[String,HCons[Int,HCons[Boolean,HNil]]] 
    = Hello world! :: 42 :: true :: HNil

scala> x.asInitAndLast.init
res0: HCons[String,HCons[Int,HCons[Boolean,HNil]]#AsInitAndLast#init] 
    = Hello world! :: 42 :: HNil

scala> x.asInitAndLast.last
res1: Boolean = true

为了方便使用,单独在=HCons=内定义=init=和=last=方法:

def init(implicit in: FullType => FullType#AsInitAndLast) = in(this).init
def last(implicit in: FullType => FullType#AsInitAndLast) = in(this).last

在实现这个特性的过程中,在=trait InitAndLastViewHCons=曾经使用了错误的类型:

type last = v.last

导致=HCons=中定义=last=时爆出

cyclic aliasing or subtyping involving type last

的错误。

实现Append方法

此节之前的HList中,构造具体实例都是通过头部添加的,在这一节里面,实现一个从尾部添加的方法。

这个功能的实现和=asInitAndLast=基本一样。同样的,先添加一个名为=AppendView=的trait,和相应的两个实现,其中=AppendView0[T]=相当是=HNil=这个类=append=时的返回,按照一般的逻辑,空列表当然也可以append元素,=AppendViewN=相当是=HCons=这个类=append=时的返回。

sealed trait AppendView {
  type Appended <: HList
  def get: Appended
}

class AppendView0[T](val t : T) extends AppendView {
  import HList._
  type Appended = T :: HNil
  def get = t :: HNil
}

class AppendViewN[H, A <: AppendView](val h: H, val a: A) extends AppendView {
  import HList._
  type Appended = H :: A#Appended
  def get = HCons(h, a.get)
}

然后先添加=HNil=里面的实现,这个很简单:

final class HNil extends HList {
  type Expand[IfHCons <: Up, IfHNil <: Up, Up] = IfHNil
  def ::[T](v: T) = HCons(v, this)
  type Append[T] = AppendView0[T] // 新添类型
  def append[T](t: T) = new AppendView0[T](t) // 新添方法
  override def toString: String = "HNil"
}

为了添加在=HCons=里的实现,先把trait =HList=添加一个抽象类型:

sealed trait HList {
    type AsInitAndLast <: InitAndLastView
    type Append[T] <: AppendView // 新添抽象类型
    type Expand[IfHCons <: Up, IfHNil <: Up, Up] <: Up
}

然后在=HCons=类中添加=Append[T]=的定义,这里类型参数使用=T1=是为了避免覆盖掉类定义里的类型参数=T=

type Append[T1] = AppendViewN[H,T#Append[T1]]

这里也可以使用=Expand=来做选择,但是没什么必要:

type Append[T1] = FullType#Expand[
  AppendViewN[H,T#Append[T1]],
  AppendView0[T1],
  AppendView
]

这时候类型部分应该是可以工作了:

scala> import HList._
import HList._

scala> val x = "Hello world!" :: 42 :: HNil
x: HCons[String,HCons[Int,HNil]] = Hello world! :: 42 :: HNil

scala> type Y = x.Append[Boolean]#Appended
defined type alias Y

scala> val y: Y = "Hello world!" :: 42 :: 3.5 :: HNil
<console>:15: error: type mismatch;
 found   : HCons[String,HCons[Int,HCons[Double,HNil]]]
 required: Y
    (which expands to)  HCons[String,HCons[Int,HCons[Boolean,HNil]]]
       val y: Y = "Hello world!" :: 42 :: 3.5 :: HNil
                                 ^

scala> val y: Y = "Hello world!" :: 42 :: false :: HNil
y: Y = Hello world! :: 42 :: false :: HNil

可见上面Y是类型=HCons[String,HCons[Int,HCons[Double,HNil]]]=,定义=y=为

"Hello world!" :: 42 :: 3.5 :: HNil

编译失败。定义=y=为

"Hello world!" :: 42 :: false :: HNil

时成功通过。同样的,接着定义两个隐式方法,因为=HNil=里的实现是trival case,这里的两个隐式方法都是给=HCons=所准备的,其中=append0=是递归基,适用于只有一个元素的情形。

implicit def append0[H, T](x : H :: HNil, t: T) = new AppendViewN(x.head, new AppendView0(t))
implicit def appendN[H, T <: HList, T1, Prev <: AppendView](x : H :: T, t: T1)
    (implicit prev: (T, T1) => Prev)
  = new AppendViewN(x.head, prev(x.tail, t))

同样的,可以尝试手动调用方法=appendN=:

scala> import HList._
import HList._

scala> val a = "hello world!" :: 3.5 :: 2 :: true :: HNil
a: HCons[String,HCons[Double,HCons[Int,HCons[Boolean,HNil]]]] 
  = hello world! :: 3.5 :: 2 :: true :: HNil

scala> appendN(a, 5)
<console>:16: error: could not find implicit value for parameter prev: 
  (HCons[Double,HCons[Int,HCons[Boolean,HNil]]], Int) => Prev
       appendN(a, 5)
              ^

scala> val b: a.Append[Int] = appendN(a, 5)
b: a.Append[Int] = AppendViewN@4ee8051c

scala> b.get
res1: HCons[String,HCons[Double,HCons[Int,HCons[Boolean,HNil]]]#Append[Int]#Appended] 
 = hello world! :: 3.5 :: 2 :: true :: 5 :: HNil

scala> val c: b.Appended = "hello world!" :: 3.5 :: 2 :: true :: 3.2 :: HNil
<console>:15: error: type mismatch;
 found   : HCons[String,HCons[Double,HCons[Int,HCons[Boolean,HCons[Double,HNil]]]]]
 required: b.Appended
    (which expands to)  HCons[String,HCons[Double,HCons[Int,HCons[Boolean,HCons[Int,HNil]]]]]
       val c: b.Appended = "hello world!" :: 3.5 :: 2 :: true :: 3.2 :: HNil

同样的,直接调用=appendN(a,5)=时由于不能推导出=Prev=而报错,添加指定的返回类型=a.Append[Int]=后成功编译。在=c=的定义中可以发现=appendN(a,5)=后的类型是

HCons[String,HCons[Double,HCons[Int,HCons[Boolean,HCons[Int,HNil]]]]]

最在HCons里面定义=append=方法就可以正常使用了:

final case class HCons[H, T <: HList](head: H, tail: T) extends HList {

    // 其他代码

    def append[T](t: T)(implicit in: (FullType, T) => FullType#Append[T]) = in(this, t)

    // 其他代码
}

示例:

scala> import HList._
import HList._

scala> val a = "3.5" :: 20 :: 4.7 :: true :: HNil
a: HCons[String,HCons[Int,HCons[Double,HCons[Boolean,HNil]]]] 
    = 3.5 :: 20 :: 4.7 :: true :: HNil

scala> val b = a.append(false)
b: AppendViewN[String,HCons[Int,HCons[Double,HCons[Boolean,HNil]]]#Append[Boolean]] 
    = AppendViewN@545d2560

scala> b.get
res0: HCons[String,HCons[Int,HCons[Double,HCons[Boolean,HNil]]]#Append[Boolean]#Appended] 
    = 3.5 :: 20 :: 4.7 :: true :: false :: HNil

实现Join

上面的Append是从尾部添加一个元素,而Join则是把两个HList连成一个。

和上面的实现基本一致,首先实现trait =JoinView=及两个派生类:

sealed trait JoinView {
    type Joined <: HList
    def get: Joined
}

class JoinView0[T <: HList](val t: T) extends JoinView {
    type Joined = T
    def get = t
}

class JoinViewN[H, NextJoinView <: JoinView]
  (val h: H, x: NextJoinView) extends JoinView {
    type Joined = H :: NextJoinView#Joined
    def get = HCons(h, x.get)
}

然后在HList添上对应类型:

sealed trait HList {
  type AsInitAndLast <: InitAndLastView
  type Append[T] <: AppendView
  type Join[H <: HList] <: JoinView
  type Expand[IfHCons <: Up, IfHNil <: Up, Up] <: Up
}

同样先处理=HNil=里的实现:

final class HNil extends HList {
    // 其他方法

    type Join[H <: HList] = JoinView0[H] // 新添类型
    def Join[T <: HList](t: T) = new JoinView0[T](t) // 新添方法
    def :::[T <: HList](t: T) = t // 新添方法
}

然后处理HCons中的实现,和上面的方法一样,定义两个隐式方法:

implicit def join0[T <: HList](x : HNil, t: T) = new JoinView0(t)
implicit def joinN[H, T1 <: HList, T <: HList, Prev <: JoinView](
    x : H :: T1, t: T)(implicit prev : (T1, T) => Prev)
  = new JoinViewN(x.head, prev(x.tail, t))

HCons中添加相应类型及方法:

final case class HCons[H, T <: HList](head: H, tail: T) extends HList {

    // 其他代码

    type Join[H1 <: HList] = T#Expand[
      JoinViewN[H, T#Join[H1]],
      JoinViewN[H, JoinView0[H1]],
      JoinView
    ]

    def Join[T <: HList](t: T)(
      implicit in: (FullType, T) => FullType#Join[T]
      ) = in(this, t)
    def :::[T <: HList](t: T)(
      implicit in: (T, FullType) => T#Join[FullType]
    ) = in(t, this).get

    // 其他代码

  }

就可以正常工作了。

实现下标索引

这个是Scala In Depth书中原有的例子,这个例子比以上三个功能都要难,虽然大致思路一样,*但这个例子中用到了类型lambda,而上面三个类型中都没有用上*,这个例子中,功能类似于=Expand=的类型也复杂一点(带有参数),在实现过程中,我也给HList添了一个带参数的用于选择的类型,但是没有用上:

// 未使用
type ExpandJoin[IfHCons[H, T <: HList] <: Up, IfHNil <: Up, Up] <: Up

同样的,和之前InitAndLast、Append、Join一样,先实现=trait IndexedView=及对应的两个类:

sealed trait IndexedView {
    type Before <: HList
    type After <: HList
    type At
    def fold[R](f: (Before, At, After) => R): R
    def get = fold( (_, value, _) => value)
}

class HListView0[H, T <: HList](val list : H :: T) extends IndexedView {
    type Before = HNil
    type After = T
    type At = H
    def fold[R](f: (Before, At, After) => R) : R = f(HNil, list.head, list.tail)
}

final class HListViewN[H, NextIdxView <: IndexedView](h: H, next: NextIdxView) extends IndexedView {
    type Before = H :: NextIdxView#Before
    type At = NextIdxView#At
    type After = NextIdxView#After

    def fold[R](f: (Before, At, After) => R) : R = next.fold(
      (before, at, after) => f(HCons(h, before), at, after)
    )
}

这个就是scala in depth原书中的代码,可以和上面其他功能的实现比较一下。

将自然数嵌到类型系统中

这部分是和之前的功能不大一样的地方,*原来的append、join 、initAndLast都可以用列表本身进行类型的递归定义(当一个HList定义出来之后,就定义了一个具体的类型,然后可以用这个类型一步步往下推)*,但实现下标索引中,需要另添这一部分代码:

sealed trait Nat {
    type Expand[NonZero[N <: Nat] <: Up, IfZero <: Up, Up] <: Up
}

object Nat{
    sealed trait _0 extends Nat {
      type Expand[NonZero[N <: Nat] <: Ret, IfZero <: Ret, Ret] = IfZero
    }
    sealed trait Succ[Prev <: Nat] extends Nat {
      type Expand[NonZero[N <: Nat] <: Ret, IfZero <: Ret, Ret] = NonZero[Prev]
    }
    type _1 = Succ[_0]
    type _2 = Succ[_1]
    type _3 = Succ[_2]
    type _4 = Succ[_3]
    type _5 = Succ[_4]
    type _6 = Succ[_5]
    type _7 = Succ[_6]
    type _8 = Succ[_7]
    type _9 = Succ[_8]
    type _10 = Succ[_9]
    type _11 = Succ[_10]
    type _12 = Succ[_11]
    type _13 = Succ[_12]
    type _14 = Succ[_13]
    type _15 = Succ[_14]
    type _16 = Succ[_15]
    type _17 = Succ[_16]
    type _18 = Succ[_17]
    type _19 = Succ[_18]
    type _20 = Succ[_19]
    type _21 = Succ[_20]
    type _22 = Succ[_21]
    type _23 = Succ[_22]
    type _24 = Succ[_23]
    type _25 = Succ[_24]
    type _26 = Succ[_25]
    type _27 = Succ[_26]
    type _28 = Succ[_27]
    type _29 = Succ[_28]
    type _30 = Succ[_29]
}

如前所述,=Nat#Expand=带了参数=N <: Nat=。然后补上HList和HCons中的类型定义:

sealed trait HList {
  type Expand[IfHCons <: Up, IfHNil <: Up, Up] <: Up
  type AsInitAndLast <: InitAndLastView
  type Append[T] <: AppendView
  type Join[H <: HList] <: JoinView
  type ViewAt[Idx <: Nat] <: IndexedView
}

以及:

final case class HCons[H, T <: HList](head: H, tail: T) extends HList {

    // 其他代码

    type ViewAt[N <: Nat] = N#Expand[
        ({type Z[P <: Nat] = HListViewN[H, T#ViewAt[P]]})#Z,
        HListView0[H, T],
        IndexedView
    ]

    // 其他代码
}

注意

({type Z[P <: Nat] = HListViewN[H, T#ViewAt[P]]})#Z

这个语句,这里比前面三个实现多了两个事情:

实现主体方法

定义对应的implicit方法:

implicit def index0[H, T <: HList](list : H :: T): HListView0[H, T] = {
  new HListView0[H, T](list)
}
implicit def indexN[H, T <: HList, Prev <: IndexedView](list : H :: T)(
  implicit indexTail: T => Prev): HListViewN[H, Prev] ={
  new HListViewN[H, Prev](list.head, indexTail(list.tail))
}

以及HCons中的方法:

def ViewAt[Idx <: Nat](implicit in: FullType => FullType#ViewAt[Idx]) = in(this)

就可以正常工作了:

scala> val a = "hello world!" :: 3.5 :: 20 :: true :: HNil
a: /* type info */ = hello world! :: 3.5 :: 20 :: true :: HNil

scala> a.ViewAt[_0].get
res2: String = hello world!

scala> a.ViewAt[_1].get
res3: Double = 3.5

scala> a.ViewAt[_2].get
res4: Int = 20

scala> a.ViewAt[_3].get
res5: Boolean = true

scala> a.ViewAt[_4].get
<console>:19: error: No implicit view available /* ... */
       a.ViewAt[_4].get
               ^

在IndexedView中添加更多方法

这也是Scala In Depth提到的,为何上面要单独放一个fold方法出来的原因,Scala In Depth一书中给出了如下四个方法的实现:

书中给出的使用=:::=方法的实现为:

def remove = fold {
  (before, _, after) => before ::: after
}

def insertBefore[B](x : B) = fold {
  (before, current, after) => 
    before ::: (x :: current :: after)
}

def replace[B](x : B) = fold {
  (before, _, after) => before ::: (x :: after)
}

def insertAfter[B](x : B) = fold {
  (before, current, after) => 
    before ::: (current :: x :: after)
}

但由于我在实现=:::=方法时,使用到了隐式参数,没办法按照上面的方式来实现,实际的实现如下:

def remove(implicit in: (Before, After) => Before#Join[After]) = fold {
  (before, _, after) => in(before, after).get
}

def insertBefore[B](x: B)(implicit in: (Before, B :: At :: After) =>
  Before#Join[B::At::After]) = fold {
  (before, current, after) => in(before, HCons(x, HCons(current, after))).get
}

def insertAfter[B](x: B)(implicit in: (Before, At :: B :: After) =>
  Before#Join[At::B::After]) = fold {
  (before, current, after) => in(before, HCons(current, HCons(x, after))).get
}

def replace[B](x: B)(implicit in: (Before, B :: After) =>
  Before#Join[B::After]) = fold {
  (before, _, after) => in(before, HCons(x, after)).get
}

和示例实现的区别主要在这四个方法都加了一个隐式参数,另外一点就是尝试在实现中使用方法=:::=失败(即使传入了所需的隐式参数),因此实际实现中也直接使用了=in=方法。

使用示例:

// two part tests
val xViewAt2 = x.ViewAt[Nat._2]
println(xViewAt2.get)
println(xViewAt2.replace("30"))
// can't compile
// Error:(266, 22) could not find implicit value for parameter in:
// (xViewAt2.Before, xViewAt2.After) => xViewAt2.Before#Join[xViewAt2.After]
// println(xViewAt2.remove)
println(xViewAt2.insertAfter(true))
println(xViewAt2.insertAfter(false))

// one part tests
println(x.ViewAt[Nat._2].get)
println(x.ViewAt[Nat._2].replace("30"))
println(x.ViewAt[Nat._2].remove)
println(x.ViewAt[Nat._2].insertAfter(true))
println(x.ViewAt[Nat._2].insertAfter(false))

但如示例中所写的,连着使用=remove=不会有编译失败的错误

x.ViewAt[Nat._2].remove

但是拆开使用时就会报出隐式变量获取不到的错误:

// Error:(266, 22) could not find implicit value for parameter in:
// (xViewAt2.Before, xViewAt2.After) => xViewAt2.Before#Join[xViewAt2.After]
val xViewAt2 = x.ViewAt[Nat._2]
println(xViewAt2.remove)

总结

这个笔记基本就是Scala里type level programming的一个示例,相关的repo放在onriv/HeterogeneousTypeListInScala。目前我还没有在实际需要使用异构类型列表的例子。实现中类型相关的部分问题应该还很大,可以参考对照一下shapeless里的实现:shapeless/hlists.scala at master · milessabin/shapeless