caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* RE: Classes: when and where? (long)  (was: looking for a nail)
@ 1999-02-01 12:48 Don Syme
  1999-02-02 14:42 ` Markus Mottl
  0 siblings, 1 reply; 4+ messages in thread
From: Don Syme @ 1999-02-01 12:48 UTC (permalink / raw)
  To: 'Markus Mottl'; +Cc: caml-list


Thanks Markus.  I think I can buy classes as a structuring device to
encapsulate mutable state, as you say, rather than as an abstraction device.
That is, classes can indeed be effectively employed to create "components",
whereas the utility of subtyping to create "generic components" is not so
clear.  I also agree that modules are not a terribly effective structuring
device when hiding mutable state. I might use records of closures (as in the
Format library), but I would also consider using classes.  Certainly many
C++ and Java libraries benefit from this kind of use.  

Beyond this, I'm aware that there are generic components/abstractions that
can be represented in object calculii that can't be represented conveniently
in the core of Ocaml, and I'm still willing to be convinced of the practical
utility of such abstractions.  I think examples may come, especially when we
try to express complex APIs such as those for existing windowing libraries,
but until then I feel the jury is out.  (IMHO, given the contorted nature of
many OO-based attempts at abstraction, one might feel the jury could have
given a preliminary verdict some time ago ;-) )

On the question of project failures: I would certainly blame the failure
and/or slow-progress of several projects I've seen on the mis-application of
OO design techniques, in particular on the over use of inheritance and
subtyping as an abstraction technique, and the over use of abstraction in
general.  I believe this represents a balanced assessment rather than a
position based on dogma, since it was also the opinion of some of those
involved in the project, after the fact. (I was just an observer).  Other
people, perhaps, can report brilliant success in the application of these
abstraction techniques - I'd be pleased to have my opinions changed!

Cheers,
Don

------------------------------------------------------------------------
At the lab:                                     At home:
Microsoft Research Cambridge                    11 John St
St George House                                 CB1 1DT
Cambridge, CB2 3NH, UK
Ph: +44 (0) 1223 744797                         Ph: +44 (0) 1223 722244
http://research.microsoft.com/users/dsyme
email: dsyme@microsoft.com
   "You've been chosen as an extra in the movie
        adaptation of the sequel to your life"  -- Pavement, Shady Lane
------------------------------------------------------------------------


> As it seems, you see classes just as a (bad) form of abstraction. But
> please don't forget their origin and what they were actually 
> invented for:
> they were specifically invented to encapsulate overwriteable (mutable)
> values together with methods that manipulate their state. I think that
> they appear more natural in this respect than modules.
> 
> Then, in modules you have to explicitly match values so as to extract
> components to which you can apply functions later on. You 
> have to manage
> the whole state (or, if not mutable, value) even if you want to change
> just a very small component. This is not necessary in objects, because
> they "know" of what they consist.
> Just count the times, how often you have to write "_" for unnecessary
> parameters when manipulating values in modules. This is often not very
> readable/intuitiv.
> 
> > Personally, I think the apparent preference of OCaml users 
> to stick with the
> > basic language may be telling - or have I misjudged this?   Comments
> > welcome!
> > 
> > > There is probably hardly any 
> > > program, where
> > > you wouldn't need such constructs. 
> > 
> > In summary, I think my point is that there are many 
> programs (but not all -
> > I don't want to be dogmatic about this) where the 
> application of class based
> > abstraction techniques is inappropriate.  For example, IMHO 
> no compiler I've
> > seen would benefit, nor any symbolic computation system, 
> nor any module in
> > the basic OCaml library apart from, perhaps, the Format library.  
> 
> You are definitely wrong - at least in the rather symbolic area I
> have to program, I find it much more natural to think in objects than
> in modules.  Take, for example, direct graphs (yes, I know 
> Launchbury's
> purely functional, mathematically appealing, even efficient and short
> but still not very intuitiv implementation of graph algorithms):
> 
> Imagine a set of programs that share subprograms (i.e. 
> subgraphs). If I
> want to traverse such structures, evaluate meaning, change 
> the semantics
> of "objects", apply abstraction operators to subtrees 
> (better: subgraphs),
> then I really prefer OO over a normal module system. It is just a more
> *natural* way to reason about it - at least to me.
> 
> That you can do the same with modules does not necessarily mean that
> reasoning about such structures with modules is as intuitiv.
> 
> I hope you see my point: I don't use OO to build huge inheritance
> hierarchies (I hate those). I even don't care too much, whether my
> classes are fully reusable (maximally abstract). All I want to have
> is a means of abstraction that reflects the way I think. And sometimes
> (not always), objects fit better into this scheme.
> 
> Take a look at e.g. Jukka Paakki's paper in ACM Computing Surveys,
> Vol. 27, No. 2, June 1995:
> There he compares different paradigms in implementing languages via
> attribute grammars. One can also define those in terms of "modules" or
> "classes". To me the most concise, flexible and extendible 
> example is the
> OO one. Maybe because its form of abstraction better fits the problem
> of specifying the semantics of a language (at least: via attribute
> grammars).
> 
> Anyway, don't say without true motivation that compilers (here:
> compiler-compilers) cannot profit from this paradigm. It's simply
> not true.
> 
> > > Imagine that all basic types were classes and all of them 
> > > would support
> > > a method e.g. "print".
> > 
> > All very well, but this is, after all, imaginary.  The 
> runtime cost of
> > supporting a dictionary of methods for basic types is just 
> too high (though
> > I guess Haskell manages it via type classes).
> 
> No, this has nothing to do with runtime speed. Wherever you 
> can resolve
> the exact type statically, you can generate the same code (or 
> at least: if
> the compiler is clever enough). This is not a matter of 
> language/paradigm
> - it is a matter of compilation. And where it is not possible 
> to resolve
> at compile time, without objects you would have to use sum 
> types anyway -
> but explicitely whereas this is handled implicitely with objects!
> 
> Best regards,
> Markus
> 
> -- 
> Markus Mottl, mottl@miss.wu-wien.ac.at, 
http://miss.wu-wien.ac.at/~mottl




^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Classes: when and where? (long)  (was: looking for a nail)
  1999-02-01 12:48 Classes: when and where? (long) (was: looking for a nail) Don Syme
@ 1999-02-02 14:42 ` Markus Mottl
  0 siblings, 0 replies; 4+ messages in thread
