Subject: first, rest, last, any, ...

From: Kent M Pitman <[email-masked]>
Date: Wed, 18 Aug 1993 22:51:00 -0700

Hypertalk has primitives "any", "first", and "last" that I think
belong in MOO because they are so often needed and look so much
better than their idiomatic alternatives.  It has "middle" or some
such, which I see little use for.  It is missing "rest" which would
be more useful.

 What you write now...                   What you should write...

 some_foo = foo[random(length(foo))];    some_foo = any foo;
 last_foo = foo[length(foo)];            last_foo = last foo;
 first_foo = foo[1];                     first_foo = first foo;
 rest_of_foos = foo[2..length(foo)];     rest_of_foos = rest foo;

In addition to making code slightly smaller, much more readable, 
and less likely to be buggy, it's also possible that there is a
speed gain to be had from these, no?

If you wanted to make them take parens as in any(foo), last(foo), etc.
I guess I wouldn't really fuss.

---

From: Kent M Pitman <[email-masked]>
Date: Thu, 19 Aug 1993 10:48:00 -0700

I think rest is useful and you shouldn't just throw it out due to some
symmetry argument.  Rather, you should repair the problem in the other way:
I recommend providing BOTH butfirst and butlast.

I have thought about the syntax x[i...] for the same reason.  (I think the
extra dot would help you confirm that someone hadn't made a c-m-K kind 
of error in Emacs.)  

But note that it's not symmetric without x[...i]. And in any case, that
doesn't generalize well to the other operators without, for example, 
including support for negative subscripts.  You -could- do:
 x[1] = first(x)
 x[2...] = butfirst(x)
 x[...-1] = last(x)
 x[...-2] = butlast(x)
but these would be more prone to programming errors if the arguments were
complex expressions that fell into negative numbers accidentally.  (Right
now, I think such bounds errors are checked, but you'd have to accept them
as "correct values" if you revised it.)  If you don't have negative subscripts,
then really only butfirst (and its friends [3...],...) become easier to write.
And you introduce an expressional asymmetry where last and butlast get written
as function calls but first and butfirst get written with brackets.  I think
that is bad.  Hence even though I think the ... notation is cute, I think it
will work against the symmetry thing you (probably rightly) care about.

So I say just add the named operators (first, butfirst, last, butlast, and any)
and don't do the syntax thing.

---

From: Pavel Curtis <[email-masked]>
Date: Thu, 19 Aug 1993 10:17:45 -0700

> Date:	Wed, 18 Aug 1993 22:51:00 -0700
> From:	Kent M Pitman <[email-masked]>
> 

I'm inclined to agree, I think, except that I think I'd follow Hypertalk's lead
and leave out `rest'.  Since MOO lists are end-to-end symmetric, it's not at
all clear to one who hasn't hacked LISP what `rest' you mean; I could even
imagine programmers getting confused into thinking that `rest' referred to all
of the elements other than the last one referenced (or some such).

If I put these things in, I'd only be putting in `first' for symmetry with
`last', which is far more useful.

I have been thinking for a long time that the syntax

	x[i..]

should mean

	x[i..length(x)]

> speed gain to be had from these, no?

The speed gain is negligible.

> I guess I wouldn't really fuss.

I would probably do this, since that fits in better with the existing language
and doesn't require any new reserved words.

	Pavel

---

From: Roger Crew <[email-masked]>
Date: Thu, 19 Aug 1993 13:13:39 -0700

About first() and rest():  

  I'll have to admit that this proliferation of synonyms bothers me.
  Why do we need first(a) and rest(a) 
  when we already have a[1] and listdelete(a,1)?

  I've always found the latter two to be clear and unambiguous.

About symmetry and being able to easily grab/omit final elements:

  Granted, symmetry would be really nice, but the only way negative subscripts 
  make sense to me is if we view lists as being circular, e.g.,

   a={#1,#2,#3,#4};

   a[-2]   => #3
   a[-1]   => #4
   a[0]    raise E_RANGE
   a[1]    => #1
   a[2]    => #2
   a[3]    => #3
   a[4]    => #4
   a[5]    raise E_RANGE
   a[6]    => #1
   a[-3..-2] => {#2, #3}
   a[4..6]   raise E_RANGE
   a[7..8]   => {#2, #3}

  Note that because lists are 1-based, we have to paste in an extra
  "forbidden element" to get the negative indices to work out right.
  E_RANGE now means "you tried to include the forbidden element".
  Actually, with the forbidden element in there, we could then say that
  an omitted range boundary means "continue in that direction until you
  get to the forbidden element" and now have a reasonable way of doing 
  list prefixes as well as suffixes.

   a[3..]  => {#3, #4}
   a[..2]  => {#1, #2}
   a[..-2] => {#1, #2, #3}
   a[-3..] => {#2, #3, #4}
   a[8..]  => {#3, #4}

--
Roger Crew
Internet:   [email-masked]
LambdaMOO:  Rog

---

From: Roger Crew <[email-masked]>
Date: Thu, 19 Aug 1993 13:14:34 -0700

>   I have been thinking for a long time that the syntax
note that if you make this change (please-please-please? :-)
a corresonding change should be made for FOR statements, 
e.g.,

FOR i IN [1..]
 ...

for those of us who like to write indefinite-upper-bound loops
(and hate using WHILE because we always forget to put in the `i=i+1').
Of course, this would be a lot more useful with a loop exit 
construct and/or an [e-word deleted]

