Add Array#first= and Array#last= to std lib

2007/12/28, Yu-raku-an [email protected]:

Hi, Robert,

In this case, I think the best way for you is to do it by yourself.
You can override an existing class in Ruby, even the one is a standard
library class.

I am aware of that, thanks. The whole point of this discussion was to
find out whether there are serious reasons why one should not have
this in the standard lib. Array#first and #last are in the standard
lib already and at the moment I do not see any reason why Array#first=
and Array#last= should not be.

Kind regards

robert

Robert K. wrote:

I do not see why allowing last= would make lasts(n)= necessary.

For consistency. I believe that any method foo= should be equivalent to
the
method foo except for the fact that it sets the value instead of getting
it,
which in this case isn’t possible (without changing the language). I
mean,
that’s not a major problem, but it’d feel a tad incosistent to me.

Robert K. wrote:

against.
I’m not quite sure whether you’re talking to bbiker or me, but if you’re
talking to me:
Yes, that was the point I was trying to make. I’m on your side,
remember?
(Well, mostly anyway)

Robert K. wrote:

So far the only argument against I have seen so far is the question
about assignment to an empty array. In that case I’d say, either raise
an exception or silently add the element. Have to think about this a bit.

I really don’t think that’s much of an argument. Since last= would be
equivalent to [-1]= in every other case, it should behave like it in
this case as well (meaning raise an exception) and if last= uses []=
(which I assume, it would) that would automatically be the case anyway.

2007/12/28, Sebastian H. [email protected]:

Robert K. wrote:

I do not see why allowing last= would make lasts(n)= necessary.

For consistency. I believe that any method foo= should be equivalent to the
method foo except for the fact that it sets the value instead of getting it,
which in this case isn’t possible (without changing the language). I mean,
that’s not a major problem, but it’d feel a tad incosistent to me.

You’re right. Actually I was not aware that you could actually use an
argument with last. Thanks for pointing that out!

And I agree, that changes the situation a bit. As a workaround you
could implicitly use the number of elements when assigning an Array
but I doubt it’s a good idea. What if you wanted the last element to
be that array and not replace the last n elements? That situation
could not be distinguished. Hm…

Re your other posting: yes, making last= behave like [-1]= is
certainly a good idea. So it’s “throw an exception”.