From: Markus Mottl @ 1999-02-02 14:42 UTC (permalink / raw)
  To: Don Syme; +Cc: OCAML

[snip]
> but until then I feel the jury is out.  (IMHO, given the contorted nature of
> many OO-based attempts at abstraction, one might feel the jury could have
> given a preliminary verdict some time ago ;-) )
> On the question of project failures: I would certainly blame the failure
> and/or slow-progress of several projects I've seen on the mis-application of
> OO design techniques, in particular on the over use of inheritance and
> subtyping as an abstraction technique, and the over use of abstraction in
[snip]

If things do not work so fine with OO, it would be a bit too early
to blame this paradigm for failure. I think the problem is a totally
different one: the design approach.

Thinking back about courses in software engineering (with OO), the
design approach taught was mostly "from top down". In my first projects
I almost often failed completely to implement the design I developed
without fundamentally changing it.

A severe example:
it took me quite some weeks to develope a class system for a specific
project. When it was implemented as designed, I noticed that I had
overlooked many necessary properties to make it work as it should. So I
started to change it. It became worse and worse until I finally decided:
let's kill this beast. I tore out all algorithms / functions, etc.,
which I would need anyway, put them together into small components that,
again, would be needed anyway. Then I did the same with these components
and when I was ready, it more or less worked "out of the box" - within
less than two days.

This "lesson" showed me that the top-down approach is not such a good
method - at least for my style of programming. All that counts in the
long run is: when does the program/prototype work. Instead of investing a
lot of effort into designing "class hierarchies" or "interfaces", I now
begin immediately with implementing small components, of which I know
that they will have to be in the final system anyway - no matter, how
"important" they seem from a "higher-order" point of view. When I have
all these small components, I try to build with them the next "layer of
abstraction" that will bring me closer to the final result - and so forth.

This doesn't seem to be so problematic with modules: I think that modules
enforce a coding style that focuses on "the component" - not so much on
the "system view". If the module works, which is much easier to achieve
because of the smaller size/complexity, it is truly reusable. But
if I fail in the design phase of an OO-system to correctly see all
dependencies between the classes/objects, I will be in deep trouble,
because then I have to manage the whole system complexity at once.


