REBOL 3.0

Comments on: Series ordinals now behave like PICK

Carl Sassenrath, CTO
REBOL Technologies
9-Apr-2006 21:59 GMT

Article #0008
Main page || Index || Prior Article [0007] || Next Article [0009] || 21 Comments || Send feedback

The series ordinals (first, second, etc.) were originally designed to be more strict - mainly for beginner usage. For example:

print first tail "abc"
** Script Error: Out of range or past end

causes an error. However, the pick function does not:

print pick tail "abc" 1
none

I used to think this difference was ok because experts did not use first. However, I no longer believe that. The ordinals are very efficient functions, and I tend to find myself using them as fast selectors with code like this:

the-name: :first
the-email: :second
the-message: :third

send the-email data the-message data

For high performance code, this method is as fast as you can get. When used in high-speed loops, it is much faster than using object field selectors (those require both path interpretation and run-time selector binding). In fact, I use this method so often that I've implemented an ordain function for defining the above ordinal relationships in my code.

There is only one problem. The "past end" exception is no longer desireable.

In REBOL 3.0 changes in the implementation of the ordinals (from datatype-specific action functions to generic abstract native trampolines) made them by default work like pick (what the trampolines actually call). The past-end error is now gone.

This seems preferable, and the change should be compatible because the past-end usage case was an exception in prior versions, so is not utilized in existing code.

Note that this will require some minor documentation updates.

21 Comments

Comments:

Volker Nitsch
9-Apr-2006 18:33
:) I use it sometimes to cheaply enforce runtime-errors. Which i get for free, which is good in time-critical code too. But i agree its not obvious. Could there be two versions maybe, if first? block [ we have something ] (its bloat, but..) Related: could the relaxed 'first deal with none, like remove does? for any[ second? find here that default-value ]
Anonymous
9-Apr-2006 19:30
Will the path notation generate these errors or will they act like pick too?
Ed
9-Apr-2006 19:30
:) Receiving a none for series that are "out of range" seems like an improvement to me.
Reisacher
10-Apr-2006 2:34
:) How can I distinguish between not there and a none! item? e.g. I have a block: reduce [a none b]
Volker Nitsch
10-Apr-2006 8:30
:) One idea: check size. if 1 >= length? series[ first will work ] (length works from current position, giving length of the rest

Another idea (sorry i am in extension mood, but i like it): How to add another value, 'not-found! ? it would work like none, except that series-functions return it. Reason: 1) i want array/3/4 to work, even if out of bounds. Think of lits's supply-block, could be as simple as supply: [face/text: data/:count/:index] 2) i dont want 'none to work here, because 'none is the value for initialization, "not known yet". if series act on this, a lot init-errors are untrapped. But fetching from a series is not "not known yet", but "quite precisely known: its not there". only produced by 'find or 'pick . In that case series-access could safely handle it, by returning 'not-found IMHO.

Advantage: cool chaining either missing? val: third find here that[..][..] instead of either all[p: find here that third p][..][..] Disadvantage: yet another feature. Maybe not much advantage, except of beeing cool?

Gabriele
10-Apr-2006 12:02
:) Volker, what happens when you put your not-found! value inside a block?
Brian Hawley
10-Apr-2006 12:22
When you think about it, series length is just an implementation detail. Series expand when you insert stuff into them, so the length is really just the current length. If you are using none as the value of no value (like NULL in SQL), what really is the difference between returning a none from the middle of a block and returning a none from off the end of one. You could think of a series as being of potentially infinite length, with none as an uninitialized value.
Volker Nitsch
10-Apr-2006 12:28
:) Gabriele, it returns not-found too, i know. but not-found is a rare value, while none is the most used value in rebol. Also its main purpose is not to be returned, but to be handled by 'pick & co as a series, instead as an error. So picks can be chained without ugly checks. We could do that with 'none instead, pick none 24 -> none but 'none is also the default value in locals and contexts, kind of "not initialized". And i want that to be trapped. pick not-found 24 -> not-found would be reasonable safe IMHO.
Brian Hawley
10-Apr-2006 13:03
Volker, your description of not-found sounds exactly like what none is supposed to mean. That's why none is the most used value in REBOL. Or do you intend not-found to mean the same as unset?
Volker Nitsch
10-Apr-2006 15:38
:) something in between. I want a none which can not happen accidentally. None is the default for locals and contexts, i can not distingish a none from /local from a none from a failed lookup. So i am in a dilemma:

i want path-lookups (same as chained picks) to succed or not. two-dimensional-arrays, or "third find". i think to split such things with explicit checks is ugly.

