From: MonkeeSage Date: 2007-12-31T16:25:08+09:00 Subject: Re: Add Array#first= and Array#last= to std lib On Dec 30, 5:50 am, Robert Klemme wrote: > 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 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. :) Regards, Jordan