So if you say, that there was an overuse of abstraction techiques,
it might well be due to the wrong "direction" of development: they
tried to begin with the abstractions instead of with the components.
It is probably not a matter of "how abstract" something is, but how this
abstraction came into existence...

Best regards,
Markus Mottl

-- 
Markus Mottl, mottl@miss.wu-wien.ac.at, http://miss.wu-wien.ac.at/~mottl




^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Classes: when and where? (long)  (was: looking for a nail)
  1999-01-29 18:32 Don Syme
@ 1999-01-30  0:17 ` Markus Mottl
  0 siblings, 0 replies; 4+ messages in thread
From: Markus Mottl @ 1999-01-30  0:17 UTC (permalink / raw)
  To: Don Syme; +Cc: OCAML

> I've used a few object systems, and I agree there are certainly abstractions
> you can express with classes that can't be expressed in basic OCaml.  My
> question is more this: what do people reckon is a sensible way of deciding
> when to use class based abstraction?  Having witnessed several OO projects
> sink into a morass of unnecessary inheritance hierarchies (and this with
> very intelligent people on the design teams), it's hard to believe the
> devices should be used indiscriminately.  

This is also a matter of the language - I am not sure, what kind of
projects you mean, but if it's not OCAML, it might be that other reasons
account for the failure (and unnecessary inheritance hierarchies are
just the symptom).

One shouldn't forget that modules can also be used to restrict access
to classes / class interfaces. I think in combination they can be very
powerful.

> I think it would be useful if ultimately the OCaml manual included some
> discussion on this topic, as experience is gained.  In all I've seen very
> few balanced assessments of the value of using classes as an abstraction
> device. O'Caml seems like an excellent place for making such an assessment
> because it's possible to compare solutions side by side, without resorting
> to the old fashioned rhetoric about the essential beauty of the lambda
> calculus or the purity of OO.  ;-)

Just to make sure you know: I am not an OO fetishist. But there are some
applications, where I think they are a very helpful extension.

[lengthy abstraction discussion snipped]

As it seems, you see classes just as a (bad) form of abstraction. But
please don't forget their origin and what they were actually invented for:
they were specifically invented to encapsulate overwriteable (mutable)
values together with methods that manipulate their state. I think that
they appear more natural in this respect than modules.

Then, in modules you have to explicitly match values so as to extract
components to which you can apply functions later on. You have to manage
the whole state (or, if not mutable, value) even if you want to change
just a very small component. This is not necessary in objects, because
they "know" of what they consist.
Just count the times, how often you have to write "_" for unnecessary
parameters when manipulating values in modules. This is often not very
readable/intuitiv.

> Personally, I think the apparent preference of OCaml users to stick with the
> basic language may be telling - or have I misjudged this?   Comments
> welcome!
> 
> > There is probably hardly any 
> > program, where
> > you wouldn't need such constructs. 
> 
> In summary, I think my point is that there are many programs (but not all -
> I don't want to be dogmatic about this) where the application of class based
> abstraction techniques is inappropriate.  For example, IMHO no compiler I've
> seen would benefit, nor any symbolic computation system, nor any module in
> the basic OCaml library apart from, perhaps, the Format library.  

You are definitely wrong - at least in the rather symbolic area I
have to program, I find it much more natural to think in objects than
in modules.  Take, for example, direct graphs (yes, I know Launchbury's
purely functional, mathematically appealing, even efficient and short
but still not very intuitiv implementation of graph algorithms):

Imagine a set of programs that share subprograms (i.e. subgraphs). If I
want to traverse such structures, evaluate meaning, change the semantics
of "objects", apply abstraction operators to subtrees (better: subgraphs),
then I really prefer OO over a normal module system. It is just a more
*natural* way to reason about it - at least to me.

That you can do the same with modules does not necessarily mean that
reasoning about such structures with modules is as intuitiv.

I hope you see my point: I don't use OO to build huge inheritance
hierarchies (I hate those). I even don't care too much, whether my
classes are fully reusable (maximally abstract). All I want to have
is a means of abstraction that reflects the way I think. And sometimes
(not always), objects fit better into this scheme.

Take a look at e.g. Jukka Paakki's paper in ACM Computing Surveys,
Vol. 27, No. 2, June 1995:
There he compares different paradigms in implementing languages via
attribute grammars. One can also define those in terms of "modules" or
"classes". To me the most concise, flexible and extendible example is the
OO one. Maybe because its form of abstraction better fits the problem
of specifying the semantics of a language (at least: via attribute
grammars).

Anyway, don't say without true motivation that compilers (here:
compiler-compilers) cannot profit from this paradigm. It's simply
not true.

> > Imagine that all basic types were classes and all of them 
> > would support
> > a method e.g. "print".
> 
> All very well, but this is, after all, imaginary.  The runtime cost of
> supporting a dictionary of methods for basic types is just too high (though
> I guess Haskell manages it via type classes).

No, this has nothing to do with runtime speed. Wherever you can resolve
the exact type statically, you can generate the same code (or at least: if
the compiler is clever enough). This is not a matter of language/paradigm
- it is a matter of compilation. And where it is not possible to resolve
at compile time, without objects you would have to use sum types anyway -
but explicitely whereas this is handled implicitely with objects!

Best regards,
Markus

-- 
Markus Mottl, mottl@miss.wu-wien.ac.at, http://miss.wu-wien.ac.at/~mottl




^ permalink raw reply	[flat|nested] 4+ messages in thread

* Classes: when and where? (long)  (was: looking for a nail)
@ 1999-01-29 18:32 Don Syme
  1999-01-30  0:17 ` Markus Mottl
  0 siblings, 1 reply; 4+ messages in thread
