这本书的大部分内容都与形式化抽象有关
各种各样的机制。在简单类型的 lambda 微积分中,我们
形式化了取一个术语并抽象出一个术语的操作
子项,产生一个函数,以后可以通过
在 SystemF中,我们考虑了
operation of taking a term and abstracting out a type, yielding a term
that can be instantiated by applying it to various types. Inλω, we
概括了简单类型的 lambda 微积分“ one”的机制
升级,”获取一个类型并抽象出一个子表达式以获取
a type operator that can later be instantiated by applying it to
一种方便的方式来思考所有这些形式的
abstraction is in terms of families of expressions, indexed by other
一个普通的匿名函数 ABc0
术语 [x -> s]t1被术语 s索引。类似地,类型抽象
λX::K1.t2是由类型索引的一系列术语和一个类型运算符
是由类型索引的类型族。
case class Integer(v: Int) {
object IsEven { require(v % 2 == 0) }
object IsOdd { require(v % 2 != 0) }
}
def f(n: Integer)(implicit proof: n.IsEven.type) = {
// do something with n safe in the knowledge it is even
}
val `42` = Integer(42)
implicit val proof42IsEven = `42`.IsEven
val `1` = Integer(1)
implicit val proof1IsOdd = `1`.IsOdd
f(`42`) // OK
f(`1`) // compile-time error
关键是要注意值 n如何出现在值 proof的类型中,即 n.IsEven.type:
def f(n: Integer)(implicit proof: n.IsEven.type)
^ ^
| |
value value
我们说 类型n.IsEven.type依赖于 valuen,因此术语 受养人类别。
As a further example let us define a dependently typed function where the return type will depend on the value argument.
The objective is that indexing should not lose any compile-time type information, for example, after indexing the third element the compiler should know it is exactly a String
val i: Int = index(hlist)(0) // type Int depends on value 0
val l: List[Int] = index(hlist)(1) // type List[Int] depends on value 1
val s: String = index(hlist)(2) // type String depends on value 2
下面是依赖类型函数 index的签名
type DTF = [L <: Tuple] => L => (idx: Int) => Elem[L, idx.type]
| |
value return type depends on value
或者换句话说
对于 L类型的所有异构列表,以及对于所有(值)索引
idx of type Int, the return type is Elem[L, idx.type]
where again we note how the return type depends on the value idx.
// Bring in scope Peano numbers which are integers lifted to type-level
import compiletime.ops.int._
// Match type which reduces to the exact type of an HList element at index IDX
type Elem[L <: Tuple, IDX <: Int] = L match {
case head *: tail =>
IDX match {
case 0 => head
case S[nextIdx] => Elem[tail, nextIdx]
}
}
// type of dependently typed function index
type DTF = [L <: Tuple] => L => (idx: Int) => Elem[L, idx.type]
// implementation of DTF index
val index: DTF = [L <: Tuple] => (hlist: L) => (idx: Int) => {
hlist.productElement(idx).asInstanceOf[Elem[L, idx.type]]
}
A dependent type is the label used to indicate that the output's type (i.e. the type of the co-domain) depends on the actual input value/argument passed to the (dependent) function. e.g. F:forall a:A, Y(A) means the input type of F is A and that depending on the specific value of a the 输出类型输出类型 will be Y(a). So the output type depends on the input argument.
对于一个普通函数,我们通常写 f: A -> B,这意味着函数 f,任何 A类型的输入(非正式的 \forall a \in A或 a:A)返回一些 B类型的元素(非正式的 some a \in B或 b:A)。这是一个“正常”函数类型。但是,依赖类型表示(依赖)函数 F的输出类型取决于参数。类似地,输出类型也是通过参数的值进行索引的(即“依赖”)。例如,我喜欢的符号(没有其他人使用 afaik)是 F: a:A -> Y(a)或者常用的符号 f0。这些表示法只是说 F接受 A中的一些元素 f2(即类型为 A) ,并返回类型为 f6的一些元素 f5(或者如果你喜欢索引集更多地使用 f7)。它仅仅意味着 F的输出类型根据输入值 f2到 F的不同而变化。
以 C + + 为例,表达式“ int”是整数类型,它的类型不依赖于任何东西,总是
一样。让我们定义另一个类型“ int (x)”,其中 x 是一个数字。让我们将 int (x)类型的用途定义为
帮助我们得到一个只有“ x”位数的整数。因此,int (x)的类型取决于 x 的值,其中 x 的值
可以在运行时确定。例如,我们可以有一个包含一个表达式“ int (x)”的语句,其中
of x is passed as an argument to the function. What we are saying is that, for example, int(2) and int(3) are of different data types, i.e. how they behave
在计算过程中是不同的。