Created by escargo on 28 Feb 2003
A rule of thumb for implementing a "Low Coupling Principle" for objects [L1 ] [L2 ] [L3 ].
"The Law of Demeter was originally formulated as a style rule for designing object-oriented systems. "Only talk to your immediate friends" is the motto. The style rule was discovered at Northeastern University in the fall of 1987 by Ian Holland.
A more general formulation of the Law of Demeter is: Each unit should have only limited knowledge about other units: only units "closely" related to the current unit. Or: Each unit should only talk to its friends; Don't talk to strangers."
What has this got to do with Tcl?
Consider lset or some of the extra features proposed for various new or enhanced commands, for example recent TIPs[L4 ] TIP 111 and TIP 127.
By allowing access to the extra depth into nested lists or dictionaries, coupling is increased.
It's a trade off between increased coupling between the code and the data and increased performance.
Increased coupling makes code more fragile or more resistant to change.
This not to say that Tcl should not allow such deep indexing, but people should be clear on the consequences.
NEM 1Mar2003 - While this sounds good in principle, I fail to see how allowing multiple indices (essentially giving multiple dimension lists), and adding a new dictionary data type could in any way be a bad thing. Admittedly, if you have two indices into a list, then more can go wrong if the structure of the list changes, but I don't really consider that too much of a problem. Most times I would use more than one index would be where it made sense (for instance representing a grid in a list), so the list structure is unlikely to change. As for adding the new dictionary data type, you've completely lost me here - in what way does this increase coupling between objects?
escargo - The problem (such as it is) is that when you start having nested structures your code has to make assumptions about the kind of objects it's dealing with. For example, a straight lindex will index into any list. An lset with multiple indices makes assumptions beyond each element being a simple list. (Likewise, indexing into a dictionary is straightforward, but indexing into what is supposed to be a dictionary of dictionaries makes an additional assumption.)
So, how can you easily verify that a list is composed of elements that are all two-element lists? (So that lset will do what you want, or the proposed changes to lsearch.) To the extent that assumptions might be incorrect and the code might not be robust in the presence of unmet assumptions there might be problems.
Michael Schlenker 2Mar2003 - I agree with escargo, that nested structures raise the fragility of code significantly, but what is the alternative? Path one: use a C-coded extension for all complex data structures and manipulation of them. This is the old Tcl way as envisioned by Ousterhout, as far as i have read the papers. Path two: use an OO-framework for more complex data. I use this with XOTcl, but like the simplicity of nested lists.
Take a look at the struct module of tcllib. It grows and grows and grows, why? Programmers want extra data structures without using any C-extension or the burden of OO. Giving them tools to build such structures from nested lists or dictionaries is o.k. in my point of view.
Basically the only problem is missing list introspection, nothing more and nothing less. And there is no simple way to fix it, as a list is just a strong shadow behind the string representation.
Rohan Pall 2003-03-01 The way I see it is that the more effective efficient tools the better. I'm sure a smarty pants can come up with a way to use this thing very very effectively, without screwing up code architecture. You can't just blanket statement a tool like this. What's next on the hit list for code purists?
escargo I did not intend to say that the Law of Demeter puts features or practices on a hit list of things that are forbidden. To the extent that using those features or practices makes code more fragile, extra care is needed when coding them, documenting them, inspecting them, and testing them.
Rohan Pall 2003-03-01 I don't agree that it makes code more fragile -- I do think in some cases it does, and in others it will actually work the other way --> helping your code architecture. This doesn't change the fact that most people should stick to java where their screw ups cannot become extraordinary. Ordinary tools for ordinary people, and extraordinary tools for extraordinary people. Or something. Then again I think forbidden fruit is extra crunchy.
ulis 2003-03-02 I agree with "Increased coupling makes code more fragile or more resistant to change" and "the more effective efficient tools the better". And I hate "code purists".
KPV 2003-03-02 I think the Law of Demeter is a good maxim but that examples for tcl are incorrect. I think the question is not whether one call to lset is better or worse than multiple calls to lindex, but rather one of encapsulation. Keeping internal data structures hidden and accessible only through member functions is, in general, a good thing--it decreases coupling and allows flexibility in changing data structures later on. The downside being that it requires more lines of code and it sometimes may be slower. In regards to lset, the problem is not in the function itself, but rather in who is using it. Maybe the problem with lset is that by providing easier access into deep structures, it increases the programmer temptation to break encapsulation (and thus violate the Law of Demeter).
escargo I can agree with that. One of the other implications of the Law of Demeter with tcl is that object-oriented extensions should make it easy and efficient to pass on method calls to subcomponents. That seems to be one of the features of Snit's Not Incr Tcl. If it's easy for an object to forward method calls, then it is easier to support a broader interface where the caller does not have to keep dereferencing objects to get at subcomponents to invoke their methods (violating the Law of Demeter). Instead, the interface of the containing object fields the method calls and forwards them to the subcomponents and the initial caller remains unaware of implementation details. Coupling is decreased and flexibility is increased.
DKF 2005-12-20: Looking at this discussion, I think the main thing to note is that although the Law of Demeter does apply to Tcl, it's not particularly relevant on the level of a list value. I think the correct place to use it is in determining the interface between packages; if you think about things in those terms, you'll find it all makes much more sense. If a package exposes its state as lists/dicts, the LoD says that it is the package that's wrong and not necessarily the code that uses that information.
escargo 20 Dec 2005 - I think it also applies to objects, especially ones created by composition. (Since there is a choice of object systems in Tcl, actual implementations vary, but the philosophy or design law still applies.) In this case, you should not ask an object to return an internal object for you to act upon. If the object is intended to allow you to perform a function, it should provide a method for you to do that function. It should then either do the function itself or forward that function to an internal object without exposing details of the implementation to you. The contract between the object and its caller is direct; internal details of the implementation should be hidden.
KBK 2005-12-23: In defence of [lset]:
In defining lset, I intended merely to expose lists-as-arrays. Where an object is conceptually a one- or two-dimensional array, I don't see any violation of the Law of Demeter in exposing that; in fact, requiring code to approach a two-dimensional array as a collection of collections is needless exposure of implementation detail; is a two-dimensional array a collection of its columns? Its rows? Something else? From this standpoint, lrepeat is the constructor for arrays; lindex is the accessor; and lset is the mutator.
All the rest of the stuff comes along for the ride. Since Tcl's lists have arbitrary nesting and the capability of non-uniform dimension, you can indeed use these functions in other ways. I thought this better than creating a new array object - which would differ from a list only in that the operations on it would be more restricted. Generally, in Tcl development, Ockham's Razor trumps the Law of Demeter.
DKF 2008-11-08: There's nothing wrong with lset per se, but it should be used in nested mode when that's what is natural for the data model (modeling matrices is a good example of this). It's when you're tempted to implement something much bigger and more complex than that that you can go horribly wrong; better to arrange the code to access the “real” data model into one place and use commands (procedures/methods) to access it at a conceptual level. This is good in many ways, but one of the best is that it makes it easier to look at the code later on and see what it's doing. For example:
# Bad code! lset theModel $names($name) $fields(matrix) $x $y $value # Good code! proc updateMatrix {modelName name x y value} { upvar 1 $modelName model global fields names lset model $names($name) $fields(matrix) $x $y $value } updateMatrix theModel $name $x $y $value
It's not that the code is shorter, it's that the code is saying what it is doing that makes this better. And of course, if/when you decide to update things you only have to fix one spot per access type rather than one per actual access; in most code that makes a huge difference.