func ListEqualsUsing(a Allocator, lhs, rhs List) bool {
	if lhs.Length() != rhs.Length() {
		return false
	}
	lhsRange := lhs.RangeUsing(a)
	defer a.Free(lhsRange)
	rhsRange := rhs.RangeUsing(a)
	defer a.Free(rhsRange)
	for lhsRange.Next() && rhsRange.Next() {
		_, lv := lhsRange.Item()
		_, rv := rhsRange.Item()
		if !EqualsUsing(a, lv, rv) {
			return false
		}
	}
	return true
}
ListEqualsUsing uses the provided allocator and compares two lists lexically.
WARN: This is a naive implementation, calling lhs.EqualsUsing(allocator, rhs) is typically the most efficient.
package value
type List interface {
	Length() int
	At(int) Value
	AtUsing(Allocator, int) Value
	Range() ListRange
	RangeUsing(Allocator) ListRange
	Equals(List) bool
	EqualsUsing(Allocator, List) bool
}
type ListRange interface {
	Next() bool
	Item() (index int, value Value)
}
var EmptyRange = &emptyRange{}
type emptyRange struct{}
func (_ *emptyRange) Next() bool {
	return false
}
func (_ *emptyRange) Item() (index int, value Value) {
	panic("Item called on empty ListRange")
}
func ListEquals(lhs, rhs List) bool {
	return ListEqualsUsing(HeapAllocator, lhs, rhs)
}