r3wp [groups: 83 posts: 189283]
  • Home
  • Script library
  • AltME Archive
  • Mailing list
  • Articles Index
  • Site search
 

World: r3wp

[!REBOL3]

BrianH
6-Jun-2011
[8994x2]
In R2, indexes are constrained to the bounds of the series they reference, 
so if you shorten the series you lose your position. In R3 your position 
is preserved for most of the standard operations, just in case you 
add back to the series later. The operations that can't possibly 
work for those out-of-bounds references trigger errors (mostly modifying 
operations), and the ones that could work if you consider series 
bounds to be an implementation detail and out-of-bounds values to 
be just not there return none (data access functions). SKIP is an 
exception, afaik, to allow you to sync up with the bounds - useful, 
but I don't remember whether it was intentional or a happy accident.
>> a: "abc"
== "abc"
>> c: skip a 2
== "c"
>> remove/part a 2
== "c"
>> index? skip c 0
== 2
>> index? c
== 3
The point of this in R3 was to rebalance the behavior in favor of 
not triggering as many errors when they aren't useful. The policy 
in R3 is that errors should be your friends, and that means triggering 
errors where they help in general, preferably informative errors, 
and not triggering errors where they aren't generally helpful. The 
conceptual definition of series was changed in R3 to make bounds 
less hard so as to trigger fewer errors; out-of-bounds positions 
are now considered to have no values in them, rather than not being 
there, under most circumstances when you can get away with it - you 
can't necessarily get away with it for modifying operations. This 
change was based on analysis of common code patterns, and considering 
that error catching is expensive in most programming languages, including 
REBOL; none checking is much less expensive. In the cases where explicit 
bounds checking is necessary, that isn't expensive to add to your 
code, not nearly as expensive as the bounds checking required in 
code in R2 that isn't required at all in R3.
onetom
6-Jun-2011
[8996]
BrianH: that's a beautiful description. it should be part of the 
R3/Concepts document. can we just dump it to there? (i don't have 
write privileges yet)
BrianH
6-Jun-2011
[8997]
It would be a victory if we can get to the point where if R3 developers 
see an error triggered in R3 they are thankful for the information 
provided, because it will make it easier for them to find the mistake 
in their code that caused the uncaught error to be triggered in the 
first place. This is why error locality is so important, as is triggering 
better errors, and not assuming that something is an error unless 
the code says it is. If developers can think of errors as being their 
friends, all the better.
onetom
6-Jun-2011
[8998]
these are the kind of explanations which i miss in case of other 
languages when im wondering over design decisions like: "what kind 
of animal could have came up with such a fucked-in-the-nose idea?"
BrianH
6-Jun-2011
[8999]
It's a little easier with R3, where a lot of the design decisions 
were made for good reasons, and by consensus. And because we decided 
early that R3's answer to backwards compatibility with R2 is to keep 
maintaining R2 for such code. That frees us to fix aspects of R2's 
design that turned out to not be that good an idea in retrospect.
Gregg
6-Jun-2011
[9000]
+1 for getting Brian's detailed notes logged someplace so they are 
usable (and so they don't get lost).
Geomol
6-Jun-2011
[9001]
Desiding what to do with block indexes out of range is a tough call, 
I think. I understand the argument not to cause errors, if it can 
be handled somehow, but I'm not sure, handling out-of-range problems 
is always good. What if it's a user bug in the code, that made the 
index get out of range? Then the user won't easily find that bug, 
as it causes no error.


It's not possible to index a block lower than 1 (first element). 
It's only possible to index out of range in the other end of the 
block, getting past the tail. And that can only be done by having 
an index there, and then remove something from earlier in the block. 
When the index is beyond the tail, then it has to be desided what 
to do with
	insert, remove, skip, next, back, pick, select, append
used on that index. (and maybe more like TAIL?, INDEX?, ...)

What does other languages do?
onetom
6-Jun-2011
[9002]
what other languages have position markers associated with arrays/lists/vectors 
inherently?
Geomol
6-Jun-2011
[9003x2]
Let's say, my index is way beyond the tail, and I insert a new element 
there. It may then just be appended to the series, which is at an 
index way before my pointer. What if I then e.g. say:
	remove/part my-index -1
what should happen? And why?
Ladislav
6-Jun-2011
[9005x3]
Geomol: "I'm not sure, if this is known or desired behaviour" - Brian 
used a long description, but the fact is, that the best part of it 
is "accident". I bet, that it has not been checked, and it is not 
clear, whether the difference is desirable or not.
As far as I am concerned, the difference is not desirable.
Let's say, my index is way beyond the tail, and I insert a new element 
there.
 - you are out of luck, such an operation is not supported
Geomol
6-Jun-2011
[9008x3]
>> s
== [1 2 3]
>> t: tail s
== []
>> remove/part s 2
== [3]
>> insert t 1
== []
>> s
== [3 1]
>> index? t
== 4
>> system/version
== 2.100.111.2.5
Seems to be.
Ladislav
6-Jun-2011
[9011x2]
Hmm, did not actually check, thanks
Now, what do you think about the behaviour? Do you find it useful? 
(I do not)
Geomol
6-Jun-2011
[9013]
The more I think about it, I tend to not like it. It must be up to 
the user to be sure, the indexes are in bound when used. It's really 
cryptic code required to make this work.
Ladislav
6-Jun-2011
[9014]
BTW, "Let's say, my index is way beyond the tail, and I insert a 
new element there." - in fact, I was right saying that the operations 
was not supported, since it did not insert a new element there
Geomol
6-Jun-2011
[9015]
yeah :)
BrianH
6-Jun-2011
[9016]
Only the part with the behavior of SKIP might be an accident. The 
rest was the result of a blog discussion over two years ago, which 
iirc happened while Ladislav was taking a break from REBOL.
Ladislav
6-Jun-2011
[9017]
In my opinion, this is clearly unsettled (BACK vs. SKIP), and was 
not discussed before
BrianH
6-Jun-2011
[9018]
No, SKIP wasn't discussed. The rest was.
Ladislav
6-Jun-2011
[9019]
So, the above mentioned INSERT behaviour was discussed?
BrianH
6-Jun-2011
[9020]
I think so, but it was in that private world where Carl's GUI for 
R3 was being worked on with Henrik and me. It looks like INSERT works 
like SKIP.
Ladislav
6-Jun-2011
[9021x2]
In R2, indexes are constrained to the bounds of the series they reference, 
so if you shorten the series you lose your position
 - this is provably false
