Newbie question 
Author Message
 Newbie question

Quote:

> To make the circle round, are there Teco docs available to write a
> Teco emulator for Emacs? ;-)

Emacs already has a teco emulator, of course. I think it's a copy of a
copy of the DEC one, so it's probably not very like the ITS one.

--tim



Thu, 25 Oct 2001 03:00:00 GMT  
 Newbie question

Quote:

> The type declarations I have in mind were of a very different
> kind. The system I am working on is based on a complex,
> object-oriented information model. Functions, mostly generic
> functions, operate on instances of well-defined types from the
> model. Passing an instance of a non-conforming type is almost
> certainly an error. These errors are usually easy to fix when looking
> at the symptoms, but they are a major frustration when they slip
> through initial tests and make it into QA or beyond. What makes this
> so frustrating is that it would be so easy for a good compiler to
> detect. [Yes, I'm complaining about a vendor's implementation, but
> then again, you seem to be defending this weakness as perfectly
> acceptable. In that light, I don't think my response is inappropriate
> in this newsgroup.]

I'm not sure what you mean by `non-conforming' here.

If you mean something like calling a generic function with the wrong
signature, then it's reasonable to hope that an implementation might
complain about that. But I agree with Kent that you should ask your
vendor about this not complain here, and that it certainly should
*not* be required that an implementation detect this. It's hard enough
to get a CL working without putting extra fences in the way.

If, however, what you mean is calling a GF with an argument of a class
for which there is no method, then that is almost certainly *not*
detectable without extensions to the language.  Consider this
fragment:

    (defgeneric foo (x))

    (defmethod foo ((x frobly))
      ...)

    (defun blob (y)
      (declare (type fribly y)) ; FRIBLY is not a subtype of FROBLY
      (foo y))

