On Apr 27, 2:40 pm, Steven D'Aprano <steve +comp.lang.pyt...@pearwood.info> wrote: > On Fri, 27 Apr 2012 10:33:34 -0700, Adam Skutt wrote: > >> Why should the caller care whether they are dealing with a singleton > >> object or an unspecified number of Borg objects all sharing state? A > >> clever interpreter could make many Borg instances appear to be a > >> singleton. A really clever one could also make a singleton appear to be > >> many Borg instances. > > > Trivial: to break cyclical references in a deep copy operation. > > I asked why the *caller* should care. If the caller has to break cyclical > references manually, the garbage collector is not doing its job.
It's a necessary requirement to serialize any cyclical structure. Garbage collection has nothing to do with it. If I have some structure such that A --> B --> A, I need to be able to determine that I've seen 'A' before in order to serialize the structure to disk, or I will never write it out successfully. There are plenty of situations where we legitimately care whether two pointers are the same and don't give one whit about the state of objects they point to. You cannot conflate the two tests, and that's precisely what your 'give all borg instances the same identity' idea does. > I think that if you're talking about per-instance members of a Borg > class, you're confused as to what Borg means. I'm not. I'm talking about per-instance members of a subclass of a Borg class. There's nothing about the Borg pattern that forbids such behavior, which is one of the reasons it's such a terrible idea in general. Borg implies promises that it cannot readily keep. > Since all instances share state, you can't have *per-instance* data. I most certainly can do so in a subclass. Shared state in a parent doesn't mandate shared state in a child. > Not at all. Obviously each Borg subclass will have it's own fake > identity. > When I said that Borg instances are indistinguishable except for > identity, I thought that was obvious that I was talking about instances > of a single type. Mea culpa. > > Clearly if x is an instance of Borg, and y is an instance of > BorgSubclass, you can distinguish them by looking at the type. The point > is that you shouldn't be able to distinguish instances of a single type. No, that's not the least bit obvious nor apparent, and it still violates LSP. It means every function that takes a Borg as an argument must know about every subclass in order to distinguish between them. The serialization function above would need to do so. Imagine an object x that holds a Borg object and a BorgSubclass object. If the serialization function keeps a list of objects it has seen before and uses that to determine whether to write the object out, it will fail to write out one or the other if we implemented your harebrained 'All Borg objects have the same identity' idea. Your idea means that 'x.borg is x.subborg' must return True. It also means either x.borg isn't going to be written out, or x.subborg isn't going to be written out. The program is broken. If you modify your idea to ignore subtypes, than this function breaks: def write_many(value, channel1, channel2): channel1.write(value) if channel2 is not channel1: channel2.write(value) Calling write_many("foo", x.borg, x.subborg) now gives different behavior than write_many("foo", x.borg, x.borg). That's probably not what the programmer intended! Like it or not, whether you have only one object with shared state or infinite objects with the same shared state is not an implementation detail. Just because you write code that doesn't care about that fact does not make it an implementation detail. I can write code that depends on that fact, and there's not a single thing you can do to stop me. This is why the Borg pattern is a bad idea in general, because it encourages programmers to write code that is subtly wrong. If you have a Borg class, you can't ignore the fact that you have multiple objects even if you want to do so. You will eventually end up writing incorrect code as a result. Yet, many people try to do precisely that, your idea is attempting to do precisely that! > Oh please, enough of the religion of LSP. > > Barbara Liskov first introduced this idea in 1987, twenty years after > Simula 67 first appeared and thirty years after MIT researchers came up > with the concept of object oriented programming. That's hardly > fundamental to the concept of OOP. > People have, and still do, violate LSP all the time. People write code with security flaws all of the time too. This doesn't even being to approach being an reasonable argument. It's completely disingenuous. People come up with ideas and fail to properly formalize them all of the time. People come up with useful, revolutionary ideas and get parts of them wrong all of the time. If you violate LSP, then you enable interface users to write buggy code. Correct class hierarchies must follow it; correct interface implementations must follow it. There's nothing optional about it, it even applies even if you don't have objects at all. It's just extra inescapable for the sort of class hierarchies most OOP languages use. > Besides: > > - In real life, subtypes often violate LSP. An electric car is a type of > car, but it has no petrol tank. Wolf spiders have eyes, except for the > KauaŹ»i cave wolf spider, which is is a subtype of wolf spider but is > completely eyeless. Yes, real life is more complicated than the simplistic relationships that most class hierarchies support. So what? Where did I advocate encoding such relationships using class hierarchies? Adam -- http://mail.python.org/mailman/listinfo/python-list