---

From: [email-masked]
Date: Thu, 19 Aug 1993 15:08:14 -0700

>  Granted, symmetry would be really nice, but the only way negative
>  subscripts make sense to me is if we view lists as being circular, e.g.,

My visceral reaction is "Yuck"!  Right now, you know how many elements will be
in x[i..j] l-- j-i+1, as long as this is >= 0.  If you have negative
subscripts, then the equation is:
    j-i+1           if j,i>0,
    length(x)+i-j+1 if i>0, j<0;
    j+i-length(x)+1 if i<0, j>0
    i-j+1           if i,j<0

(If I've made some arithmetical mistakes in the above, then that only goes
to prove my point -- this is confusing!!)

However, I do agree that it is irritating that one has to keep writing
length(x).  Another language I've seen solves the problem by using
an abbreviation, as follows:
  x[i..*] means x[i..length(x)], and
  x[*-3..*] will mean "take the last 4 elements of x, etc
Ie, the * means "length of whatever list the expression is subscripting"

You might want to choose a different symbol, instead of *.  *shrug*

At least for me, this syntax is quite readable.

  -Sam Weber

---

From: Roger Crew <[email-masked]>
Date: Thu, 19 Aug 1993 16:41:10 -0700

No, it's always j-i+1, even when j and i are both negative
(e.g., length(x[-8..-5]) == (-5)-(-8)+1 == (-5)+8+1 == 4).

The real problem is that a number of previously-illegal array
expressions become legal, and the question is, how badly does
this hurt as compared with the benefit obtained from being
able to cleanly refer to final elements?  To what extent do 
we want/need to have broken code stay broken?

My thought was that it's the one-beyond-the-end cases that are the
most important to have producing errors, though admittedly, the case
of foo[wild_random_number] bothers me...

 - - - - - - - - - - - - - - - - - - - - - - - -
Another odd thought that occurred to me was the notion of a 
list reverser

   a = {#1,#2,#3,#4,#5}
   a[2..3]   => {#2, #3} 
   a|[2..3]  => {#4, #3}
   a|[2..3]| => {#3, #4}
   a|[1]     => #5
   a|[2..]|  => {#1, #2, #3, #4}

I suppose if we need to be constrained to existing syntax we could use
unary minus to reverse lists, in which case we get things like

   (-a)[2..3]  => {#4, #3}
   -(-a)[2..3] => {#3, #4}
   (-a)[1]     => #5
   -(-a)[2..]  => {#1, #2, #3, #4}

but I think a postfix reverser works much better...
--
Roger Crew
Internet:   [email-masked]
LambdaMOO:  Rog

---

From: [email-masked]
Date: Thu, 19 Aug 1993 17:36:43 -0700

>(e.g., length(x[-8..-5]) == (-5)-(-8)+1 == (-5)+8+1 == 4).

This is not true.

length(x[5..-8]) is not -8-5+1= -12

In fact, length(x[5..-8]) is an error if length(x)<12, and is equal
to length(x)-12 otherwise.

  -Sam

---

From: Roger Crew <[email-masked]>
Date: Thu, 19 Aug 1993 22:34:51 -0700

My apologies; that was a bad use of the word "always".
What I should have said is that it's

  max(0,j-i+1) 

in the all of the non-error cases.

In the case where i>0 and j<0, x[i,j] is empty (this is in accordance 
with the current convention, i.e., that x[i,j] is empty when j<i).
The case i<0,j>0 would be an error in the scheme I was proposing
since x[i,j] would be including the "forbidden element" x[0].

Actually, I'm now convinced that any attempt to reconcile negative
indices with the current state of affairs is basically hopeless.

For one thing, it's now clear that what *you* meant by x[5..-8] is
"the sublist of x starting at the fifth element and ending at the
eighth from the end," which I'll admit is not an unreasonable thing 
to expect, but which is entirely different from what I had in mind,
namely the empty list, obviously (*), since 5 > -8.

(*)  :)