irb(main):004:0> a=[]
=> []
irb(main):005:0> a[-1]=123
IndexError: index -1 out of array
from (irb):5:in `[]=’
from (irb):5
from :0

Also a tad inconsistent:

irb(main):006:0> a[0]=123
=> 123
irb(main):007:0> a
=> [123]

So at least I now have an idea why last= is absent from the std lib.
So the discussion was definitively worthwhile.

Kind regards

robert

On Dec 28, 6:39 am, “Robert K.” [email protected]
wrote:

You’re right. Actually I was not aware that you could actually use an
argument with last. Thanks for pointing that out!

And I agree, that changes the situation a bit. As a workaround you
could implicitly use the number of elements when assigning an Array
but I doubt it’s a good idea. What if you wanted the last element to
be that array and not replace the last n elements? That situation
could not be distinguished. Hm…

So at least I now have an idea why last= is absent from the std lib.
So the discussion was definitively worthwhile.

I see it from a different pov: Wasted Semantics. Regardless of the
fact that #first and #last can take an optional argument, #first=
and #last= are both semantically obvious and would take up essentially
no new space in the ruby “dictionary” (to use a Forth term). So, NOT
having them is a waste of resources.

Also, it’s not inconceivable that they could provide some added value
by being different form [0], [-1] by not throwing an error.

T.

On Dec 27, 2007 7:52 PM, Trans [email protected] wrote:

[].last = 1

still be convenient to have the obvious n=1, no arg case. I find that
my programs are usually easier to read when I can use words rather non-
alphabetic symbols.

T.

Amen, big +1, I guess lots of us define #last= and #first=, I do it,
it is just that readable and I did not even check the
doc before trying to use it first, I hate to be disappointed by my
favorite language :wink:

Cheers
Robert

http://ruby-smalltalk.blogspot.com/


All truth passes through three stages. First, it is ridiculed. Second,
it is violently opposed. Third, it is accepted as being self-evident.
Schopenhauer (attr.)

On Dec 28, 6:39 am, Robert K. [email protected] wrote:

You’re right. Actually I was not aware that you could actually use an

irb(main):006:0> a[0]=123


use.inject do |as, often| as.you_can - without end

I mho, making #last= and #first= adds nothing to ruby, the ability is
already available. So why bloat ruby unnecessarily?

On Dec 27, 1:58 pm, Sebastian H. [email protected]
wrote:

I’m sorry if I wasn’t clear, let me rephrase:
ICQ: 205544826
Sebastian (and Robert),

I guess I wasn’t being very clear. By saying that #last just returns a
value like #pop, my point was that #last is the semantic equivalent of
the example I gave: “def l(a); a[-1]; end”. I should have probably
compared it to #length rather than #pop since #pop is destructive.

I wasn’t arguing for or against #last=, per se; I was only trying to
explain why it doesn’t make sense, to me, “Unless the semantics [of
#last] change,” to have #last=. I.e., #[] subscripts the array and
returns the value of the index, but #last returns the value of
subscripting the array at index #length-1; it is another level of
abstraction removed from #[]; so with those semantics, #last= would be
like #length= (or #pop=), and you end up with exceptions when the
value is immutable.

Regards,
Jordan

On Dec 29, 6:06 pm, Gary W. [email protected] wrote:

you seem to be objecting based on semantics and I’m trying to understand
that objection because the semantics seem to be quite natural to me.

Gary W.

As I understand it, #last is semantically equivalent to this code:

class Array
def last
self[self.length-1]
end
end

Which means that #last is a specialized form of #[], which means that
it is another level of abstraction removed from #[]. So the result of
#last is the same as #[-1], but the semantics of #last are more like
#length or #pop, which are also a level of abstraction removed from
#[]. Once again, I’m not arguing against #last=, per se, but I fail to
see how it differs at the present time from, for example:

class Array
def foo
self[self.length-1]
end
end

[1,2,3].foo = 25

Or more simply, [1,2,3].length = 20.

Of course, one could implement those methods where they so inclined,
but it would make the semantics of, e.g., #foo and #foo= very
different. In this case of #last / #first, I don’t think it is very
confusing to have those different semantics; I was probably being too
much of a stickler for “purism”.

Regards,
Jordan

On Dec 29, 2007, at 6:24 PM, MonkeeSage wrote:

I wasn’t arguing for or against #last=, per se; I was only trying to
explain why it doesn’t make sense, to me, “Unless the semantics [of
#last] change,” to have #last=. I.e., #[] subscripts the array and
returns the value of the index, but #last returns the value of
subscripting the array at index #length-1; it is another level of
abstraction removed from #[]; so with those semantics, #last= would be
like #length= (or #pop=), and you end up with exceptions when the
value is immutable.

Why do you shift your terminology when talking about last? Using
your terminology but my mental model of arrays:

#[n] subscripts the array with n and returns the value of the index
#[-1] subscripts the array with (length-1) and returns the value of
the index
#last subscripts the array with (length-1) and returns the value of
the index

Why do you rephrase that last case as:

#last returns the value of subscripting the array at index #length-1

Are you suggesting that #last does something distinctly different than
subscripting via -1? If not then it would seem your argument against
last=
would apply just as well to ‘array[-1] = x’. Are you suggesting that
element assignment with negative subscripts also don’t make sense to
you?

Objections to last= and first= based on redundancy is one thing, but
you seem to be objecting based on semantics and I’m trying to understand
that objection because the semantics seem to be quite natural to me.

Gary W.

On 30.12.2007 02:17, MonkeeSage wrote:

value is immutable.

you seem to be objecting based on semantics and I’m trying to understand
end

Which means that #last is a specialized form of #[], which means that
it is another level of abstraction removed from #[]. So the result of
#last is the same as #[-1], but the semantics of #last are more like
#length or #pop, which are also a level of abstraction removed from
#[].

I do not understand your talking of “removing a level of abstraction”.
Basically, I’d say #first and #last are more abstract than #[] because
they add semantics and they are (or at least could be) built on top of
#[]. You do not have to provide the detail of the index to access.

Then, comparing #[], #first and #last with #length and #pop seems very
strange to me - for different reasons: #length has nothing to do with
element access (which is all #[], #first and #last are about). #pop on
the other hand does deal with element access but it also modifies the
Array.

Once again, I’m not arguing against #last=, per se, but I fail to
see how it differs at the present time from, for example:

class Array
def foo
self[self.length-1]
end
end

Ah, I believe I start to see something: you do not seem to recognize #[]
and #[]= as regular methods. With the implementation of #foo that you
present above you can not do this assignment:

[1,2,3].foo = 25

Consider this:

irb(main):001:0> class Foo
irb(main):002:1> def last;1;end
irb(main):003:1> end
=> nil
irb(main):004:0> f=Foo.new
=> #Foo:0x7ff958ec
irb(main):005:0> f.last
=> 1
irb(main):006:0> f.last = 10
NoMethodError: undefined method `last=’ for #Foo:0x7ff958ec
from (irb):6
from :0
irb(main):007:0> class Foo
irb(main):008:1> def last=(x) puts “assigned”; end
irb(main):009:1> end
=> nil
irb(main):010:0> f.last = 10
assigned
=> 10

