异构类型列表
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 theHCons
andHNil
classes with the same definition. The answer is the full type of the list is required when constructing a newHCons
cell. If you placed this definition onHList
, the captured typeT
in any new HCons cell would always be onlyHList
. 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
这个语句,这里比前面三个实现多了两个事情:
- 类型lambda
- 带参数的Expand
实现主体方法
定义对应的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一书中给出了如下四个方法的实现:
- remove
- insertBefore
- replace
- insertAfter
书中给出的使用=:::=方法的实现为:
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