From: Don Syme @ 1999-01-29 18:32 UTC (permalink / raw)
  To: 'Markus Mottl'; +Cc: caml-list


Markus writes:

>  let print x = x # print
>  List.iter print [3; "hello"; 3.14; ["yeah, another list!"]]

I've used a few object systems, and I agree there are certainly abstractions
you can express with classes that can't be expressed in basic OCaml.  My
question is more this: what do people reckon is a sensible way of deciding
when to use class based abstraction?  Having witnessed several OO projects
sink into a morass of unnecessary inheritance hierarchies (and this with
very intelligent people on the design teams), it's hard to believe the
devices should be used indiscriminately.  

I think it would be useful if ultimately the OCaml manual included some
discussion on this topic, as experience is gained.  In all I've seen very
few balanced assessments of the value of using classes as an abstraction
device. O'Caml seems like an excellent place for making such an assessment
because it's possible to compare solutions side by side, without resorting
to the old fashioned rhetoric about the essential beauty of the lambda
calculus or the purity of OO.  ;-)

So when is it worth using OO features?  For waht it's worth, it seems to me
you must at least have 
	(a) more than one component/representation/type supporting the same
interface
      (b) these components/representations/types also support other
interfaces 
      (c) each component corresponds to just one type (hence I've written
component/representation/type above)
	(d) a non-trivial body of code that you judge to be worth making
abstract over the interface 
      (e) some indication that it won't be more appropriate to use other
abstraction mechanisms, e.g. modules
  and (f) be pretty sure that what you're trying to express will actually be
expressible in the object system you're using

I mention (a) because it's worth remembering that many programs will only
actually work with one implementation of a particular interface, in which
case you've added some abstraction clutter for little gain.  It may seem
stupid, but I've seen many smart people over-abstract in this way - all they
end up doing is creating unnecessary work for themselves.

I mention (b) because many components only support one interface, and in
this case there are alternative abstraction mechanisms.  For example,
Markus' example above could be achieved by modelling an interface as a
record of function closures and some state shared between these closures.
This is how formatters are defined in the Format module in the OCaml
library.  This technique is not as general as classes, since if you have a
single component supporting multiple interfaces then things get very
cumbersome, because you have to do all the "plumbing" by hand.  However in
some ways it is very conceptually simple and localized, and there is no need
to introduce new concepts in a program design.  

I mention (c) because classes necessarily implement only one type, whereas
modules can implement more.  However, I would agree it is relatively rare to
have module parameters (components) that implement more than one type, so
it's probably not a big deal. 

