16 — Equality
Previous: Generics, traits, and derive
日本語版: 16_equality.vibe.md
== compares by value. Two arrays with equal contents are equal, two
structs with equal fields are equal, and nothing here quietly compares
addresses instead.
One edge is worth knowing, and it does not answer wrongly either: a
generic T with no Eq witness is a compile error. It is below.
The ordinary cases
Scalars, tuples, structs and enums with derive(Eq), Bytes by
content, and arrays.
struct Point {
x: Int; y: Int
} derive (Eq)
fn same_ints(a: Array[Int], b: Array[Int]) -> Bool {
a == b
}
fn main allows Console {
println("lits = \{[1, 2] == [1, 2]}")
let a = [
1,
2
]
let b = [
1,
2
]
println("lets = \{a == b}")
println("fn = \{same_ints(a, b)}")
println("tuple = \{([1, 2], 0) == ([1, 2], 0)}")
println("struct = \{Point::{ x: 1, y: 2 } == Point::{ x: 1, y: 2 }}")
}
lits = true
lets = true
fn = true
tuple = true
struct = true
Bytes is content equality too, including as a tuple element or a
derive(Eq) field.
The cases people expect to be exceptions
Arrays whose elements are not scalars, arrays that arrive as a function's return value, and arrays that started out empty are compared by value too. The last one is worth running: pushing into one of two empty arrays gives the right answer, not a stale one.
fn mk() -> Array[Int] {
[
1,
2
]
}
fn main allows Console {
let pairs: Array[(Int, Int)] = [(1, 2)]
let same: Array[(Int, Int)] = [(1, 2)]
let other: Array[(Int, Int)] = [(1, 3)]
println("non-scalar elements = \{pairs == same}, differ = \{pairs == other}")
println("function returns = \{mk() == mk()}")
let xs: Array[Int] = []
let ys: Array[Int] = []
println("empty and empty = \{xs == ys}")
Array::push(xs, 1)
println("after one push = \{xs == ys}")
Array::push(ys, 1)
println("after both = \{xs == ys}")
let us = []
let vs = []
Array::push(us, 1)
Array::push(vs, 2)
println("no annotation = \{us == vs}")
}
non-scalar elements = true, differ = false
function returns = true
empty and empty = true
after one push = false
after both = true
no annotation = false
us and vs carry no annotation, and they compare by content all the
same: an unannotated let xs = [] takes its element type from the
Array::push calls that fill it (#2157) — as long as the pushed value
says what it is. A literal does, and so does an array, tuple or struct
of literals, or an if whose branches agree.
Push a name or a call result instead and that binding's syntax
does not say the element type. vibe run and vibe test still type it,
and the comparison answers by content when the element is one == can
compare: Int, String, Double, Bytes, an array, an option, a
tuple, or a declared struct. What does not compare is an element with no
structural comparator, such as a closure field. That comparison is a
build error that names the edit, not an address comparison and not a
trap you meet at run time. vibe check does not run this pass. The
annotation on xs and ys above states the element type in the
binding itself.
A generic struct is compared at each concrete instantiation.
Box[Double], Box[Bytes] and Box[Array[Int]] compare by content,
the same as Box[Int]. Two shapes still have no comparator: an
instantiation whose argument is a type parameter of an enclosing
function, and a recursion that never closes, such as Nest[T] holding
Nest[Array[T]]. Those two still compile. The comparison traps if it
runs. They are not the build error below.
The compile-time edge: a generic T with no witness
Inside fn f[T: Eq](a: T, b: T), the element type is gone by the time
code is generated, so == is answered by the Eq witness the caller
passes. For a type that has one, that works and gives the structural
answer:
fn eq2[T: Eq](a: T, b: T) -> Bool {
a == b
}
fn main allows Console {
println("Int same = \{eq2(1, 1)}, differ = \{eq2(1, 2)}")
println("String same = \{eq2("x", "x")}, differ = \{eq2("x", "y")}")
}
Int same = true, differ = false
String same = true, differ = false
For a type that has no Eq witness, there is nothing to pass, and the
call is rejected:
// skip: this is a compile error, shown for the message it produces
fn eq2[T: Eq](a: T, b: T) -> Bool { a == b }
fn main allows Console {
println("\{eq2([1], [1])}")
}
no impl `Eq` for `Array[Int]`
That is the whole of it for a missing witness: the call is a compile error. An element with no structural comparator is a build error that names the edit. Neither answers by address. The enclosing-parameter and never-closing recursion above are the exception: they still compile, and the comparison traps if it runs.
Next: Concurrency.