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 Wright
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
#[].
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:
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.