Then I believe that this `error' can *not* be detected at compile
time.  It can't be detected because it's not an error: there could be
other methods defined on FOO almost anywhere.

In order to be able to detect this you have to be able to make some
kind of closed-world assumption about FOO, which CLOS doesn't let you
do.  This would typically be done by some kind of sealing declaration;
Dylan has mechanisms to do this.  It seems to me that you could also
get some similar kind of checking by extending the declarations that
you had in the GF:

    (defgeneric foo (x)
      (declare (type fribly-super x)))

Which is not portable CL but is allowed as an extension.

This is even more a case where you should be talking to your vendor.
Sealing declarations or just type declarations in GFs are both things
that could be provided as an extension to the language by a vendor if
there was enough demand.

I think sealing of this kind might be something interesting -- Dylan
has made a big thing of it after all -- but it's pretty hard to get
right I suspect, and I'd be really unwilling to see something
standardised without real implementations first, and I do not know of
any right now.

--tim



Thu, 25 Oct 2001 03:00:00 GMT  
 Newbie question
: >

: > >
: > > LISP's object-oriented paradigm is powerful and yet... It's essentially
: > > dynamic operator overloading, which is about as interesting to an object
: > > modeller as a stack of bricks.  Although this is certainly only an
: > > opinion and perhaps a trend, object orientation is centered on message
: > > passing, while LISP's object orientation is based on function calls
: > > (still).  I won't say its not USEFUL, but its not attractive, and its
: > > not a step forward unless you are writing "toy" applications.
: >
: > Sorry, but this doesn't make much sense to me, I must be missing your
: > point. Perhaps you should explain what you mean? CLOS generic
: > functions offer everything that C++ virtual methods, Java methods,
: > Eiffel features, etc. give you. What is it that makes generic
: > functions less useful than their more primitive counterparts in most
: > other OO languages?

: CLOS provides a world view where we have extended functions to overload
: based on the class of its operators.  The object abstraction of data and
: methods has been lost; CLOS methods are not methods, but functions, and
: CLOS does not protect encapsulated data from the "outside world" of
: functions and methods.  

: A "better" object world view would allow for only acessing fields in the
: object directly inside of methods for the object, thus providing the
: mystical "encapsulation" that is currently underpromoted in CLOS.  I
: hesitate to say that CLOS does not allow you to think in this way, yet I
: will assert that typical examples I have encountered make no effort to
: do so.

: The syntax, although it is often trivialized by many people, is also
: fairly important.  In a LISP function call, all arguments are weighted
: equally, and so you are led to think about specialising based on any or
: all of the parameters.  And object-oriented version, i.e. "(send-msg-to
: obj (do-something a b c d))" shows you which object is being operated
: upon, and although you could (easily) still specialize on the remaining
: variables (i.e. a b c and d) to call a different method, the
: message-passing paradigm is preserved.

Sometimes "which object is being operated upon?" is not a meaningful
question. In my experience, it's not a meaningful question in any code
which can be written in a "functional", side-effect-free style.  I'm
currently working on a rather extensive rewrite of CMUCL so that it
can be built cleanly, and fairly recently I was messing around inside
its type system, which treats types themselves as classes, and uses
methods and inheritance to implement operations like union-of-types
and intersection-of-types and is-a-subtype-of-b?

The type system of CMUCL is not written in CLOS (perhaps since CLOS
wasn't available when it was originally written, perhaps because CLOS
is currently implemented as a sort of inefficient afterthought in
CMUCL). Showing the actual code would obfuscate the issue for a number
of reasons, but I can illustrate what I'm talking about with some
CLOS-ish code along the same lines. (Beware that I've always found
CLOS's syntax somewhat non-intuitive and this project has kept me from
writing CLOS for months.) Consider

  (DEFCLASS TYPE () ..)
  (DEFCLASS NUMBER-TYPE (TYPE) ..)
  (DEFCLASS FLOAT-TYPE (NUMBER-TYPE) ..)
  (DEFCLASS SINGLE-FLOAT-TYPE (FLOAT-TYPE) ..) ; e.g. (SINGLE-FLOAT 0.0 1.0)
  (DEFCLASS MEMBER-TYPE (TYPE) ; e.g. (MEMBER :YES :NO :MAYBE)
    (MEMBERS))
  (DEFGENERIC TYPE-INTERSECTION ((X TYPE) (Y TYPE)) ..)
  (DEFGENERIC TYPE-UNION ((X TYPE) (Y TYPE)) ..)
  (DEFGENERIC TYPE-SUBTYPEP ((X TYPE) (Y TYPE))
  (DEFGENERIC OBJECT-HAS-TYPE? ((X T) (Y TYPE))

Which object is being operated on in TYPE-INTERSECTION or TYPE-UNION?
Which is better, overloading the first argument or the second?
Which of the methods below don't you like?

  (DEFMETHOD TYPE-SUBTYPEP ((X MEMBER-TYPE) (Y TYPE))
    (EVERY (LAMBDA (MEMBER) (TYPE-SUBTYPEP MEMBER Y)) (MEMBERS X)))
  (DEFMETHOD TYPE-INTERSECTION ((X TYPE) (Y MEMBER-TYPE))
    (MAKE-INSTANCE 'MEMBER-TYPE
                   :MEMBERS
                   (REMOVE-IF (LAMBDA (MEMBER)
                                (NOT (OBJECT-HAS-TYPE? MEMBER X)))
                              (MEMBERS X))))
  (DEFMETHOD TYPE-INTERSECTION ((X MEMBER-TYPE) (Y MEMBER-TYPE))
    ..)
  (DEFMETHOD TYPE-UNION ((X MEMBER-TYPE) (Y MEMBER-TYPE))
    ..)

When you have side effects, then often it does make sense to talk
about what's *the* object and what's not. For example, if we say

  (DEFGENERIC LEARN ((KB KNOWLEDGE-BASE) (F FACT) (TL TIME-LIMIT)))
  (DEFGENERIC UNLEARN ((KB KNOWLEDGE-BASE) (F FACT) (TL TIME-LIMIT)))

then the KNOWLEDGE-BASE is likely to be the object.  However, (1)
multiple inheritance can still be useful (consider different classes
of FACTs..) and (2) I fail to see what CLOS loses by using general
syntax.  Why is

  kb->learn(f,tl)

better than

  (LEARN KB F TL)?

I do see what I think are problems with CLOS, especially the large
number of levels of indirection it imposes on the implementation even
in the simplest cases. I can also understand other people who think
weak static type checking or lack of enforced information hiding
are problems. But criticizing CLOS for not imposing an asymmetric
"this argument is the object, the others are along for the ride"
semantics seems fairly silly.  CLOS evolved from systems which imposed
that asymmetry, it dropped that asymmetry in order to gain some very
useful generality (multiple inheritance), and (as above) it retains
the ability to describe algorithms which have that asymmetry. So I
just don't see the problem -- it honestly is a feature, not a bug.

(Others have pointed out that Bjarne Stroustrup himself has said that
multiple inheritance is very nice. I'll also point out that Scott
Meyers, in _Effective C++_ or _More Effective C++_, I forget which,
also spends a lot of time showing how to do some of this in C++.  You
write you could "easily" specialize on the other variables, but I
actually wouldn't characterize it as all that easy. It's obviously
doable, but it's also tedious and messy.)

  Bill Newman



Thu, 25 Oct 2001 03:00:00 GMT  
 Newbie question
: > Of course, a much more "simplistic" kind of type-checking can be
: > attained with CMU CL today... Not that I see most former C++ users
: > flocking to CMU CL instead of other CL implementations though...

: Others have mentioned CMU CL as well, I'll take a look at this.
: Unfortunately, it won't help in my current situation where we depend
: on third-party packages which are not available for CMU CL, not to
: mention a heavy investment in our own existing code.

I like Lisp a lot, and I have a lot of C and C++ programming
experience, and I have become quite fond of CMUCL's typing. I'm also
somewhat horrified by a few things about it, and also by some recent
changes to it, but that's another story -- by and large I like it and I
use it regularly. (and someday I hope its build process will be clean
enough that I can patch it without too much pain:-)

If you want to understand why Lispers consider C/C++ typing
simplistic, I'd recommend taking a look at something like ML.  Even if
you don't intend to use it (I don't myself) it'll broaden your mind..

: --




Thu, 25 Oct 2001 03:00:00 GMT  
 Newbie questions [Followup to comp.lang.lisp]



: >
: > > what about optional arguments with default values?
: >
: > It is still possible to do this a long those lines, but IMHO it gets a
: > bit messy:
: >
: > (defun myfun ((a double-float) &optional ((b double-float) 0.0d0) (x 0.1d0))
: >   (* a b x))
: >
: > The same might apply to &key args,

: Except that keywords already use this, as in:

:  ((lambda (&key ((:fu foo) 3 foo-p)) foo) :fu 4) => 4

: so you'd do

:  ( ... &key (((:b bee) integer) 3 b-p) ...)
: or
:  ( ... &key ((:b bee integer) 3 b-p) ... )
: or
:  &key (bee 3 b-p integer)
: but any way you cut it, it's messy... and somewhat inconsistent.

: Declaration syntax is less messy, IMO.

I personally hate typing names more than once, a la

  (DEFUN FOO (..) ..)
  (DECLAIM (FTYPE (..) FOO)).

because it ends up being one more maintenance headache when names
change. (Does anyone else change the names and calling conventions of
things a lot when they're setting up a program?)  For about two years
I've been using my own

  (DEFMACRO DEF-TYPED-FUN (FNN
                           (LL          ; lambda list
                            LLT         ; LL types (suitable for DECLAIM FTYPE)
                            &OPTIONAL
                            (RETURN-TYPE T)))

and also DEF-TYPED-VAR along the same lines, largely to avoid this.
(Why do I add so many declarations to programs when they're unstable
enough that they're likely to change?  I use CMUCL, which considers[1]
type declarations to be assertions, and as an old C++ programmer I
find this quite helpful -- I never got used to being blindsided by
mistakes that the compiler used to catch!)

  Bill Newman

[1] Except -- argh!! -- it sometimes doesn't anymore, since it's undergone
    a lot of maintenance and not everyone considers this guarantee as
    important (and brilliant) as I do.



Thu, 25 Oct 2001 03:00:00 GMT  
 Newbie questions [Followup to comp.lang.lisp]


: > > This is less problematic with mainline
: > > code which is likely to be run by the developer anyway, but typos in
: > > sections of the source code that are less frequently run have the
: > > habit of crashing in the hands of a user, or the QA department if
: > > you're lucky. Yes, you should test all your code, but the kind of bug
: > > we're talking about is often introduced by changes that are so
: > > 'obvious' that many developers don't imagine a bug may have been
: > > introduced.
: >
: > Again, I want to say: this is a good theoretical point, but do you
: > know of any evidence that it causes large Lisp systems to be less
: > robust than large C++ systems.  I know of none, but I have not looked
: > that hard.

: I think in fact just the opposite.  Speaking only anecdoctally here,
: it's assumed that type matching means things work.  I'm not so sure.
: It gives one almost a false sense of confidence:

: This code:

:  (defvar *foo* (- most-positive-fixnum 1))
:  (defun foo () (* *foo* 2))

: works fine undeclared in Lisp but in C the equivalent code, properly
: type declared, would do modular arithmetic.  The types would match
: but the effect would be wrong.  Now, in "properly type-declared code"
: you might see that the function was declared fixnum all over the place
: but that wouldn't make it right--it would just mean you were asking
: the compiler to trust you that the data was not going to be out of
: bounds, which isn't a good thing to trust in this case.  The CMU
: compiler actually will probalby put in a type-check to make sure
: that the declaraiton is not violated, but such type checks do cost
: and many people feel pressured not to have them.  Further, and this is
: the really insidious thing about type checks in practice, there is
: ENORMOUS pressure to turn
:  (defun foo (x) (+ x 2))
: into
:  (defun foo (x) (declare (fixnum x)) (+ x 2))
: to make it "more efficient" as if somehow the generic (+ x 2) was
: in fact less efficient.  (+ x 2) is maximally efficient when you don't
: know if x is going to be a fixnum or not.  Adding the declaration makes
: it more efficient ONLY IF you happen to know x will not be other than
: a fixnum; if you don't know that, it isn't "more efficient", but rather
: it is "broken".  The real problem with type declarations is not the
: mathematical proof techniques associated with them, it is the willingness
: to ignore or hand-wave away the very real societal tendancies of people
: to force people with access to type declarations to over-aggressively
: apply narrow type boundaries to problems, turning every program in the
: world into a metaphor for the Y2K bug becuase each such program has its
: own little time bomb in it waiting for data to become big enough to
: overflow and cause a problem.  To say that people don't overaggressively
: seek these little "shortcuts" (sacrificing the future for the present)
: is to deny that there is any cost to dealing with Y2K, and to somehow
: say that "good programmers would never make shortsighted decisions".

I think it depends somewhat on the problem domain. I've worked a lot on a
Lisp program to play the game of Go, a game played on a NxN board,
where N is traditionally 19 and the complexity of the game is
something {*filter*} (EXPTIME complete maybe?). There are a lot of values
which just can't get bigger than N+1 or NxN+1, and it's just never
going to overflow a machine word, sorry. There are large sections of
the code which would just work if you told the computer to just use
machine word arithmetic, and I put a considerable amount of time into
declarations which would let CMUCL prove to itself that everything was
OK without type checking, and by and large in retrospect that time was
not well spent, in that it didn't help me catch bugs and didn't give
me anything I wouldn't have gotten for free by just using machine
words.

This may not be a common situation, but it's probably not
pathologically uncommon either. Offhand I can't think of any other
problems which have complexity properties like this to give a
reasonably rigorous guarantee that we'll never work with a problem
size of 2^30 or even 2^16, but people working with code which iterates
over the contents of a network packet or the contents of a cipher
block probably have something of the same feeling.

  Bill Newman



Thu, 25 Oct 2001 03:00:00 GMT  
 Newbie questions [Followup to comp.lang.lisp]

Quote:

>   (DEFUN FOO (..) ..)
>   (DECLAIM (FTYPE (..) FOO)).

I recommend the DECLAIM precede, btw.  If the compiler doesn't know to
compile the DEFUN in the way you mention, the DECLAIM may not be able
to produce the effect after-the-fact.


Thu, 25 Oct 2001 03:00:00 GMT  
 Newbie questions [Followup to comp.lang.lisp]

Quote:

> I think it depends somewhat on the problem domain. I've worked a lot on a
> Lisp program to play the game of Go, a game played on a NxN board,
> where N is traditionally 19 and the complexity of the game is
> something {*filter*} (EXPTIME complete maybe?). There are a lot of values
> which just can't get bigger than N+1 or NxN+1, and it's just never
> going to overflow a machine word, sorry.

No need to apologize.  I didn't make the claim this wasn't so.
The whole point of declarations in the language are to accomodate
this.  The whole point of programmable access to the language is to make
sure you don't have to overwhelm your program with declarations.

Quote:
> There are large sections of
> the code which would just work if you told the computer to just use
> machine word arithmetic,

And the language provides the ability to do this several ways.
I'll give examples of two of them here.  Others might involve the
use of readmacros or code-walking or other such things.

[1]

Using no package surgery but different names:

(declaim (inline +&))
(defun +& (x y)
  (the fixnum (cl:+ (the fixnum x) (the fixnum y))))

(declaim (inline *&))
(defun *& (x y)
  (the fixnum (cl:* (the fixnum x) (the fixnum y))))
...etc.

----- OR -----

[2]

Using package surgery:

(defpackage "MY-STUFF"
  (:use "CL")
  (:shadow "+" "*" ...))

(defun + (&rest args)
  (the fixnum (apply #'cl:+ args)))

(define-compiler-macro + (&rest args)
  (case (length args)
    ((0) 0)
    ((1) `(the fixnum ,(car args)))
    (otherwise
         `(the fixnum (cl:+ (the fixnum ,(car  args))

...etc.

It's also easy in both of these to add a conditional check-type in
order to get type-checking and then to remove it when you're ready
to deploy.

##### N.B. I didn't test this code.

Quote:
> and I put a considerable amount of time into
> declarations which would let CMUCL prove to itself that everything was
> OK without type checking, and by and large in retrospect that time was
> not well spent, in that it didn't help me catch bugs and didn't give
> me anything I wouldn't have gotten for free by just using machine
> words.

I'm not 100% clear about what you're saying here, but would be
interested if you'd elaborate.  What do you mean "using machine
words"?

Quote:
> This may not be a common situation, but it's probably not
> pathologically uncommon either. Offhand I can't think of any other
> problems which have complexity properties like this to give a
> reasonably rigorous guarantee that we'll never work with a problem
> size of 2^30 or even 2^16, but people working with code which iterates
> over the contents of a network packet or the contents of a cipher
> block probably have something of the same feeling.

I don't really have a theory of this either.  NOTE: I am NOT saying
compilers shouldn't type-check what they can.  I think it's good for
them, too.  That's the kind of vendor I patronize personally.  However,
I think it's reasonable and appropriate for their to be other levels
of service that still call themselves common lisp.

I prefer a Conservative/Libertarian CL, not a Liberal/Socialist one.



Thu, 25 Oct 2001 03:00:00 GMT  
 Newbie questions [Followup to comp.lang.lisp]

[snippity-snip]

Quote:

> > I may be entirely
> > wrong, but I suspect that if CL incorporated a standard, succinct
> > syntax for associating types with symbols and checking the types of
> > objects then it would get used a lot more then the current, somewhat
> > verbose, ways of doing this is.

> I don't understand.  Declarations do this.  What's verbose is to
> repeat the type with every use.

Sure, declarations do it; but I'd like syntax that allows the
association between a symbol and its type to be declared at th the
point in the code that it (the symbol) was introduced. Having to
rename the symbol within a subsequent delcaration is (to my way of
thinking) somewhat cumbersome; as well as increasing the effort
required of anyone subsequently trying to understand the code.

There are lots of different forms with which it would be nice to be
able to do this: defun, lambda, let, let*, flet, labels,
destructuring-bind, multiple-value-bind, do, do*, dolist, loop....



Thu, 25 Oct 2001 03:00:00 GMT  
 Newbie questions [Followup to comp.lang.lisp]
...

Quote:

> If, however, what you mean is calling a GF with an argument of a class
> for which there is no method, then that is almost certainly *not*
> detectable without extensions to the language.
...
>     (defun blob (y)
>       (declare (type fribly y)) ; FRIBLY is not a subtype of FROBLY
>       (foo y))

> Then I believe that this `error' can *not* be detected at compile
> time.  It can't be detected because it's not an error: there could be
> other methods defined on FOO almost anywhere.

  Well it could be detected, in most cases, at compile time without any
  language extensions.  If "compile time" meant compiling the WHOLE program
  all at once.   However, to get this sort of omniscient knowledge the
  analysis has to be omnipresent.   [ Even whole program analysis cannot help
  you with classes and methods that are defined dynamically as the program
  runs. Of course, it is rather "bad form" to make static references
  to what will be dynamically generated. ]

  It is a tradeoff. CLOS can allow one to incrementally refine
  a program while it running.  C++ can have glacially slow compile
  times, but you will have type checked the heck out of you program.
  [ I think vendors could provide "automated inspection" that
    are akin to the "tree shakers" some provide.  At some point you  
    can say "all of these files (or functions)are suppose to be a coherent
    system; go confirm this".  I wouldn't expect that to finish in a rapid
    fashion.  But as a periodic sanity check before passing code
    off to QA it probably would work well.  Or run it overnight when
    you go home. :-) ]

  Lisp does not absolutely require that you compose all of your
  definitions for the convenience of the compiler.  If the
  equivalent of gcc's -Wall -pedantic flags were available in
  some cases they might generate more false positives than
  useful information when run over a incremental piece of a "program".

