From: Robert Klemme Date: 2007-12-30T20:55:02+09:00 Subject: Re: Add Array#first= and Array#last= to std lib On 30.12.2007 02:17, MonkeeSage wrote: > On Dec 29, 6:06 pm, Gary Wright wrote: >> 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: > [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 => # irb(main):005:0> f.last => 1 irb(main):006:0> f.last = 10 NoMethodError: undefined method `last=' for # 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