I mention (d) because there's the slightly tangential question of whether to
abstract at all.  I think it's fair to say that sometimes it isn't
particularly worthwhile to abstract common code that performs simple
"plumbing" operations connecting different interfaces.  People often forget
this, and go to extremes to ensure their code is "maximally abstract".  Some
people like it as a kind of programming discipline, but personally I reckon
that some "abstract" code is so short, and gets instantiated only a few
times, that it's easier to write it two or three times rather than go to the
bother of abstracting it.  We all fall into the trap of over-abstraction
sometime or another, since we tend to enjoy dreaming up abstractions more
than getting our programming tasks done.  Often we never see a pay back for
the investment we make in our abstractions, in terms of overall
cost-reduction.  But I think it's fair to say that OO tends to encourage a
tendency toward over-abstraction (just look at some of the ridiculous class
hierarchies floating around), in addition to supporting some valid
abstractions.

I also mention (d) because there's the (again slightly tangential) question
of _when_ to do abstraction.  I think it's fair to say abstraction is
usually best applied midway or late in the design process, or when we're
trying to create a library for reuse.  I agree with you that some programs
can be simplified at a late date by judicious use of subtyping and other
abstraction techniques.  However, many OO folks I've met tend to try to
design their subtype hierarchies early in a project, sometimes leading to
disaster.  This is because they wasted a lot of time discussing
representational decisions that were of very dubious long term benefit.  In
real projects, the early part of the design process is where wasted time can
hurt the most: after all, you probably should be spending the time coming up
with a prototype to show the customers (just to check that you're building
the right thing), rather than designing nice abstraction hierarchies that
may or may not give better reuse.

Often, of course, it is definitely worth writing abstract versions of
algorithms or other code.  I mention (e) because modules are sometimes the
best solution to such problems.  They're not perfect, e.g. the types
produced from different applications of a paramaterized module are not
compatible, when similar OO types might be, and this can tend to lead to a
proliferation of pretty mindless modules.  However, modules do at least
admit statically type-checked binary methods and so on in a pretty
straight-forward fashion, and their semantics are pretty much good ol'
substitution.  They also permit simpler code optimization than dispatch
mechanisms.  This is why modules are a good choice for simple purposes such
as the collection types in the OCaml library.

Finally, even when we decide a design abstraction doesn't fit other
mechanisms, it's not always easy to make it fit an object model either.  For
example, many object abstractions ultimately rely on casting, which is only
available via Obj.magic in Ocaml.  Similarly it's easy to get in trouble
with contravariance/covariance issues, as recent questions have shown.  At a
design level, it's not easy to predict in advance the methods that we might
want to support for different representations, and nor does it seem "right"
to equip types with every possible behaviour at the time of their creation
(e.g. you clearly don't want to equip every type with every posssible
visualisation apparatus).  That is, it's often difficult to come up with a
classification hierarchy (and/or naming conventions for methods) that will
allow any sensible reuse.  It's not impossible, of course, it's just not
that easy.  

Personally, I think the apparent preference of OCaml users to stick with the
basic language may be telling - or have I misjudged this?   Comments
welcome!

> There is probably hardly any 
> program, where
> you wouldn't need such constructs. 

In summary, I think my point is that there are many programs (but not all -
I don't want to be dogmatic about this) where the application of class based
abstraction techniques is inappropriate.  For example, IMHO no compiler I've
seen would benefit, nor any symbolic computation system, nor any module in
the basic OCaml library apart from, perhaps, the Format library.  

> Imagine that all basic types were classes and all of them 
> would support
> a method e.g. "print".

All very well, but this is, after all, imaginary.  The runtime cost of
supporting a dictionary of methods for basic types is just too high (though
I guess Haskell manages it via type classes).

All of this is, of course, just my opinion!
Don Syme

P.S. Hey let's ensure this discussion continues on a low-key basis rather
than descending into a flame fest??  Thanks!! :-) 


------------------------------------------------------------------------
At the lab:                                     At home:
Microsoft Research Cambridge                    11 John St
St George House                                 CB1 1DT
Cambridge, CB2 3NH, UK
Ph: +44 (0) 1223 744797                         Ph: +44 (0) 1223 722244
http://research.microsoft.com/users/dsyme
email: dsyme@microsoft.com
   "You've been chosen as an extra in the movie
        adaptation of the sequel to your life"  -- Pavement, Shady Lane
------------------------------------------------------------------------




^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~1999-02-03 15:41 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
1999-02-01 12:48 Classes: when and where? (long) (was: looking for a nail) Don Syme
1999-02-02 14:42 ` Markus Mottl
  -- strict thread matches above, loose matches on Subject: below --
1999-01-29 18:32 Don Syme
1999-01-30  0:17 ` Markus Mottl

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).