You have to implement #last= in order to be able to do the assignment.
You do not automatically get it for free when defining #last. So #last
corresponds to (or: “can be implemented in terms of”) #[] while #last=
corresponds to #[]=. Similarly #last also works with frozen instances
(like #[] does) but #last= does not (like #[]= does not). Does this
make sense?

I do not know your (programming) background but if you think Ruby’s
Array is similar to a C array then maybe you should drop that notion
because they are vastly different (note, this does not refer to the
implementation but to how they are used in each language).

Or more simply, [1,2,3].length = 20.

As #length has nothing to do with element access I would expect the
length of the Array to change when assigning to it (e.g. by pruning or
appending nils).

Of course, one could implement those methods where they so inclined,
but it would make the semantics of, e.g., #foo and #foo= very
different. In this case of #last / #first, I don’t think it is very
confusing to have those different semantics; I was probably being too
much of a stickler for “purism”.

Again, this semantic difference you are talking about is still not clear
to me. The semantic between #last and #last= and #first and #first= I
can see at the moment is the lack of a parameter as has been mentioned:
while you can make #last and #first return a sub array you can only
assign to a single element with the assignment variants. But that seems
ok to me.

Kind regards

robert

On Dec 30, 2007 10:55 PM, Robert K. [email protected]
wrote:

Again, this semantic difference you are talking about is still not clear
to me. The semantic between #last and #last= and #first and #first= I
can see at the moment is the lack of a parameter as has been mentioned:
while you can make #last and #first return a sub array you can only
assign to a single element with the assignment variants. But that seems
ok to me.

I think I’m sold on the idea. I don’t see the extra semantics of
#first and #last over the standard attribute behavior as a problem
either; the getters are just overloaded with a second meaning.

The only slight weirdness I see in it is that [].first = 1 would raise
an IndexError, which seems odd since it doesn’t look like there’s any
indexing going on. But I’m not sure what would make more sense, and
it’s probably best to keep consistent with #[] anyway.

Regards,
George.

On Dec 30, 5:50 am, Robert K. [email protected] wrote:

returns the value of the index, but #last returns the value of
#last subscripts the array with (length-1) and returns the value of
element assignment with negative subscripts also don’t make sense to
class Array

Once again, I’m not arguing against #last=, per se, but I fail to
present above you can not do this assignment:
=> #Foo:0x7ff958ec
irb(main):010:0> f.last = 10
I do not know your (programming) background but if you think Ruby’s
Of course, one could implement those methods where they so inclined,
ok to me.

Kind regards

    robert

I guess I still wasn’t being clear (hey, I’m trying!). I understand
that you don’t get a setter, #foo=, just by defining #foo; and I
realize that #[] and #[]= are methods like any other method. I surmise
that further attempts to explain my reasoning will only serve to
further muddy the water, so I’ll withdraw my statements. I have no
real problem with #last= / #first=. I was more talking about
theoretical “purity” than practical application. But I think that with
ruby pragmatism is the rule; just ignore my previous comments. :slight_smile:

Regards,
Jordan

[1,2,3].length = 20

ewww. what is this :frowning:

On 31.12.2007 08:22, MonkeeSage wrote:

returns the value of the index, but #last returns the value of
the index
that objection because the semantics seem to be quite natural to me.
#length or #pop, which are also a level of abstraction removed from

irb(main):005:0> f.last
assigned
Array is similar to a C array then maybe you should drop that notion

different. In this case of #last / #first, I don’t think it is very
that you don’t get a setter, #foo=, just by defining #foo; and I
realize that #[] and #[]= are methods like any other method. I surmise
that further attempts to explain my reasoning will only serve to
further muddy the water, so I’ll withdraw my statements.

Oh no, please don’t do that! Now I will be wandering about for the rest
of the year like a bewitched ghost and wondering what it was that you
tried to convey… :wink:

I have no
real problem with #last= / #first=. I was more talking about
theoretical “purity” than practical application. But I think that with
ruby pragmatism is the rule; just ignore my previous comments. :slight_smile:

That’ll be harder than it looks like. :slight_smile: Well, maybe with a new year
at hand you might change your mind and take a fresh start at this. At
least I for one would be interested in learning what it was that you
were aiming at.

Kind regards

robert