From: Robert Klemme Date: 2010-10-14T16:54:21+09:00 Subject: Re: setting local variables in a binding In case you get two copies of this: First send attempt was returned by a spamassassin crash... On Wed, Oct 13, 2010 at 9:23 PM, John Sikora wrote: > Robert Klemme wrote in post #947187: >> Fluent speaking of Ruby makes it somewhat easier but even for me the >> length of the post needs some digesting.  So I won't come up with a full >> coverage of everything you wrote for now.  I hope my remarks are useful >> nevertheless. > > Your remarks are always useful, and I appreciate them. Thanks for the feedback! >> A problem of your design is that you change the classes idea of default >> ordering.  What I mean is this: you need to modify a class to achieve a >> particular ordering.  Now the standard behavior of sorting has changed... > > I do not need to modify a class to achieve a particular ordering, I need > to define a class. The default ordering is set during the class > definition with attr_accessor and set_comparables_order. However, if I > do modify the class (with the same two methods), the default ordering > will change. That's what I mean.  Maybe renaming "set_comparables" to something else is enough (e.g. "attr_sort_order" which IMHO makes it look more like a keyword like "private").  The name "set_comparables" seems to suggest that the order can be changed at will.  People might be inclined to do that multiple times in a program and since at any point in time there is only one such order per class this also poses issues for multithreaded programs (assuming different ordering would be employed by different threads).  An alternative would be to allow to call set_comparables at most once per class. > Or did you mean that a subclass can / will have a different default sort > order than it's superclass? This can certainly be the case and is > actually encouraged to add flexibility. Are these element of a bad > design? If so, why? (You can be brief, hopefully I will understand.) No, the issue I am seeing has nothing to do with inheritance.  See above. >> ...and you can never tell what ordering you will get by only looking at a >> particular piece of code which only contains the call to #sort. > > True, you would have to look at the attr_accessor and > set_comparables_order methods elsewhere in the code (possibly multiple > places). If these have been modified dynamically, the > ClassName#comparables method can be used to return the default > comparables. My point is that sort order is something that belongs to a particular ordering, i.e. the place in code that uses sorting.  If you make this a property of the class which is allowed to change you open your application for all sorts of nasty effects caused by the fact that different pieces of code (not necessarily in separate threads) use that "global variable" in different ways. >> Your need to redefine remove_method etc. is fallout of your design >> decision to change the default ordering.  As I said, I believe there are >> better and more efficient designs. > > True. I said this to make the point that I have thought of things that I > need to do to try to keep things from breaking since Ruby is dynamic. I > think you are saying that by coding this way, I am making it tough on > myself since Ruby is dynamic. Hmmm, need to think about this, because I > know that there will be cases out there that I do not think to cover. I > guess this is a way to tell a good design from a bad one. :-) >> There are still some things that I didn't yet wrap my head around: why >> do you want to make classes keep track of all their instances? > > When I was learing Ruby, I came across ObjectSpace.each_object and > thought that since Ruby makes this method available, why not use it > instead of setting up my own containers? So early versions of the code > used ObjectSpace. Then I discovered self.inherited and class instance > variables.  I decided to use self.inherited to pass along the values of > certain class instance variables that I want inherited (and slightly > modified for that subclass). Since keeping track of child classes was > fairly easy with self.inherited and I could also use it to initialize > @class_all_enum_objects, I dropped the use of ObjectSpace. It seems like > ObjectSpace would be less efficient too. > >> This essentially makes classes global variables - but less obvious. > > Never thought of that. See my comments below on my lack of having to > interface with other users' code. I am not sure whether this is only related to interfacing with foreign code.  Basically my main theme is modularity.  By tying tracking functionality into the classes your design is a tad less modular.  One consequence of this is that since each class is a singleton you can only ever track all instances of a class in one place.  Assuming your application grows and you need this tracking in different places but independently you are screwed.  If you separate the tracking as I have tried to demonstrate it's as easy as creating another InstanceTracker. >>If you want automated tracking of >> instances there are other options, e.g. >> >> class InstanceTracker >>   ... lines of code >> > end >> >> it = InstanceTracker.new >> >> it.new(Foo, 1, 2) >> it.each_class(Foo).find_all {|f| f.size > 0} >> > > I see, but why have a seperate class for this? Aren't you doing the same > thing? From a functionality point of view, yes.  But I choose to distribute the functionality in a different (more modular) way across language artifacts (classes and methods).  Separating concerns is an important task of a software engineer: all the time when coding we decide where we place functionality.  For small scripts it's OK to lump everything together.  If applications grow you often have to go through a painful refactoring process to untangle different aspects.  If you start out modular you _may_ have increased effort initially but it pays off mid to long term.  Also, it _can_ help to make code more readable. > I think that the reason that my code is the way it is, is partially due > to the fact that I am writing my code in isolation; I do > not have to interface to other code to perform a broader function. In > fact, I have no experience writing code with any kind of interface > (explains a lot, huh?). Well, I guess I do use gems, so I am not in > total isolation, and at least I have given it some thought since I > turned away from mofidying Array and Enumerator directly. Well, that's perfectly OK.  Software is soft and so we change it over time.  Also, we as humans learn while we go along.  My coding certainly has changed over the years.  That's only natural.  And discussing things like these helps in thinking differently about code.  It's a creative process that increases knowledge on all sides. Kind regards robert -- remember.guy do |as, often| as.you_can - without end http://blog.rubybestpractices.com/