Quote:
> I think sealing of this kind might be something interesting -- Dylan
> has made a big thing of it after all -- but it's pretty hard to get
> right I suspect,

   For both the implementor and the users. :-)   In some sense sealing is
   a mechanism for turning off dynamism where you don't need it.  Partially,
   turning it off in some places but not everywhere can be tricky.

   Even Dylan doesn't guarantee to catch all sealing violations at
   compile time.  [ Again dynamic class and method creation/additions involve
   dynamic checks to see that the closed worlds assumptions are not
   violated.]  However, an aggressive Dylan implementation can catch many
   "static" violations.

----

Lyman



Thu, 25 Oct 2001 03:00:00 GMT  
 Newbie questions [Followup to comp.lang.lisp]
I've been using OO languages for onwards of 15 years now (mainly C++ and
Smalltalk, but also having some FLAVORS and CLOS experience).  There are a
good many problems where sending a message to a single object does not make
sense.

Look at the double dispatch issue in arithmetic implementations in Smalltalk
and for arithmetic objects in C++.  Look at displaying different shape
objects on different devices.  Each requires a level of complexity when you
code them in a message passing style, most of which has to do with the fact
that you need to dispatch n*m cases through an additional n (or m) methods
on one or the other classes.

With multimethods, you can dispatch the m*n cases directly (and quite often
get code reuse by sharing base cases).  It does simplify things...