(and the proof is quite old)
BrianH
6-Jun-2011
[9023x2]
I was simplifying to avoid having to write a bunch of test code when 
my time is limited. Any proofs are welcome, though maybe the R2 proofs 
should go in Core.
The word "constrained" was a simplification of the real process.
Ladislav
6-Jun-2011
[9025]
The proof exists, welcome or unwelcome, to demonstrate the actual 
properties of the interpreter, and to prove why the behaviour of 
some function is unuseful.
BrianH
6-Jun-2011
[9026]
Yup, especially for R2.
Ladislav
6-Jun-2011
[9027]
http://www.rebol.net/wiki/Identity#Indices_of_series
BrianH
6-Jun-2011
[9028x2]
It would be possible and in keeping with the metaphor to have an 
out-of-bounds INSERT pad blocks with none values, but since strings 
and binaries don't have a way to have inline nones, that would make 
the behavior of blocks inconsistent. That is why INSERT behaves the 
way it does. If you want INSERT to trigger an error in that case, 
like POKE and set-path modification, that would make sense too.
Or we could just say that INSERT, APPEND, SKIP, AT and LENGTH? are 
constrained to bounds, while PICK, POKE, and the ordinals and path 
accesses that are based on them are not.
Ladislav
6-Jun-2011
[9030x3]
OK, that is why the "INSERT *there*" operation is, in fact, unsupported, 
and, in such cases, it is preferable to trigger an error, in my opinion
APPEND is clearly a different case than INSERT, since APPEND always 
uses the tail
regarding the incompatibility of SKIP -1 and BACK - I am not sure, 
whether that is good to have.
BrianH
6-Jun-2011
[9033]
I think that the incompatibility of NEXT and BACK is more important, 
and definitely an error, and an accident.
>> a: [1 2 3]
== [1 2 3]
>> b: skip a 2
== [3]
>> remove/part a 3
== []
>> index? b
== 3
>> index? next b

== 3  ; constrained to the argument index, not even the series bounds
>> index? back b
== 2 ; not constrained to the series bounds
>> index? back a
== 1 ; constrained to the head
Geomol
6-Jun-2011
[9034x2]
Option 1:
	NEXT will not advance, if at tail or beyond tail.
Option 2:
	NEXT will not advance, if at tail, but will if beyond tail.
Option 3:
	NEXT will always advance.
And if it will not advance in some cases, should it then trigger 
an error, if that is tried to do?
BrianH
6-Jun-2011
[9036x5]
Option 4:

 NEXT and BACK will be constrained to the series tail, even if that 
 makes the (index? a) <= (index? next a) truism false.
I'm not advocating option 4, I was just making another option besides 
3 that made sense. Option 1 is what we have now.
Note that the other operations that are constrained to the tail of 
the series in R3 are PRINT and EMPTY?, which is why R2 gets that 
out-of-bounds error and R3 doesn't.
Also note that REMOVE/part with a negative part acts like REMOVE/part 
from the other point with a positive part. This is why it's impossible 
to create a before-the-head reference.
With Option 3 that would change, since BACK from the head of the 
series would go out of bounds. REMOVE out of bounds would continue 
to be a noop.
Geomol
6-Jun-2011
[9041]
This is something, I need to sleep on to have any useful opinion 
on. :)
BrianH
6-Jun-2011
[9042]
The off-by-one error for AT and non-positive indexes still has an 
unresolved ticket too. PICK and POKE were fixed, but not yet AT.
Geomol
7-Jun-2011
[9043]
Would a programmer expect this to be true always?

	(index? series) + (length? series) = (index? tail series)


That seems to define some basic rules for indexes and the functions 
involved. But it fails sometimes, as it is now:

>> s: [1 2]       
== [1 2]
>> t: tail s
== []
>> clear s
== []
>> (index? s) + (length? s) = (index? tail s)
== true
>> (index? t) + (length? t) = (index? tail t) 
== false


Problem here is, that LENGTH? t returns 0. It should return -2, if 
the result should be true.