@mwieder: Shall we do battle sir
And having just tested this again, the number of items of "" is 0 in either case, so you're not losing that.
The difference in the two is the number of items in "," where using the itemDelimiter you get 1 and using the itemSeparator you get 2.
In either case you end up with at least one empty item.
Okay, so I perhaps misunderstood the itemSeparator proposal slightly - it is essentially equivalent to option (B) in one of my previous posts. If the itemSeparator is set to the itemDelimiter, then:
- You can represent the empty list, but cannot represent the list of 1 empty item
- Lists with a trailing delimiter have one more item than they would with the itemSeparator set to empty
The first of these represents quite a big logical discontinuity as far as I can see - what if you have a function that at some point might need to manipulate string lists of length 0, 1 and 2 which could contain empty elements (in the context of using the itemSeparator)?
As I said above, it seems to me that option (B) is worse than option (A) from the point of logical consistency... (Perhaps that is just the lapsed mathematician in me - or I've missed something obvious somewhere...)
My opinion (and this is just mine) is that it's actually less confusing to explicitly test "if tItem is empty" rather than relying on an implicit assumption about the repeat loop.
I'm not sure I understand what you mean by 'an implicit assumption about the repeat loop' - could you elaborate?
And do notice that if you don't set the itemSeparator in your handler that processes the loop there's no change in current behavior.
I realize that - the direct effect of using the itemSeparator goes away outside of scripts that do not use it... However, the indirect effects do not...
Sorry - I don't understand this at all. If this were the case, my local copy of the engine/IDE combination would be falling flat on its face, no?
I think you misunderstand where I'm coming from... With an addition of syntax that only acts in a local handler context then it will 'coexist' with scripts that do not use it but only directly within that handler. The potential issue I perceive here is not with the direct effects that itemSeparator (or variant there of) has on code executing within a handler (which you control), but on interpretation of values that such handlers take in and return to scripts that you do not control.
Let's say you write a library that uses the itemSeparator (and takes in or returns values that are string lists that could contain empty items) then it is then necessary to declare as part of your libraries API that this is the case - why? Because the number of items that it will interpret as those lists having will be different from those that scripts that do not use the itemSeparator.
Take my example above - let's say someone writes a general handler for mapping lists. The person in question knows nothing of 'itemSeparator', so their handler is this:
Code: Select all
function mapList pList, pFunc
local tMappedList
repeat for each item tItem in pList
dispatch function pFunc to me with tItem
put the result & comma after tMappedList
end repeat
if char -1 of pList is not comma then
delete the last char of tMappedList
end if
return tMappedList
end mapList
Here this code is ignoring trailing delimiters so passing in "" will result in empty, "," will result in one item being returned and ",," will result in two.
Now, as part of larger handler someone else uses the mapList primitive in the library:
Code: Select all
function typeItem pItem
if pValue is empty then
return "empty"
else if pValue is an integer then
return "integer"
else if pValue is a number then
return "real"
end if
return "string"
end typeItem
command doSomethingWithLists
set the itemSeparator to ","
local tInputList
-- lots of code that manipulates things and generates tInputList
-- for the sake of argument, it turns out like this
put "1,2.5,foo," into tInputList
local tMappedInputList
put mapList(tInputList, "typeItem") into tMappedInputList
-- do things to tMappedInputList based on its length which is assumed to be 4
if the number of items of tMappedInputList is not 4 then
answer "something odd happened"
end if
end command
In this case the code would fail because the interpretation of the number of items in doSomethingWithLists is different from that in mapList (mapList sees three items, doSomethingWithLists sees four). This is the interoperability problem I have been talking about. So, puzzled why their handler doesn't work, the author of doSomethingWithLists looks at the docs for mapList notices there is nothing about itemSeparator - so it makes them think that maybe that's the issue. Sure enough they go and change their code to fix the problem:
Code: Select all
...
put comma after tInputList -- adjust input for a handler that doesn't use itemSeparator
local tMappedInputList
put mapList(tInputList, "typeItem") into tMappedInputList
if the last char of tMappedInputList is comma then -- adjust output for a handler that doesn't use itemSeparator
delete the last char of tMappedInputList
end if
...
And find it now works.
This has required more code, and more thought to get to work then it would if there was a universal and immutable definition of how many items are in lists.
Of course, if mapList() checked the context of the caller and took the value of itemSeparator then that would potentially mitigate the above issue (there's a part of me that says tertiary effects would still be possible - but I haven't come up with a cogent example yet) but that is then essentially imposing a tax on those writing scripts that they want to share (whether it be libraries, custom controls or whatever).
Essentially, as far as I can see, adding syntax to make it so that you can change the interpretation of the number of items of a string list would make everybody's lives harder given the huge benefit everyone gains from being able to share code easily.
Now, I realize that interoperability is more than just at the script level - certain things require policies and conventions that the engine could not enforce - however, I do feel that as a language, LiveCode shouldn't actively encourage (through provision of core syntax) something that makes it harder.
Returning to the strings vs lists thing for a moment: are you implying that when we get real lists you intend to keep the current paradigm. i.e., the last item or line in a list can't be empty?
No - 'real lists' will essentially be equivalent to (and automatically convert between) one-dimensional numerically indexed arrays with lower index 1 and all keys up to the upper index - i.e. a vector. They'd be accessed with an 'element' chunk (e.g. element 1 of tList), and would be structured data types like arrays, so you can put lists in elements, arrays in elements and so forth. There would also be syntax to insert and manipulate lists. For example (note that none of this is final, just expression of the general idea):
Code: Select all
push tValue onto back of tList
pop tValue from front of tList into tTopItemOfList
insert tValue after element 3 of tList
Additionally, we could consider automatic conversion to strings (perhaps with an 'elementDelimiter' local property), but one that in a strict mode would tell you if you were trying to convert a list to a string that has no representation as a string (perhaps because one element is a an array) it would be an error.
i.e., the last item or line in a list can't be empty
Addendum: This point of view does propagate a fallacy in some ways: the last item or line in a list can be empty at the moment - if you are dealing with string lists that could contain empty items then they must always have the trailing delimiter, you can't leave it off. That's the point of the rules I was talking about above - it is the price you pay for being able to represent lists as strings with any number of empty elements from 0 upwards.
EDIT: I made a mistake in the 'typeItem' handler above - corrected.
EDIT: I made a mistake in the 'mapItems' handler above - corrected.