faa

Quote:


>> Sometimes "which object is being operated upon?" is not a meaningful
>> question. In my experience, it's not a meaningful question in any code
>> which can be written in a "functional", side-effect-free style.

>OK, I see your point here, but you yourself dictate it as a "functional"
>style, not an object-oriented one.  The case you are referring to is one
>where you have written a "function" that takes some arguments, does some
>processing, and returns value with no side effects.  This is a useful
>functionality to have, but is object-oriented?

>> Which object is being operated on in TYPE-INTERSECTION or TYPE-UNION?

>Both are; these functions should be implemented as functions, not as
>methods on an object.

>> Which is better, overloading the first argument or the second?

>Overload neither; objects and classes are self-describing, so you can
>easily write in a cond or case statement to perform special operations
>in these cases.

>> Which of the methods below don't you like?

>>   (DEFMETHOD TYPE-SUBTYPEP ((X MEMBER-TYPE) (Y TYPE))
>>     (EVERY (LAMBDA (MEMBER) (TYPE-SUBTYPEP MEMBER Y)) (MEMBERS X)))
>>   (DEFMETHOD TYPE-INTERSECTION ((X TYPE) (Y MEMBER-TYPE))
>>     (MAKE-INSTANCE 'MEMBER-TYPE
>>                    :MEMBERS
>>                    (REMOVE-IF (LAMBDA (MEMBER)
>>                                 (NOT (OBJECT-HAS-TYPE? MEMBER X)))
>>                               (MEMBERS X))))
>>   (DEFMETHOD TYPE-INTERSECTION ((X MEMBER-TYPE) (Y MEMBER-TYPE))
>>     ..)
>>   (DEFMETHOD TYPE-UNION ((X MEMBER-TYPE) (Y MEMBER-TYPE))
>>     ..)

