Tipos fantasma são parâmetros de tipo de um tipo personalizado que não são usados em nenhum dos construtores de valores desse tipo.
Isso é um pouco abstrato, então aqui vai um exemplo:
pub type Length(unit) {
Length(amount: Float)
}
Neste exemplo, o parâmetro de tipo unit não é usado no construtor de valores Length, então unit é um tipo fantasma.
Esse parâmetro de tipo não utilizado pode parecer inútil, mas pode servir para adicionar mais restrições à forma como os valores Length podem ser usados.
Por exemplo, poderíamos ter uma função double, que multiplica o comprimento. Ela funciona com comprimentos de qualquer unidade, então o parâmetro de tipo é uma variável de tipo genérica.
// This function accepts all Length values
pub fn double(length: Length(unit)) -> Length(unit) {
Length(length.amount *. 2.0)
}
Também poderíamos ter uma função add_inch, que só funciona se o comprimento estiver em polegadas.
// 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)
}
A função add_inch não vai aceitar comprimentos de nenhum outro parâmetro de unidade. O tipo fantasma foi usado para garantir que só a unidade correta seja usada.
Também é possível escrever uma função que garanta que dois valores de comprimento sejam da mesma unidade, usando a mesma variável de tipo para ambos.
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.
Os tipos fantasma podem funcionar bem com tipos opacos. Se outros módulos não puderem construir valores Length, podemos garantir que eles não sejam construídos com um tipo de unidade inválido, e que só as funções definidas acima possam ser usadas com eles.