幻影类型是自定义类型的类型参数,它们不会在该类型的任何值构造器中使用。
这有点抽象,来看一个例子:
pub type Length(unit) {
Length(amount: Float)
}
在这个例子中,unit类型参数没有在Length值构造器中使用,所以unit就是一个幻影类型。
这个未使用的类型参数看起来可能没什么用,但可以用它来进一步限制Length值的使用方式。
例如,我们可以有一个函数double,它把长度翻倍。它适用于任何单位的长度,所以它的类型参数是一个泛型类型变量。
// This function accepts all Length values
pub fn double(length: Length(unit)) -> Length(unit) {
Length(length.amount *. 2.0)
}
我们也可以有一个函数add_inch,它只适用于以英寸为单位的长度。
// A unit type for inches. It is never constructed so we don't
// define any constructors for it.
pub type Inches
pub fn add_inch(length: Length(Inches)) -> Length(Inches) {
Length(length.amount +. 1.0)
}
add_inch函数不会接受带有任何其他单位参数的长度。幻影类型在这里被用来确保只使用正确的单位。
也可以编写一个函数,让两个长度值使用相同的类型变量,从而确保它们属于同一单位。
pub fn add(a: Length(unit), b: Length(unit)) -> Length(unit) {
Length(a.amount +. b.amount)
}
let two_meters: Length(Meters) = Length(2.0)
let two_inches: Length(Inches) = Length(2.0)
add(two_meters, two_meters)
// -> Length(4.0): Length(Meters)
add(two_meters, two_inches)
// Type error! The unit type parameters do not match.
幻影类型与不透明类型配合得很好。如果其他模块无法构造Length值,那么我们就可以确保不会用无效的单位类型来构造它们,并确保只有上面定义的函数才能与它们一起使用。
Lucy 开了一家在线贴纸店,卖各种可爱的贴纸,贴纸上印着大家最喜欢的编程语言。世界各地的人都来买她的贴纸,而她在应付各种不同货币时遇到了一些麻烦。
编写一个程序,让 Lucy 用它来计算价格,同时确保她始终使用的是正确的货币。
Usd、Eur和Jpy类型这些类型用于表示 Lucy 的顾客购买贴纸时使用的不同货币。
它们用作幻影类型,不需要有任何构造函数。
Money类型Money类型应该有一个用于表示金额的Int字段、一个货币幻影类型参数,并且是不透明类型。
dollar、euro和yen函数定义dollar、euro和yen函数,它们接收一个Int实参,并返回一个带正确货币的Money值。
total函数定义total函数,它接收一个由Money值组成的数组,并返回数组中金额的总和。
total([euro(120), euro(200), euro(145)])
// -> euro(465)