>These are all poor methods, they are in every way functions.

>> When you have side effects, then often it does make sense to talk
>> about what's *the* object and what's not. For example, if we say

>>   (DEFGENERIC LEARN ((KB KNOWLEDGE-BASE) (F FACT) (TL TIME-LIMIT)))
>>   (DEFGENERIC UNLEARN ((KB KNOWLEDGE-BASE) (F FACT) (TL TIME-LIMIT)))

>And this is a case where you are working with object-orientation because
>you are using a code/data abstraction.  The previous examples took
>several objects, and probably would have used methods to get the
>information they needed from it, while these specifically operate upon
>one object.

>> then the KNOWLEDGE-BASE is likely to be the object.  However, (1)
>> multiple inheritance can still be useful (consider different classes
>> of FACTs..) and (2) I fail to see what CLOS loses by using general
>> syntax.  Why is

>>   kb->learn(f,tl)

>> better than

>>   (LEARN KB F TL)?

>Because (LEARN KB F TL) rapidly loses its meaning when you are not
>careful.  I do not know the intimates of CLOS, but say for example that
>I write a method like:

>(defmethod learn (kb (f my-fact) tl) ... )

>This method specialises on the second object, but not the first.  While
>initially this seems like it may be a feature because we can now
>intercept any calls to "learn" our special type of fact.  On the other
>hand, (LEARN KB F TL) is no longer equivalent to KB->(LEARN F TL), and
>maybe instead decides to have side effects on F instead of KB (we don't
>know, and there is no way to find out) while still using the same
>generic function.  The other approach means that we do know, given an
>object's API, whether it is that object or another that is most likely
>to be operated on.  Rather than an inplementation or flexibility issue,
>it is about readability and psychology.

