Roku Developer Program

Developers and content creators—a complete solution for growing an audience directly.
cancel
Showing results for 
Search instead for 
Did you mean: 
EnTerr
Level 8

ifList: getIndex() unreliable iteration

In B/S, couple of objects - roList and roXmlList - implement ifList, which is essentially a dequeue (double-ended queue). Hair-splitting ifList from ifArray is rather questionable - but the fact is that quite a few APIs (roFileSystem, roRegistry*, roChannelStore*, roRegex) return the above two and cannot be enumerated over with [ ] indexing.

Here is the caveat: "oh, i know!" - i thought - "i see .resetIndex() and .getIndex() - i'll use that like so:"

lst.resetIndex()
nxt = lst.getIndex()
while nxt <> invalid
 ' ... do the due with nxt'
 nxt = lst.getIndex()
end while

Yeah but no - i realized that the way .getIndex() flags end-of-list by returning invalid would fail you if the list has a genuine invalid element inside, that will lead to premature... exit-ation. Takeout: don't enumerate with getIndex() or your lists should postulate "there could never be invalid amongst us".
0 Kudos
2 Replies
belltown
Level 7

Re: ifList: getIndex() unreliable iteration

From my reading of the documentation, in the context of arrays and lists, Invalid is not a thing; it represents the absence of a thing. In an array, Invalid is used to represent "an array element that has never been set". Invalid is used when "there is no object to return". Many of the ifArray and ifList methods (Push, Pop, RemoveHead, RemoveTail, etc.) return Invalid if "the array is empty" or "end of list reached".

So what the documentation would have me believe is that you're not supposed to be able to treat Invalid as a thing that can be stored in a list item. From the list of APIs you mentioned, I couldn't come up with an example where Invalid could be returned in a list item. I'm curious if you came up with an example, or have a particular use case for this.

However ...

There's nothing to stop you from pushing Invalid into one of your own lists, and since roList implements ifArray, which "supports the array operator[]", I don't see why they "cannot be enumerated over with [ ]", if that's what you want to do; i.e. why the need for GetIndex() in the first place. Furthermore, even though Invalid is returned when the end of the list is reached, it appears that it is indeed possible to Pop() a "genuine invalid element" that is inside the list, and continue to Pop() the remaining non-Invalid elements; in this case, if you allow Invalid elements in the list, you'd have to use Count() to ensure the list is empty, rather than testing for Invalid.


Brightscript Debugger> list = CreateObject("roList")

Brightscript Debugger> list.Push(1) : list.Push("a") : list.Push(invalid) : list.Push({}) : list.Push(42)

Brightscript Debugger> for i = 0 to list.Count() - 1 : print list[i] : end for
1
a
invalid
<Component: roAssociativeArray> =
{
}
42

Brightscript Debugger> print list.Count()
5

Brightscript Debugger> print list.Pop() ; list.Count()
42 4

Brightscript Debugger> print list.Pop() ; list.Count()
<Component: roAssociativeArray> =
{
} 3

Brightscript Debugger> print list.Pop() ; list.Count()
invalid 2

Brightscript Debugger> print list.Pop() ; list.Count()
a 1

Brightscript Debugger> print list.Pop() ; list.Count()
1 0

Brightscript Debugger> print list.Pop() ; list.Count()
invalid 0

Brightscript Debugger>
https://github.com/belltown/
0 Kudos
EnTerr
Level 8

Re: ifList: getIndex() unreliable iteration

"belltown" wrote:
From my reading of the documentation, in the context of arrays and lists, Invalid is not a thing; it represents the absence of a thing.

Oh, invalid is very much a thing. It is the intentional omission of a thing. Rather, the absence of a thing is `<UNINITIALIZED>` of type "<uninitialized>"^. Not only is `invalid` a thing but it has all kind of strange properties of a singularity (like division by zero :mrgreenSmiley Happy. For example, invalid has a counterpart "nuclear particle" - the <Component: roInvalid> instance of the "roInvalid" class. And invalid == roInvalid (the two compare favorably). Plus invalid sometimes autoboxes (e.g. getInterface(invalid, "ifString") ) and sometimes does not (e.g. (invalid).toStr() ), just to keep you on your toes.

In an array, Invalid is used to represent "an array element that has never been set". Invalid is used when "there is no object to return".

Kinda. Although half-ace-d, through insufficient design and not deep foresight. Look at Lua if you want to see consistent implementation of the concept of the Nothing - in Lua all unassigned variables's value is `nil`. Trying to access table value for missing key returns `nil` - but moreover, assigning `nil` - i.e. myDict['someKey'] = nil - actually erases the slot, as if it was never set. In B/S, assigning invalid does not remove the key-value pair, i.e. the .doesExist() predicate still returns true.

This btw, not to be confused with yet another kind of "no object to return" - the "function X as void" with "empty RETURN"  :lol:

Many of the ifArray and ifList methods (Push, Pop, RemoveHead, RemoveTail, etc.) return Invalid if "the array is empty" or "end of list reached".

So what the documentation would have me believe is that you're not supposed to be able to treat Invalid as a thing that can be stored in a list item. From the list of APIs you mentioned, I couldn't come up with an example where Invalid could be returned in a list item. I'm curious if you came up with an example,

Brightscript Debugger> ? parseJson("[1, null, 2]")
<Component: roArray> =
[
   1
   invalid
   2
]

 This will break if one starts processing the result from JSON parse with the methods you mention. Which serves to demonstrate a koan from the humorous "Zen of Python": "Special cases aren't special enough to break the rules."

... or have a particular use case for this.

I have a particular use case for this indeed. I am laboring over a test framework, in which i have to enumerate all elements of a collection - be it array, list, dictionary or... <gasp> roSgNode. Without prejudice if invalid/roInvalid is one of the members or not. That's why i was looking at the different ways to enumerate collections. And no, not all of them implement ifEnum - cases in point roXmlList and roSgNode.

However ... There's nothing to stop you from pushing Invalid into one of your own lists, and since roList implements ifArray, which "supports the array operator[]", I don't see why they "cannot be enumerated over with [ ]", if that's what you want to do; i.e. why the need for GetIndex() in the first place.

That sounds fine and dandy... until you realize that roXMLList, roAssociativeArray, roSgNode do not implement ifArrayGet, i.e. numeric indexing.

Furthermore, even though Invalid is returned when the end of the list is reached, it appears that it is indeed possible to Pop() a "genuine invalid element" that is inside the list, and continue to Pop() the remaining non-Invalid elements; in this case, if you allow Invalid elements in the list, you'd have to use Count() to ensure the list is empty, rather than testing for Invalid.

That actually would work, yes - i thought of that, a counter-intuitive combo of iList .count()/resetIndex()/getIndex(). Please read carefully what i said - i warned that naive enumerating `while getIndex() <> invalid` would exit prematurely on collections containing `invalid`. I have no question how to enumerate my collections - i found another way

(^) and that is assignable between variables (i.e. a=1: ? a: a = UNINITIALIZED: ? a), so yes - <UNINITIALIZED> is also a thing... but entirely another thing. I'd argue assigning <UNINITIALIZED> to something should never pass by silently, never ever - including as argument in function calls or return value. It beggars belief otherwise, the need of existence of <UNINITIALIZED> != invalid... but that's yet another topic Smiley Happy
0 Kudos