Re: Semantics and STL/DTL
From: Thomas Gagne (tgagne_at_wide-open-west.com)
Date: 07/14/04
- Next message: TLOlczyk: "Re: stupid error doing my head it! (C programming)"
- Previous message: Ian Upright: "Re: Static vs. Dynamic typing (big advantage or not)---WAS: c.programming: OOP"
- In reply to: Miguel Oliveira e Silva: "Re: Static vs. Dynamic typing"
- Next in thread: Denis Johnson: "Re: Semantics and STL/DTL"
- Reply: Denis Johnson: "Re: Semantics and STL/DTL"
- Maybe reply: Thomas Gagne: "Re: Semantics and STL/DTL"
- Maybe reply: Miguel Oliveira e Silva: "Re: Semantics and STL/DTL"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Date: Wed, 14 Jul 2004 01:37:08 -0400
Miguel's posting below makes some good points and they've been bothering me
all day. I think the reason is the semantics described sound familiar, sound
reasonable, but shouldn't really apply to OOP.
One of the design goals of OOA is cohesive and loosely coupled modules. From
<http://c2.com/cgi/wiki?CouplingAndCohesion>:
"Cohesion : qualitative measure of dependency of items within a module (from
worse to better, high cohesion is good)
...
"Coupling : qualitative measure of module interdependence/communication (from
worse to better, high coupling is bad)"
The page goes on to describe some of the qualities of highly cohesive modules
as well as the qualities of coupling, and advocates that the least amount of
inter-module dependencies is better than higher-dependency modules.
The reply below starts first with an example I'm unsure should be explored
because it demonstrates too many attributes of poor OO design. The meaning of
the symbol 2 when it appears in code is discouraged even in procedural
languages because the programmer can't infer any meaning from it. Is it a
constant? Does it have meaning? What good is it as a function parameter if I
don't know what the parameter's purpose is? It's even worse in OO code
because the idiom for OO is to create types that hide their implementations
but expose their API. The API defines the allowable operations, but how those
operations modify state, and how that state is described, is intentionally
hidden in objects. These premises are mostly unanimously accepted by
programmers using both procedural and OO languages and are maintained as a
catechism in the relevant literature.
The second example is the more interesting one, but for different reasons is
just as poor an example of OO programming as the first, but for more subtle
reasons.
The scenario is of a method, presumably contained inside some object (it could
be either an instance or static method) that expects one of its parameters to
be a stack-like object. This expectation is necessary because the receiver's
method will send its parameter push() and to avoid compile or runtime errors
we need a degree of confidence the parameter object implements push().
Because push() (and presumably pop()) are popular (exposed) behaviors of a
stack-like object the receiver places a requirement on the parameter, and that
is that it implement these methods.
Seems innocent enough, but to varying degrees *any* expectations or
requirements of the parameter that exceed the most primitive measure of
fitness (will it respond to push()?) accumulate into ever more increased
coupling of the receiver and its sender.
In STLs, this increased coupling is described as the parameter's type. If
that type is a classification, then it must be a descendant of some class the
receiver's confident implements the protocol it intends to invoke on the
parameter. If it knows the base class Stack implements push() and pop(), or
requires its subclasses to implement them, then the receiver's method may
specify the parameter as being of type Stack. In most (all?) OO languages any
subclass of Stack passes this test. The problem with this restriction is it
enforces a hierarchy on the consumers and a burden on the consumer-programmer
to into a narrow band of specializations as descendants of Stack. Unless the
method uses the entire Stack API the requirement is overstated, and the
utility of the method is limited to only those objects that do (or pretend to)
implement the entire suite of Stack API.
Somewhat less burdensome, but only trivially so, are a recent invention called
Interfaces, that define the type of an object not but what it is or where it
comes from, but strictly by its API. Suspected by some to be an invention to
give objects faux multiple inheritance, its popular use is as a typing
mechanism to specify a lesser hurdle for objects to pass a typing inspection
by declaring them to implement a less rigorous API. This allows objects
outside of the class hierarchies described above (a class type) to pass the
compiler's test (as specified by the programmer).
Requiring any object to implement more than it needs, or to advertise that it
does, is an accepted (nay--embraced) de rigeur for STL practitioners. But any
imposition on other objects to provide (or pretend) more behavior than they
require violates the spirit of loose coupling.
Consider the static typing requirements between just two modules. Whether its
class or interface membership the requirements (euph. contract) define a
coupling between the objects. Every behavior implicit in that requirement
increases coupling. Multiply that coupling by all the possible pairings and a
program becomes a web of coupling graphs.
Managing those dependencies has become an industry onto itself, creating
languages that infer dependencies, hide them, introduce new language features
(interfaces), or produce integrated development environments with enough bulk
and complexity of their own to rival operating systems.
Dynamically typed languages don't have a mechanism (or if they do, it's
optional) to tightly couple modules together. There are no dependencies
between them except that which is the bare minimum -- that objects respond to
the messages sent them at runtime.
I might develop this idea further elsewhere, but it suddenly struck me as odd
that OOA strives for loosely couple modules, but static typing systems strive
(ever more so with each passing upgrade or new language) for more coupling.
Considering how infrequently software is compiled throughout its lifetime and
how its maturation is far more enduring, STLs may disproportionately favor the
programmer at the expense of the program.
Miguel Oliveira e Silva wrote:
<snip>
>
>
> A lot has been said in this thread about *syntactic* type errors (either static
> or dynamic), generated when a message is sent to an object which does
> not recognize its signature (name, number and type of arguments).
>
> However, I would like to call your attention to *semantic* type errors,
> such as the ones Kazakov is talking about.
>
> Syntactic type correctness is a necessary condition for semantic type
> correctness, but not sufficient.
>
> Lets take for example an object containing the 2 value.
> What does it mean?
>
> It may be an integer number; a float number; a string;
> an integer number representing a year or the number
> of tyres of a bicycle; or many, many other "things".
> In itself, it mean nothing.
>
> However, those possible meanings are not (in general)
> mixable (it makes no semantic sense to use a "thing"
> representing the number of tyres of a bicycle, where a
> year was expected, even if by coincidence both share
> the same integer number representation).
>
> Also, just because they both are integer numbers, not
> all integer operations make sense on them. Unlike years,
> it's absurd to subtract a number bigger than 2 to the
> number of tyres of a bicycle (it wouldn't make any
> physical sense, thus break the bicycle's invariant).
>
> This is an excellent reason for imposing *explicit*
> subtype relations (as happens in static typed OO
> languages).
>
> (BTW, some functional languages make a different,
> and in this sense opposite, approach by relying on
> structural equivalence.)
>
> Another example is the mentioned type with a "push"
> operation. Again, without semantic behavior attached
> to this operation (using contracts for example), almost
> nothing can be said about this type. Does it make
> sense to allow an instance of this type to be used
> where a Stack object is expected? Should it be
> reused as a Door?
>
> Of course not to neither of them, if nothing more
> is known about this type with a "push" operation!
>
> ADTs are not simply types defined by the external
> operation applicable to them, but also (and more
> importantly) by the semantics of those operations
> (either in their individual use [pre/postconditions],
> or how they are allowed to change the object
> [invariants]).
>
> So, type relations exist not simply because of structural
> (syntactic) similarity, but because there is a explicit
> semantic choice made by the programmer, in order
> to prevent the application of sometimes foolish
> operations, regarding the type's semantics, such
> as using a number of tyres values where a year is
> expected.
>
> Of course this choice has costs. One cannot, in the
> current state of the art (and most likely for many years
> to come), automatically check for semantic similarities
> between types, neither can we currently statically (at compile
> time) ensure a complete semantic substitutability when
> an instance of a different type (subtype) is used where
> another type is expected. In static typed OO languages
> at most one can ensure that those types are related by
> an appropriate subtype relation (which is very good).
>
> Eiffel in this respect, is the language that better approaches
> this ideal goal (but it's not yet there).
>
> Getting back to the dynamic versus static type systems,
> I disagree that dynamic type systems are better because
> they are more flexible. If most syntactic type errors are
> more difficult to detect in dynamic type systems,
> semantic one are ever trickier.
>
> One should not forget that, quoting E.W.Dijkstra:
>
> "Program testing can convincingly show the presence of bugs
> but it is hopelessly inadequate to show their absence."
>
> BTW, I follow the view of Morris [1], which basically states
> that types are *not* sets of values (I disagree with Kazakov here),
> but they represent a more elaborate abstraction (again, following
> the excellent Meyer's school of thought: in OO they are ADT),
> in which type relations are not casual or structural
> coincidences, but explicit programming choices (by a
> subtype relation in the OO world).
>
> [1] "Types are not sets", James H. Morris, Jr., 1973, ACM
> http://doi.acm.org/10.1145/512927.512938
>
> Best regards,
>
> -miguel
>
> --
> Miguel Oliveira e Silva email: mos@det.ua.pt
> Dep. de Electrónica e Telecomunicações / IEETA
> Universidade de Aveiro - PORTUGAL
> phone: +351 234 370375 fax: +351 234 370545
> www: http://www.ieeta.pt/~mos
>
>
- Next message: TLOlczyk: "Re: stupid error doing my head it! (C programming)"
- Previous message: Ian Upright: "Re: Static vs. Dynamic typing (big advantage or not)---WAS: c.programming: OOP"
- In reply to: Miguel Oliveira e Silva: "Re: Static vs. Dynamic typing"
- Next in thread: Denis Johnson: "Re: Semantics and STL/DTL"
- Reply: Denis Johnson: "Re: Semantics and STL/DTL"
- Maybe reply: Thomas Gagne: "Re: Semantics and STL/DTL"
- Maybe reply: Miguel Oliveira e Silva: "Re: Semantics and STL/DTL"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Relevant Pages
|