>> I do see what I think are problems with CLOS, especially the large
>> number of levels of indirection it imposes on the implementation even
>> in the simplest cases. I can also understand other people who think
>> weak static type checking or lack of enforced information hiding
>> are problems. But criticizing CLOS for not imposing an asymmetric
>> "this argument is the object, the others are along for the ride"
>> semantics seems fairly silly.

>Perhaps my criticism there was tagged on for the ride, in response to
>someone's trivialisation of it.  It is my personal belief that syntax
>has more power over a programmer than anything else in a language, at
>many levels.  Syntax decides how a programmer will use things,
>regardless of how they CAN be used.  A syntax that places emphasis on a
>particular object means that programmers will write in a way that uses
>one object.  A syntax that places no emphasis leads to programs that use
>any number of the objects, possibly in any number of ways.  The failure
>to hide data means that programmers feel free to modify the data from
>anywhere in their code, regardless of good practices etc.

>> CLOS evolved from systems which imposed
>> that asymmetry, it dropped that asymmetry in order to gain some very
>> useful generality (multiple inheritance), and (as above) it retains
>> the ability to describe algorithms which have that asymmetry. So I
>> just don't see the problem -- it honestly is a feature, not a bug.

>I fail to see how multiple inheritance is affected by the syntax in this
>case.. perhaps I use the word in a different way than you do?