But if we change the accessors to simply pass none, instead of throwing an error, thats bad. That would mean none/5 -> none and so a/5 -> none instead of error. Bad idea, often this is an error. So lookups need to know i really tried to lookup and it failed. a: find here that a/5 shall be ok. And the straightforward way to that is a special none, one which does not occur by auto-initialisation. (if it is the best one, or if chaining introduces other traps i don't know yet)

Brian Hawley
10-Apr-2006 17:45
Perhaps a /check refinement to pick to do bounds checking, throw an error when it fails?
Gregg irwin
10-Apr-2006 18:18
What is the most common case for the average script (I know, there is no "average" script)? Do you need to differentiate between a NONE (out of bounds) and a NONE (the value) more often than not, and is there stilll a way to do so (one that isn't too painful, preferably)?

Today, we have the same issue with SELECT, right? SELECT is the way to avoid errors, just like PICK is. If path access still throws errors, you can do it that way or, as Brian suggests, maybe add a refinement to PICK if you *want* an error.

Worst case, you check to see if the index is in bounds, which I think is the less common case.

Having them return NONE is, I think, good for productivity and programming-in-the-small, like associative arrays in AWK.

Brian Hawley
10-Apr-2006 18:28
At the very least, we should remember that none doesn't really mean anything until we decide it does, and that the meaning of "no value" or "missing data" makes as much sense as any.
Volker Nitsch
10-Apr-2006 23:45
:) Maybe i failed to make it clear: my problem is not with single-level access (although my example looks like this). what i want is this: > pick pick pick series 1 2 3 should work. even if one of the picks runs out of bounds.
Anonymous
11-Apr-2006 1:47
Volker, would having pick on none return none, and pick/check on none throw an error, work for you?
Brian Hawley
11-Apr-2006 1:48
Sorry, that was me :)
Volker Nitsch
11-Apr-2006 10:47
:) That would work. i swap it, the default checks errors, and then: > pick/from pick/from pick series 1 2 3 > third/from find block key Would "flow" and still catch that "uninitalized". (/from was the first word which came to mind)
Brian Hawley
11-Apr-2006 16:19
No, keep the default as returning none. The /check we are talking about here refers to bounds checking, not error checking. It throws an error, it doesn't check one.

You are assuming that a pick out of bounds is an error - it may not be, depending on your data. I am proposing adding optional bounds checking to pick, for use when you have decided that out-of-bounds access in this case is such an exceptional condition that it is worth throwing an error about. The normal case would be to consider none to be an indicator of missing data, no value, null or whatever.

Volker Nitsch
11-Apr-2006 17:53
:) No, this are two different things, which shall work hand in hand. One is, 'first does not throw an error, it returns none. The other is, 'first still throws an error if it *gets* none. Result: we have relaxed single-dimension-access. But two dimensions still fail: > first [] ; works now > first first [][] ; still error Why not change that? Its easy: change 'first to accept none as input, and >first first [][] returns none. Problem solved! Cool eh?

Uncool: A good language catches obious errors. A common error is a forgotten initialisation. Look at > lots of code > a: [] > ;lost of code > first first a And now: > lots of code > ;lost of code > first first a See? 'a is none, because 'a is a local! As usual forgot the initialization.. The current 'first catches that, the smarter one would not. Bad deal, its a quite common error.

Know imagine a special none, one which does not happen by accident, but is the result of a failed lookup. lets call it 'not-found! or 'failed-lookup! or something. Now the inner 'first would trap forgotten inits, because it does not allow a real none. But if only out of range in a series, it would return a 'lookup-failed. Now the outer 'first would accept 'lookup-failed and simple return it. Now we can chain lookups happily and look for success in the end. > if first a [we have more values] > if first first a [we have more values] Works with any amount of dimensions.

Now lookup-chains are so usefull that we have even a special syntax for it, pathes. > first first a and > a/1/1 are the same. Wouldn't it be nice if we could write > if a/1/1 [we have more] instead of > if all[t: a/1 t/1] [we have more] ?

Brian Hawley
11-Apr-2006 18:24
Well, with first being a trampoline that calls pick in REBOL 3, you could solve your first problem by having pick on none return none, and fix up the ordinals' argument types to include none.

As for your second problem, your usage example would work just as well with none being the out-of-range value as it would with some other none with a different name. Assuming you solved the first problem, your code could run as-is. I'm still not sold on the need for another none - we have two already, none! and unset! (the erroneous none).

If you are set on bounds checking, that /check refinement could be added to the ordinals too and passed along to pick. When you are debugging you can add the checks where you need them.

Volker Nitsch
11-Apr-2006 19:49
:) "pick as none", that would break proper initialisation-checking. If thats the tradeoff, trash it.

Post a Comment:

You can post a comment here. Keep it on-topic.

Name:

Blog id:

R3-0008


Comment:


 Note: HTML tags allowed for: b i u li ol ul font span div a p br pre tt blockquote
 
 

This is a technical blog related to the above topic. We reserve the right to remove comments that are off-topic, irrelevant links, advertisements, spams, personal attacks, politics, religion, etc.

REBOL 3.0
Updated 13-Dec-2024 - Edit - Copyright REBOL Technologies - REBOL.net