Re: SoA Vs OO
From: I spy (s_nedunuri_at_yahoo.com)
Date: 07/04/04
- Next message: Virus: "Determining the location of the program"
- Previous message: I spy: "Re: SoA Vs OO"
- In reply to: Joachim Durchholz: "Re: SoA Vs OO"
- Next in thread: Mark S. Hathaway: "Re: SoA Vs OO"
- Reply: Mark S. Hathaway: "Re: SoA Vs OO"
- Reply: Joachim Durchholz: "Re: SoA Vs OO"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Date: Sun, 4 Jul 2004 10:46:57 +0100
"Joachim Durchholz" <jo@durchholz.org> wrote in message
news:cbvj0g$k29$1@news.oberberg.net...
> I spy wrote:
>
> > "Joachim Durchholz" <jo@durchholz.org> wrote:
> >>
> >> Hmm... I never saw a class library that did the things the way I
> >> liked them ;-)
> >
> > Not a single one? What are you trying to do? :-)
>
> Oh, simple things. Like a GUI library that sat on top of GDI and created
> all things of nifty abstractions (most notably validation, a rule-based
> framework for automatically resizing subcontrols when the enclosing
> control changed size, a homegrown table control that could host any
> other controls, and a tab order mechanism that was independent of
> control creation order - none of these were mean feats *ggg*) (in
> fairness, I must add that we were a team of three *ggggg*)
I may be misunderstanding you but these sound like the kinds of things you
ought to be able to to in Swing but then I haven't really used Swing much so
I can't say.
> >> And, let me repeat: it's *not* a roll-your-own version of OO.
> >> First, what's a class in an OO language is usually spread all over
> >> the data structures in an FPL, in the form of single-purpose
> >> functions. There is no such thing as a strictly coupled data type,
> >> not in conjunction with dispatching. (FPLs have abstract data types
> >> and tightly coupled, encapsulated implementations for them. It's
> >> just that they don't use dynamic dispatch for it.)
> >>
> >> As I wrote below, it can be made into a roll-your-own version of OO
> >> dispatch using records of closures. You still don't have local
> >> data in these records, nor do you have inheritance, nor any of the
> >> other bells and whistles (which is just as well: these records
> >> simulate just dynamic dispatch, not the other features of OO, which
> >> are supplied via the ADT mechanisms).
> >
> > Yes I got all that, but I'm still not sure exactly what your setup
> > looks like.
>
> Um... time for an example.
>
> Say the task is to have a list of things that can display on a screen.
>
> In the OO world, I need to have a type Displayable, with a
> draw(where:Screen) method. If I need to display something that doesn't
> inherit from Displayable (maybe because it's from the class library of a
> third party), I have to write some wrapper code (if Displayable is a
> large class with many, in this context unneeded features, what I
> actually want to do is buried in tons of other code, either lots of
> stubs or lots of wrapper code).
Tools man tools! Eclipse will generate the delegation code for you
> In the FPL world, I make a list of curried functions.
> I wrap every object that I want to draw in a function that will draw it
> on a Screen object.
> So, if I have a Rectangle type that offers me
> draw_rect x,y,w,h:int; where:Screen
> I say
> new_list = cons
> (draw_rect 1 15 50 60 _)
> -- invented syntax for creating a curried function
> old_list
> to prepend a 50x60 rectangle at (1,15) to old_list.
> If I need a circle, I do likewise.
> If I need a bitmap, I do something like
> (draw_bitmap (bitmap_from_file "foo.bmp") 50 80 _)
I assume that draw_rect, draw_bitmap, etc all return the same type in order
that you can place the returned terms in the same list?
> What I get is a list of functions that will draw a given scene on the
> screen. Or on something else (if the various drawing routines accept
> something more general than a Screen).
OK standard OO stuff..
> For traditional thinking, this list is just a series of functions
> waiting to execute.
> In a functional context, this isn't less true, but the list is also just
> data. It *is* the scene.
>
> If I want to do hit-testing (i.e. associate a given point with a
> geometric figure), I create a similar list.
What, and repeat all that construction information, for potentially 1000's
of shapes? I *must* be missing something...
> I'm free to leave out those
> scene elements that are irrelevant for hit-testing (and don't have to go
> back to the Displayable class and add a boolean attribute that says
> whether it can be found during hit testing - besides, which elements
> hit-test and which don't may vary due to circumstances, so this isn't a
> really satisfactory solution anyway). I can add elements that aren't
> Displayable but require hit-testing anyway (e.g. invisible areas like
> "anywhere on the table line, between the controls on it").
Why do you want to go and muck with Displayable? You just need another
interface HitTestable which objects that can be hit tested will support.
When you create your list, excuse me, collection of Displayable widgets you
create a collection of HitTestable ones too (the two collections will most
probably share elements of course).
> In short, a class hierarchy forces me to make many design decisions,
> quite far up in the hierarchy.
I am not sure what decisions you're referring to
> In an FPL, I can defer many of these decisions. I can defer them until I
> really need to make them.
>
> >> The advantage of not having everything in its separate type is that
> >> there are less barriers for using a type. If everything is a list,
> >> you can apply the standard list functions to everything, without
> >> the need for type acrobatics. In an OO language, I have to lay my
> >> type structure out very carefully to avoid having barriers, and
> >> sometimes I'm forced to choose the lesser of to evils.
Actually, this one slipped my notice the first time. What do you mean by
"lay out my type structure carefully"? What is so careful about declaring to
be a generic Collection?
> > And this is where we get into the maintenance issue. Making
> > everything a list might do the job for now, but what if I really do
> > need O(1) or O(logn/nlogn) access down the road?
>
> That's a valid criticism. It's difficult to replace code that uses lists
> with something that uses other, more advanced data structures.
>
> On the other hand, the issue is being addressed. There are type classes
> and functors (which are different ways of abstracting out commonalities
> like iteration-over-a-bag and similar stuff, i.e. can be used as ADTs).
Type classes and functors are nice but what's been going though my mind here
is ADTs. There really is no excuse for the weak support for ADTs in Haskell.
ADTs predate both OO and FP. Perhaps b/c OO popularized the use of ADTs,
they are starting to be taken more seriously now in Haskell.
> > OTOH, OO definitetly doesn't come for free. There is a cost to having
> > to anticipate what might change.
>
> Definitely :-)
>
> >> Besides, these nitty-gritty functions will have to be implemented
> >> some day. The ability to have the IDE generate the code for me just
> >> defers the work, it doesn't eliminate it.
> >
> > We're going in circles here. If you need to implement them anyway,
> > then where's the extra labour you're complaining about?
>
> Because if I'm forced to implement them right now, at a point in the
> design where I don't need them, I'll pretty sure make mistakes. I'll
> want to get over with that just to get my code working, so I'll sloppily
> hack something together and test it as quickly as possible (i.e. usually
> not at all - I have a deadline to meet, and my superior won't understand
> that I missed by a week just because I had to clean up some service
> class and write code that isn't even used).
> Half a year after that, this sloppy, scantily-tested code will come back
> and bite me. Colleagues might rely on those nominally-working functions,
> set their deadlines according to their expectations, just to find that
> the code they're relying on doesn't really work.
This is a procedural/managerial issue you're trying to solve with a
technical answer. If the classes need those methods, then they need those
methods. You can hack it for now, but if your pointy haired boss doesn't
understand its a GAD (Gross And Disgusting - a great term coined by Marshall
Cline :-) implementation then I don't think the solution is to say oh well I
guess class hierarchies were a bad thing after all b/c we shipped incomplete
code. In any case, are you saying that you've never been in the situation of
having to stubbed out functions in the FP world?
> I've been on both sides of this fence far too often to find much comfort
> in the idea of "abstract everything in classes, and if a new subclass is
> called for, you must *immediately* implement *everything* in it".
What do you mean everything? The whole point of sublassing is that you
inherit the functionality of the parent. Its *saving* you labor not costing
you.
> I could do stubs. That would have the advantage that the
> untested/not-yet-done parts of the class are clearly marked.
> However, this moves a large part of static checking back into the
> dynamic domain - these stubs won't be detected by the compiler, they
> will fail at run-time.
>
> The functional approach is more pragmatic here. Write those functions
> that you need, when you need them. Ironically, I feel FPLs are far
> nearer to Extreme Programming practices in this respect than OO
> languages :-)
I'll save my opinions on XP for another day :-)
> >>> OK, I'll concede you the wilful overriding bit. That's a problem
> >>> of a lack of semantic typing in most OO languages.
> >>
> >> Which isn't really feasible, even with Design by Contract in mind
> >> (I have used it for several years, and while it's far, far better
> >> than nothing, it doesn't and cannot cover all cases).
> >
> > Well there's always going to be exceptions in any real world problem
> > (the Birds can fly/Penguins example comes to mind). You have to deal
> > with them as best you can, and that might sometimes call for willful
> > overriding.
>
> Which is again a case for not doing it with type hierarchies :-)
Nonsense. We use hierarchies all the time. The example itself comes from a
hierarchy in Zoology! In fact the only way we can make sense of the world
(and as it turns out the software beast) is by categorizing.
> (Oh, the contortions I had to make to accommodate such cases in an
> otherwise perfectly class hierarchy... boolean members like "can_fly"
> which had to be properly initialized and kept correct at all times,
> assumptions that these members could never change during the lifetime of
> an object that turned out to be wrong later, and - worst of all - having
> to add such flags very, very high up the class hierarchy, possibly
> breaking any code that introduced its own can_fly attribute, for
> potentially different purposes... you're recalling some very ugly
> memories in me - not that that should be considered your fault *g*.)
Any hierarchy that mimics the real world is going to have flaws b/c guess
what the real world has "flaws" too. A few birds don't fly. A few mammals do
lay eggs. A few products don't support a feature or two. I dont see that as
a case for doing away with hierarchy. Heck even mathematics has "flaws" - I
can divide any two Numbers except for zero.
> >>>>>>>> The advantage is: you don't have to inherit from a
> >>>>>>>> common "CanvasItem" superclass that may be polluting
> >>>>>>>> your namespace. [...]
> >>>>>>>
> >>>>>>> I am not sure what you mean by "polluting the namespace"
> >>>>>>
> >>>>>> If I inherit the CanvasItem class, my new subclass
> >>>>>> automatically contains all the stuff that was declared in
> >>>>>> the superclass, whether I want it or not.
> >>>>>
> >>>>> Well that goes back to the issue of whether your inheritance
> >>>>> hierarchy was appropriate to start with. Again think of the
> >>>>> WindowWithScrollBar class. If I add stuff to the Window base
> >>>>> class, then yes I would want WindowWithScrollBar to reflect
> >>>>> that too. If not, then add it privately.
> >>>>
> >>>> No, I didn't mean that. Say the Window class contains a
> >>>> GdiHandle member. It's private since it's doing very, very
> >>>> crufty internal stuff, so the subclass can't even access it.
> >>>> Yet when the subclass declares a GdiHandle member, it has a
> >>>> name clash.
> >>>
> >>> And for a very good reason! if the base class introduced
> >>> GdiHandler, there's a goodly possibility that whatever code you
> >>> had in your derived class may be obsoleted. So it doesn't
> >>> surprise me that it breaks your derived class.
> >>
> >> Why on earth should the subclass care whether the superclass now
> >> reimplements something that's perfectly working in the subclass?
> >
> > But without a semantics and theorem prover and a lot of time on your
> > hands you aren't going to know whether or not it was an addition to
> > or an obsoleting. If the base class was introducing a concept that
> > didn't exist before and the concept had a name clash with one I
> > already use, I think I'd want to at least investigate and eliminate
> > the potential for confusion for future readers of my program.
> > Remember also we're talking implementation inheritance here, which as
> > Bertrand Meyer points out is useful - but risky.
>
> Thanks for making my point: that subclass inheritance is unmodular :-)
You can read what you want into what I've written, but I said nothing of the
sort. You were complaining about namespace pollution, but that's missing the
point. What's going on here isn't namespace pollution but a potential
semantics conflict. Its rather like when you merge two DB schemas and you
have the inevitable name conflicts ("Address" used in one DB isn't quite the
same as "Address" used in the other DB), and one of the DBAs complains of
namespace pollution!
> >> Assuming the subclass uses the GdiHandle for something that's
> >> entirely unrelated to what the superclass is doing (say, it's the
> >> handle to a bitmap, while the superclass wants to refer to a cached
> >> window handle) - then the class is entirely irrelevant.
> >>
> >> The bad thing isn't that there may not be good reasons. The bad
> >> thing is that the superclass and the subclass aren't properly
> >> encapsulated from each other. They cannot be if the subclass uses
> >> internals of the superclass.
> > If you're really bothered by it then use the public access methods
>
> Which usually aren't available - that GdiHandle thing was supposed to be
> strictly internal.
> Besides, we've been talking about name clashes in the subclass. These
> happen independently of whether the member is externally visible or not.
>
> > Well if you have anything that's supposed to look like an inheritance
> > hierarchy in your problem domain you've severely broken it and LSP
> > doing that. So I would say "don't do that!"
>
> Exactly.
> Interface inheritance (i.e. subtyping) can be a useful tool to
> streamline interfaces.
> Implementation inheritance is - according to my experience - not worth
> the risks. It should be replaced by good abstraction and reuse
> facilities that use other means (e.g. modules that can be parameterized
> with types and values, where the values might be functions or closures).
>
> Of course, that's just my personal view :-)
If its not exposed publicly, and you're a good designer who knows what
you're doing, I have no problem with it at all. Like any tool, its open to
misuse, but please don't outlaw it because some butthead out there doesn't
know how to use it. I'm still POd about Gosling leaving multiple inheritance
out of Java bc programmers might get confused.
> > I am not denying that the orthogonal decomposition you have outlined
> > isn't useful. But which way I break it up should be a function of
> > what my expectations are for the software I am designing. I don't
> > like having it forced on me by language shortcomings
>
> If implementation inheritance is the only or the main way of reusing
> code, than *that* is a language shortcoming.
Its not your only means, and never has been, certainly not in Java. You have
interface inheritance, delegation, composition, generics, anonymous classes,
and others I can't even think of right now.
OTOH it would appear that HO functions *are* your primary means in languages
like Haskell and of course when all you have is a hammer...
cheers
-sri
- Next message: Virus: "Determining the location of the program"
- Previous message: I spy: "Re: SoA Vs OO"
- In reply to: Joachim Durchholz: "Re: SoA Vs OO"
- Next in thread: Mark S. Hathaway: "Re: SoA Vs OO"
- Reply: Mark S. Hathaway: "Re: SoA Vs OO"
- Reply: Joachim Durchholz: "Re: SoA Vs OO"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]