>> (Others have pointed out that Bjarne Stroustrup himself has said that
>> multiple inheritance is very nice. I'll also point out that Scott
>> Meyers, in _Effective C++_ or _More Effective C++_, I forget which,
>> also spends a lot of time showing how to do some of this in C++.  You
>> write you could "easily" specialize on the other variables, but I
>> actually wouldn't characterize it as all that easy. It's obviously
>> doable, but it's also tedious and messy.)

>You would specialise on the other variables exactly as CLOS does, I
>don't see how it could possibly be any more messy than that.  You would
>not even require a code change.

>Another message has pointed out about 10 lines of code that change
>functional syntax into message-passing syntax.  Its easy, and so are
>other changes to make CLOS more object-oriented-ish.  The issue is not
>difficulty of implementation, the difficulty is that its not in th
>standard, and nobody is going to implement it unless there is something
>driving them to do so.  With the reputation of LISP as it is, nobody who
>would complain about these things (except maybe me, for some reason)
>even bothers with LISP, so I suspect word never reaches the ears of the
>LISP publishers and standard-setters.

>CU
>Dobes



Thu, 25 Oct 2001 03:00:00 GMT  
 
 [ 350 post ]  Go to page: [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24]

 Relevant Pages 
 

 
Powered by phpBB® Forum Software