On “lists are never empty”:
The reason that a list is never empty is that Bubble (and the Bubble expression builder) is just a clever abstraction layer on top of JavaScript. And, in JavaScript there “sort of” aren’t arrays. Arrays exist, but they are just a special case of objects. All objects are unique of course, even if they hold the same values. The data type of an Array (in JavaScript) is object. Arrays have a prototype (by which we can identify them as being Arrays), but at the end of the day they are just objects.
So, if you open your console and do:
var a = [1, 2, 3, 4]
// a is an array of four items 1 thru 4
And then:
var b = [1, 2, 3, 4]
And then you ask, is “a” the equivalent to “b”?
a == b
// “==” is the equivalency operator in JS, not to be confused with “=” (the assignment operator).
The result will be false
. Because “a” is one object and “b” is another object. So, while they hold identical values, these can never be the same thing (object).
The only way that a
can be identical to b
is via an object reference:
b = a
// we assign b to be the same thing as a
Now b and a refer to the same object (a single instance of the array/object [1, 2, 3, 4] ).
So, what is “empty”? Empty is Bubble’s equivalent of the special JavaScript value null
. As it so happens, null
is also an object (it is not its own data type). It is a unique object that never exists naturally, it can only be called into existence by the programmer and it is a way of saying, while some thing is not undefined, neither does it have any value or any data type beyond object.
Additionally (and this is a feature of all drag & drop programming environments), in Bubble there is no concept of undefined
. For us to reference something in a drag and drop environment, it must have some label, some symbol. And further (if there’s an Issue Checker as in Bubble or we care about data types as does Bubble), there needs to be additionally metadata (e.g., where it’s located, what types of data it can store) about a symbol for it to be useful…
So there’s no such thing as “a thing that is labeled but does not yet exist” in such environments. So “empty” kind of plays a dual role: It represents both null
and in some special circumstances, the state of being undefined
. Nothing is ever undefined
, so we either simply cannot reference it or it is assumed to be null
/empty
.
(Example: Try to use the expression builder to reference some custom state that does not exist. You can’t do it. Instead, you would have to define the custom state first (at which point it is empty/null, but it is most definitely not undefined). And, in fact, in such situations, Bubble actually invites you to define a new custom state! That’s the way graphical/drag & drop environments work.)
Back to null
:
While null
is an object, we cannot modify it. We cannot add any additional properties to it, nor does it have any properties. Null
is just… null
. And, in the same way that all instances of the integer 5 are the same, all nulls are the same. 5 == 5
, null == null
, 5 !== anything else
, similarly null !== anything else
.
And so, because Bubble lists are Arrays, they are also objects. And objects are unique. And some Array object can never be the special object null. (Additionally, null is not an Array, so no Array can ever be null
and no null
can ever be an Array.)
And, thus, an Array (a List in Bubble) is never null (empty in Bubble terms).
The same is true in pure JavaScript. An Array can have no elements: Here is an Array of zero length:
var a = []
// a is an Array, but it has no elements, its length is 0 (zero).
… but a
is still an object and thus is unique (and it is definitely not null
!).
All indexes of a
are undefined
. a[0]
(the first element of the array) does not exist and thus is undefined. As is a[1]
and on and on and on.
Now, in the same way that our Array a
can have 0 length, Bubble Lists can also have zero length. But they are still objects and thus are not equivalent to empty/null.
So we can’t ask if a List is empty (null), but we can ask if the first item is “empty” (this is one of those cases, where Bubble equates null
and undefined
– technically speaking, the first item in a List with no items is undefined
, but since Bubble can’t have that, it evaluates to empty
).
The other thing we can do of course is ask how many elements a List (Array) has. In Bubble terms this is the List’s :count
(in JavaScript the same thing is an Array’s .length
property).
A Bubble List without any items in it will have a :count
of 0
. A JavaScript array without any items in it will have a .length
of 0
.
NOW, to the question of, “which is better… to test if a Bubble List has a :count < 1
or if the :first item is empty
”?
In vanilla Bubble, it doesn’t really matter. HOWEVER, in plugin land, where we encounter some Lists (in fact, most of the Lists we encounter) that are themselves objects, it turns out that we can know the :count
of the List (the .length
of the Array of items that List represent) before accessing any of the items in the List.
This is because the results of a Search is a List object – which is a simple JS object with two methods on it: a .get()
method (to retrieve individual items in the List) and a .length()
method (which instantly returns to us the number of items in the List – it doesn’t query the database to know this, the value just travels along with the List).
So, in a plugin, if I want to evaluate whether a List presented to me has any items or not, I’d just look at List.length()
and, if it’s 0
, I know that there are no items in the List and do not have to bother to .get()
the first item and examine it. (Nor should I bother to fetch the items in the List at all, I already know that the JavaScript equivalent of such a list is []
).
Anyway, that’s a little story about Lists. 
Postscript: The only place I know of where a Bubble programmer can access this same functionality is in my List Shifter plugin. There’s an exposed state in List Shifter named “Item Count”. When List Shifter is first initializing, I inspect the .length()
property of the incoming List and immediately publish it to that output just before fetching the items in the List.
In this way, you might (for example) send List Shifter some giant List, but you can know the size of it well before List Shifter finishes initializing, just by keeping an eye on that exposed state.