概述
类型层次的编程与传统,价值层次的编程有许多相似之处。但是,与运行时发生计算的值级编程不同,在类型级编程中,计算发生在编译时。我将尝试在价值层次的编程和类型层次的编程之间进行比较。
范式
有在类型级别的编程两个主要范式:“面向对象”和“功能”。从这里链接到的大多数例子遵循面向对象的范例。
在面向对象范型级编程的一个很好的,很简单的例子可以在apocalisp的implementation of the lambda calculus发现,这里复制:
// 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
}
正如在本例中可以看出,面向对象类型级编程的范例如下进行:
- 第一:用各种抽象类型字段定义一个抽象特征(参见下文抽象字段是什么)。这是一个用于保证所有实现中都存在某些类型字段而不强制执行的模板。在lambda微积分示例中,这对应于
trait Lambda
,它确保存在以下类型:subst
,apply
和eval
。
- 下一页:定义扩展抽象性状subtraits和实施各种抽象类型字段
- 通常,这些subtraits将与参数进行参数化。在演算例如,亚型是
trait App extends Lambda
其是参数化与两种类型(S
和T
,两者都必须是Lambda
亚型),trait Lam extends Lambda
参数与一种类型(T
),并trait X extends Lambda
(这不是参数化)。
- 类型字段通常通过引用减法类型参数来实现,有时通过哈希运算符引用其类型字段:
#
(与点运算符:值为.
非常相似)。在lambda微积分示例的特征App
中,类型eval
按如下实现:type eval = S#eval#apply[T]
。这本质上是调用eval
类型的特征参数S
,并调用apply
与参数T
对结果。请注意,S
保证有一个eval
类型,因为该参数指定它是Lambda
的子类型。类似地,eval
的结果必须具有apply
类型,因为它被指定为Lambda
的子类型,如抽象特征Lambda
中所指定的那样。
的功能范例包括定义许多未在特征组合在一起,参数化类型构造的。
值电平编程和类型级编程的比较
- abstract class
- 值级别:
abstract class C { val x }
- 类型级:
trait C { type X }
- 路径依赖类型
C.x
(引用字段值/功能在对象C X)
C#x
(在性状ç引用字段类型为x)
- 函数签名(未实施)
- 值电平:
def f(x:X) : Y
- type-level:
type f[x <: X] <: Y
(这称为“类型构造函数”,通常发生在抽象特征中)
- 函数实现
- 值级别:
def f(x:X) : Y = x
- 类型级:
type f[x <: X] = x
- 条件句
- 检查平等
- 值级别:
a:A == b:B
- 类型级:
implicitly[A =:= B]
- 值级别:在运行时通过一个单元测试发生在JVM(即没有运行时的错误):
- 在essense是断言:
assert(a == b)
- 类型级:经由一个类型检测在编译器会发生(即没有编译器错误):
类型之间转换和值
诀窍是使用隐式函数和值。基本情况通常是一个隐含的值,递归情况通常是一个隐式函数。事实上,类型级编程大量使用了implicits。
考虑这个例子(taken from metascala和apocalisp):
sealed trait Nat
sealed trait _0 extends Nat
sealed trait Succ[N <: Nat] extends Nat
这里有自然数的皮亚诺编码。也就是说,每个非负整数都有一个类型:0的特殊类型,即_0
;并且每个大于零的整数具有形式为Succ[A]
的类型,其中A
是表示较小整数的类型。例如,代表2的类型将是:Succ[Succ[_0]]
(继承者对代表零的类型应用两次)。
我们可以别名各种自然数为更方便的参考。例如:
type _3 = Succ[Succ[Succ[_0]]]
(这是像限定val
是一个函数的结果很多)
现在,假设我们要定义一个值级函数def toInt[T <: Nat](v : T)
这需要在一个参数值,v
,它符合Nat
并返回一个整数,表示在v
的类型中编码的自然数。例如,如果我们的值为val x:_3 = null
(null
,类型为Succ[Succ[Succ[_0]]]
),我们希望toInt(x)
返回3
。
要实现toInt
,我们要充分利用以下类:
class TypeToValue[T, VT](value : VT) { def getValue() = value }
正如我们下面将看到的,将有来自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())
我们实现toInt
如下:
def toInt[T <: Nat](v : T)(implicit ttv : TypeToValue[T, Int]) : Int = ttv.getValue()
要了解toInt
是如何工作的,让我们来看看它做什么一对夫妇的投入:
val z:_0 = null
val y:Succ[_0] = null
当我们拨打toInt(z)
,编译器查找TypeToValue[_0, Int]
类型的隐式参数ttv
(因为z
的类型为_0
)。它找到对象_0ToInt
,它调用该对象的getValue
方法并取回0
。需要注意的重要一点是,我们没有向程序指定使用哪个对象,编译器隐式地发现它。
现在让我们考虑一下toInt(y)
。这一次,编译器查找类型为TypeToValue[Succ[_0], Int]
的隐式参数ttv
(因为y
的类型为Succ[_0]
)。它找到函数succToInt
,它可以返回适当类型的对象(TypeToValue[Succ[_0], Int]
)并对其进行评估。该函数本身采用TypeToValue[_0, Int]
类型的隐式参数(v
)(即TypeToValue
,其中第一个参数的类型为Succ[_]
)。编译器提供_0ToInt
(如上面对toInt(z)
的评估中所做的那样),并且succToInt
构造了一个新的TypeToValue
对象,其值为1
。同样,重要的是要注意编译器隐式提供所有这些值,因为我们没有显式地访问它们。
检查你的工作
有几种方法来验证你的类型,级别的计算都在做你的期望。这里有几种方法。使两种类型A
和B
,你想验证是相等的。然后,检查以下编译:
Equal[A, B]
implicitly[A =:= B]
或者,您可以将类型转换为数值(如上所示)并对这些值进行运行时检查。例如。 assert(toInt(a) == toInt(b))
,其中a
是类型A
和b
是B
类型。
其他资源
一套完整的可构建可在the scala reference manual (pdf)类型部分找到。
Adriaan Moors具有大约从阶类型构造和相关主题与实例几个学术论文:
Apocalisp是一个博客,有许多类型级专业版的例子在scala中编写。
ScalaZ是提供使用各种类型级编程功能延伸Scala的API功能一个非常活跃的项目。这是一个非常有趣的项目,有一个很大的关注。
MetaScala是Scala的一个类型级库,包括自然数的元类型,布尔值,单位,HList等。这是一个由Jesper Nordenberg (his blog)开发的项目。
的Michid (blog)具有型电平编程的Scala中一些真棒的例子(从其他的答案):
Debasish Ghosh (blog)有一些相关的帖子,以及:
(我一直在做这方面的一些研究,这里是我学到的。我仍然对它不熟悉,所以请指出此答案中的任何不准确之处。)
社区wiki? – 2010-12-11 09:01:37
就我个人而言,我发现有人想要在Scala中进行类型编程已经知道如何在Scala中编程非常合理。即使这意味着我不理解你链接到的那些文章的一个词:-) – 2010-12-11 12:58:17