根据这个问题,Scala的类型系统是Turing完整的.有哪些资源可以让新手利用类型级编程的强大功能?
以下是我到目前为止找到的资源:
这些资源很棒,但我觉得我缺少基础知识,因此没有坚实的基础来构建.例如,哪里有类型定义的介绍?我可以对类型执行哪些操作?
有没有好的入门资源?
dsg*_*dsg 139
概观
类型级编程与传统的价值级编程有许多相似之处.但是,与计算在运行时进行的值级编程不同,在类型级编程中,计算在编译时进行.我将尝试在值级别的编程和类型级别的编程之间绘制相似之处.
范式
类型级编程有两种主要范例:"面向对象"和"功能".从此处链接的大多数示例都遵循面向对象的范例.
面向对象范例中类型级编程的一个很好的,相当简单的例子可以在apocalisp的lambda演算的实现中找到,在这里复制:
// Abstract trait
trait Lambda {
type subst[U <: Lambda] <: Lambda
type apply[U <: Lambda] <: Lambda
type eval <: Lambda
}
// Implementations
trait App[S <: Lambda, T <: Lambda] extends Lambda {
type subst[U <: Lambda] = App[S#subst[U], T#subst[U]]
type apply[U] = Nothing
type eval = S#eval#apply[T]
}
trait Lam[T <: Lambda] extends Lambda {
type subst[U <: Lambda] = Lam[T]
type apply[U <: Lambda] = T#subst[U]#eval
type eval = Lam[T]
}
trait X extends Lambda {
type subst[U <: Lambda] = U
type apply[U] = Lambda
type eval = X
}
Run Code Online (Sandbox Code Playgroud)
从示例中可以看出,面向对象的类型级编程范例如下:
trait Lambda
该保证了以下类型的存在:subst
,apply
,和eval
.trait App extends Lambda
使用两种类型进行参数化(S
并且T
两者都必须是子类型Lambda
),trait Lam extends Lambda
使用一种类型(T
)进行参数化,并且trait X extends Lambda
(未进行参数化).#
这与点运算符非常相似:.
对于值).在App
lambda演算示例的特征中,类型eval
实现如下:type eval = S#eval#apply[T]
.这实质上是调用eval
trait参数的类型S
,并apply
使用参数调用T
结果.注意,S
保证有一个eval
类型,因为参数指定它是一个子类型Lambda
.类似地,结果eval
必须具有apply
类型,因为它被指定为Lambda
抽象特征中指定的子类型Lambda
.功能范例包括定义许多未在特征中组合在一起的参数化类型构造函数.
价值级编程与类型级编程之间的比较
abstract class C { val x }
trait C { type X }
C.x
(引用对象C中的字段值/函数x)C#x
(在特征C中引用字段类型x)def f(x:X) : Y
type f[x <: X] <: Y
这称为"类型构造函数",通常出现在抽象特征中)def f(x:X) : Y = x
type f[x <: X] = x
在类型和值之间转换
在许多示例中,通过traits定义的类型通常都是抽象的和密封的,因此既不能直接实例化也不能通过匿名子类实例化.因此,null
在使用某种类型的兴趣进行值级计算时,通常将其用作占位符值:
val x:A = null
,A
你关心的类型在哪里由于类型擦除,参数化类型看起来都一样.此外,(如上所述)您正在使用的值往往都是null
,因此对对象类型的调节(例如通过匹配语句)是无效的.
诀窍是使用隐式函数和值.基本情况通常是隐式值,递归情况通常是隐式函数.实际上,类型级编程大量使用了implicits.
考虑这个例子(取自metascala和apocalisp):
sealed trait Nat
sealed trait _0 extends Nat
sealed trait Succ[N <: Nat] extends Nat
Run Code Online (Sandbox Code Playgroud)
在这里,你有一个自然数的peano编码.也就是说,每个非负整数都有一个类型:0的特殊类型,即_0
; 并且每个大于零的整数都有一个表单类型Succ[A]
,其中A
是表示较小整数的类型.例如,表示2的类型将是:( Succ[Succ[_0]]
后继应用两次到表示零的类型).
我们可以为各种自然数添加别名,以便更方便地参考.例:
type _3 = Succ[Succ[Succ[_0]]]
Run Code Online (Sandbox Code Playgroud)
(这很像定义val
一个函数的结果.)
现在,假设我们要定义一个值级函数def toInt[T <: Nat](v : T)
,它接受一个参数值,v
符合Nat
并返回一个整数,表示以v
s类型编码的自然数.例如,如果我们有值val x:_3 = null
(null
类型Succ[Succ[Succ[_0]]]
),我们会想要toInt(x)
返回3
.
为了实现toInt
,我们将使用以下类:
class TypeToValue[T, VT](value : VT) { def getValue() = value }
Run Code Online (Sandbox Code Playgroud)
正如我们将在下面看到,会有从类构造的对象TypeToValue
的每个Nat
从_0
高达(例如)_3
,并且每个将存储相应类型的值表示(即,TypeToValue[_0, Int]
将存储的值0
,TypeToValue[Succ[_0], Int]
将存储值1
等).注意,TypeToValue
参数化有两种类型:T
和VT
.T
对应于我们尝试将值赋给的类型(在我们的示例中Nat
),并且VT
对应于我们分配给它的值的类型(在我们的示例中Int
).
现在我们进行以下两个隐式定义:
implicit val _0ToInt = new TypeToValue[_0, Int](0)
implicit def succToInt[P <: Nat](implicit v : TypeToValue[P, Int]) =
new TypeToValue[Succ[P], Int](1 + v.getValue())
Run Code Online (Sandbox Code Playgroud)
我们实施toInt
如下:
def toInt[T <: Nat](v : T)(implicit ttv : TypeToValue[T, Int]) : Int = ttv.getValue()
Run Code Online (Sandbox Code Playgroud)
为了理解它是如何toInt
工作的,让我们考虑它对几个输入的作用:
val z:_0 = null
val y:Succ[_0] = null
Run Code Online (Sandbox Code Playgroud)
当我们调用时toInt(z)
,编译器会查找ttv
类型的隐式参数TypeToValue[_0, Int]
(因为z
是类型_0
).它找到了对象_0ToInt
,它调用了getValue
这个对象的方法并返回0
.需要注意的重点是,我们没有向程序指定使用哪个对象,编译器隐式地发现了它.
现在让我们考虑一下toInt(y)
.这次,编译器会查找ttv
类型的隐式参数TypeToValue[Succ[_0], Int]
(因为y
是类型Succ[_0]
).它找到了函数succToInt
,它可以返回相应类型(TypeToValue[Succ[_0], Int]
)的对象并对其进行求值.此函数本身采用类型的隐式参数(v
)TypeToValue[_0, Int]
(即TypeToValue
第一个类型参数少一个的地方Succ[_]
).编译器提供_0ToInt
(如toInt(z)
上面的评估中所做的那样),并succToInt
构造一个TypeToValue
具有值的新对象1
.同样,重要的是要注意编译器隐式提供所有这些值,因为我们没有显式访问它们.
检查你的工作
有几种方法可以验证您的类型级计算是否正在按预期执行.以下是一些方法.使两种类型A
和B
,要确认是相等的.然后检查以下编译:
Equal[A, B]
Equal[T1 >: T2 <: T2, T2]
(取自apocolisp)implicitly[A =:= B]
或者,您可以将类型转换为值(如上所示)并对值进行运行时检查.例如assert(toInt(a) == toInt(b))
,a
类型A
和b
类型的位置B
.
其他资源
可以在scala参考手册(pdf)的类型部分中找到完整的可用构造集.
Adriaan Moors有几篇关于类型构造函数和相关主题的学术论文,以及来自scala的示例:
Apocalisp是一个博客,其中包含许多scala中的类型级编程示例.
ScalaZ是一个非常活跃的项目,它提供了使用各种类型级编程功能扩展Scala API的功能.这是一个非常有趣的项目,有很大的追随者.
MetaScala是Scala的类型级库,包括自然数的元类型,布尔值,单位,HList等.这是Jesper Nordenberg(他的博客)的一个项目.
该Michid(博客)在斯卡拉型高级编程的一些例子真棒(从对方的回答):
Debasish Ghosh(博客)也有一些相关的帖子:
(我一直在研究这个问题,这是我所学到的.我还是新手,所以请指出这个答案中的任何不准确之处.)
mic*_*hid 12
除了这里的其他链接,还有我在Scala中关于类型级元编程的博客文章: