Robert Kiendl <rkie...@gmx.net> writes: > i am pretty new to the lisp community. someone told me that lisp is the > language that provides quite every structural concept found in any other > language. also Paul Grahams introductory comments in "ANSI Common Lisp" > can get you delighted. > as far as i got insight to lisp up to now, it really seems to satisfy > many promises. especially in fields of versatile operator programing > (the term "functional programing" misleads in my opinion because it is > associated with restrictive mathematical mappings), dynamic structures > and types.
welcome aboard ;-)
> though i quickly can find fifty items where lisp is (much) more > "eloquent" and productive like for example c++, there remain few but > heavy scruples.
> Besides things like
> - odd source code reading, which is a point of habit (i hope)
It is, and it is made easier by a very uniform way of indentation among experienced Lisp programmers. Much of the homework code posted here is simply not readable until formatted in the standard way.
> - low speed, which is not so heavy weight with modern compilers and > c++-interfaces (for algorithms at the low level layer of the program)
CL can be written to be very fast but the style in which you do it is different. First you write your program as clearly as possible paying attention to choice of algorithms, space/time trade offs etc and then, *if* your application is too slow you can profile it and optimize the hot spots by adding declarations or other means. There have been cases cited on this group where a CL implementation tuned like this was faster than a C/C++ implementation it replaced. Especially since you cite MFC later on, MFC is many things but a speed daemon it is certainly not ;-)
> - poor encapsulation formalism with respect to mind relieving > OO-thinking (automatic object context, private/protected, > overloading mechanisms ...), which is partially weight up by dynamic > features and to the other part is (probably) an inevitable tribute > to the versatile operator approach
Again, I think you're approaching this from the wrong angle. Yes, there are certain features (::, SLOT-VALUE) that can be used to violate encapsulation, but nobody is forcing you to use them. All access to slots can be done by accessors which are an alternative mean to encapsulation. private/... can be done by selectively exporting in different packages. (Not that you should try to write C++ in CLOS syntax) If you worry that some coworker might use these features to break your classes, the same can be done in C++
#define private public #include "class.h"
[Note to C++ language lawyers, I know about the One Definition Rule but this has a fair chance of working in practice]
The answer to such concerns is education and code review. No useful language can shield you from such tricks.
> there seem (to me) some "real" lacks. They mostly have to do with so > called missing "destruction automization" and the "hiding of the pointer > !type!"). The problem seems also to overlap with the coercion to the > "garbage collecting".
In a way garbage collection is destruction automation. Theoretically finalization is missing, although I wonder how useful it is in practice. If you want to control stuff when an object becomes unreachable, timing is usually also important. You don't want to wait with closing file descriptors until your application exits, you want to do it fairly soon after a stream object becomes unreachable.
And looked at in another way, everything in CL is a pointer.
-- Lieven Marchand <m...@bewoner.dma.be> If there are aliens, they play Go. -- Lasker
>> - odd source code reading, which is a point of habit (i hope)
>It is, and it is made easier by a very uniform way of indentation >among experienced Lisp programmers. Much of the homework code posted >here is simply not readable until formatted in the standard way.
How do I get code formatted in a standard way? I have an editor that'll format a block of code on command, but I often view a file, only to find that it's not formatted to my tastes. I've read a little about Water's XP and chapter 22 of the HyperSpec concerning the Pretty Printer, it's too fine grained. I'm seeking a way to format a file or list of files according to one of a small set of pre-defined styles. Emacs probably does this, but I don't use emacs much; how would this be done in emacs?
> In a way garbage collection is destruction automation. Theoretically > finalization is missing, although I wonder how useful it is in > practice. If you want to control stuff when an object becomes > unreachable, timing is usually also important. You don't want to wait > with closing file descriptors until your application exits, you want > to do it fairly soon after a stream object becomes unreachable.
I find finalization very useful for some UI objects like fonts pens brushes OpenGL display lists etc.. When they are collected you have to free the underlying OS object also. It's generally not worthwhile to bother to release them as soon as possible.
> i am pretty new to the lisp community. someone told me that lisp is the > language that provides quite every structural concept found in any other > language. also Paul Grahams introductory comments in "ANSI Common Lisp" > can get you delighted. > as far as i got insight to lisp up to now, it really seems to satisfy > many promises. especially in fields of versatile operator programing > (the term "functional programing" misleads in my opinion because it is > associated with restrictive mathematical mappings), dynamic structures > and types.
> though i quickly can find fifty items where lisp is (much) more > "eloquent" and productive like for example c++, there remain few but > heavy scruples.
> Besides things like
> - odd source code reading, which is a point of habit (i hope)
you get used to it. i am a lisp newbie. now i find both lisp and C to be weird looking.
> - low speed, which is not so heavy weight with modern compilers and > c++-interfaces (for algorithms at the low level layer of the > program)
the speed isn't as low as you seem to think. good lisp compilers exist. lisp may add some overhead compared to C, but it won't suddenly turn O(n) algorithms into O(n^2) algorithms. you never know, sometimes the lisp solution can be faster since you have a more simple and direct way to express it.
> - poor encapsulation formalism with respect to mind relieving > OO-thinking (automatic object context, private/protected, > overloading mechanisms ...), which is partially weight up by dynamic > features and to the other part is (probably) an inevitable tribute > to the versatile operator approach
private/protected is imho overrated. it just gets in your way without helping much. (if i could protect data in a C++ from getting nuked by a stray pointer, *then* maybe i could see the point...)
> there seem (to me) some "real" lacks. They mostly have to do with so > called missing "destruction automization" and the "hiding of the pointer > !type!"). The problem seems also to overlap with the coercion to the > "garbage collecting". > Apart from simple things like e.g. a automatic CHideCursor"-object > (M...soft/MFC) i'll give an example deeper > associated with expressiveness of a computer language.
> In a simulation software i wrote (c++), there exist various objects (for > example MInfoAtom), where from many locations in the program (other > dynamic objects, globals, ...) is pointed to. Sometimes there is reason > the destroy those pointed-to-objects. the locations which point to > should be informed (for example - to set the pointers to 0). Its very > hard to book-keep those widespread non-uniform connections. therefore in > c++ i created a "reflecting pointer class template" (PR<class T>). > the "PR-pointable-to" classes incorporate a simple uniform > Interface-class IPR ("class MInfoAtom: public other_base_classes, public > IPR"). > If I want to define a reflective pointer i just write "PR<MInfoAtom> > mylink" instead of "MInfoAtom* mylink". the PR-Pointer class behaves > nearly like a normal pointer, i do not have to worry about during > writing code, i can also mix it with other pointers. I also use that new > pointers heavly inside of other template-containers (lists, maps, > queues, index-containers ...). they savely get informed by the mechanism > in the IPR-destructor interacting with PR<> when i make "delete myobj" > from anywhere. i also use complexer pointertypes like a PRH<class T> > which incorporates a share-holder concept. I use them all mostly like > normal pointers. The additional pointer properties are determined a the > definition location. > The software is "contaminated" with those "automatic links" and they > really do a good and efficient job preventing me from > getting jumbled in the complex connection structure of the program. Also > the robustness of the software gained very much from that concept.
> I can hardly imagine to rewrite such software not having this automatic > link concept at hand.
sure. lisp keeps track of all this for you. basically, you just stop refering to something and it'll get reaped by the collector. this is an issue that the lisp vendor takes care of for you in their implementation of the garbage collector.
if you do make some huge data structures and need to unload them, it can be tricky to find all the referents, however. i think this is what you were trying to get at in your question. i am sure that there are tools to find what is using memory. perhaps someone with better lisp knowledge/experience can comment on how to track things down.
> With my lisp knowledge i do not see a handsome way to realize that > concept in lisp. I hope someone out there can tell me that its possible > anyhow.
*you* don't need to realize it all. just lay back and accept it! ;-)
> There are some other scruples rooted mainly in the hiding of the pointer > type in lisp. but if someone can tell me a way to realize those > automatic links those scruples may also disapear. Bjarne Stroustroup > said, that it is dangerous to restrict language means just for the > intention of preventing from bugs (this prevention comes better from the > bottom up way: restrict where you like it). This is the reason for > example why JAVA is not a universal language suited for really complex > structured programs, though many poeple fascinated by the great network > and platform independent gui features think that. > My summarizing question is: Does the hiding of the pointer type and > garbage collection in the bottom up lisp language throw a shadow on > the space of expressiveness (analog to an area of disclosure of > computability in the sense of Goedel :-)) which comes apparent even > on the high level like for example in the above mentioned problem? > Or is it possible to enlight those area from the backdoor?
in my (limited) experience with lisp, low level cruft just doesn't come up as an issue. i can do what i like. i don't worry about memory allocation. i have a hard enough time trying to phrase things properly. i find lisp to be totally backwards when coming from languages such as C and fortran. (i don't mean lisp is wrong, it's just different. i get the feeling of standing on my head since i have spent many years hard wiring myself into C/C++.)
> Or: Lisp is great on managing symbols and semantics of symbols. This > includes symbols which other languages have in the "code segment". But > is Lisp also great on connections (which do not suit in the hierarchy > paradigm)?
* Robert Kiendl <rkie...@gmx.net> | i am pretty new to the lisp community. : | there seem (to me) some "real" lacks.
which other disciplines do you consider yourself sufficiently well versed in to judge while you admit to being pretty new to them? would you, say, walk into a hospital and tell the staff how to run the place? would you take over the operating theater because you had seen a better way to do it on Chicago Hope? do you, say, take the control of boats or planes?
or would you perhaps try to learn first instead of judge first in other disciplines?
I think the reason people find "lacks" with Lisp is that they actually expect everything in computing to be so simple they can understand it right away, since that's how things work in the mass market segments, where anyone with above average IQ is ahead of the "user-friendly" baby talk. Lisp, however, is not optimized for newbies and well-below-average mass market consumers. Lisp has its own traditions, far removed from the newbiedom that Microsoft, et al, have monopolized and capitalized on.
my advice to those who are used to understanding much more than the user friendly crap intended them to do and thus acquire a haughtiness and arrogance towards new fields in computing that they might think are as stupidly designed as the ones they legitimately know better than, is to expect something different from what _looks_ different from what they're used to and to seize the opportunity to learn from the different rather than, effectively, to dumb it down to whatever they already understand much better than the designers.
(if you felt insulted by the above, please give up Lisp, too.)
#:Erik -- @1999-07-22T00:37:33Z -- pi billion seconds since the turn of the century
i am pretty new to the lisp community. someone told me that lisp is the language that provides quite every structural concept found in any other language. also Paul Grahams introductory comments in "ANSI Common Lisp" can get you delighted. as far as i got insight to lisp up to now, it really seems to satisfy many promises. especially in fields of versatile operator programing (the term "functional programing" misleads in my opinion because it is associated with restrictive mathematical mappings), dynamic structures and types.
though i quickly can find fifty items where lisp is (much) more "eloquent" and productive like for example c++, there remain few but heavy scruples.
Besides things like
- odd source code reading, which is a point of habit (i hope) - low speed, which is not so heavy weight with modern compilers and c++-interfaces (for algorithms at the low level layer of the program) - poor encapsulation formalism with respect to mind relieving OO-thinking (automatic object context, private/protected, overloading mechanisms ...), which is partially weight up by dynamic features and to the other part is (probably) an inevitable tribute to the versatile operator approach
there seem (to me) some "real" lacks. They mostly have to do with so called missing "destruction automization" and the "hiding of the pointer !type!"). The problem seems also to overlap with the coercion to the "garbage collecting". Apart from simple things like e.g. a automatic CHideCursor"-object (M...soft/MFC) i'll give an example deeper associated with expressiveness of a computer language.
In a simulation software i wrote (c++), there exist various objects (for example MInfoAtom), where from many locations in the program (other dynamic objects, globals, ...) is pointed to. Sometimes there is reason the destroy those pointed-to-objects. the locations which point to should be informed (for example - to set the pointers to 0). Its very hard to book-keep those widespread non-uniform connections. therefore in c++ i created a "reflecting pointer class template" (PR<class T>). the "PR-pointable-to" classes incorporate a simple uniform Interface-class IPR ("class MInfoAtom: public other_base_classes, public IPR"). If I want to define a reflective pointer i just write "PR<MInfoAtom> mylink" instead of "MInfoAtom* mylink". the PR-Pointer class behaves nearly like a normal pointer, i do not have to worry about during writing code, i can also mix it with other pointers. I also use that new pointers heavly inside of other template-containers (lists, maps, queues, index-containers ...). they savely get informed by the mechanism in the IPR-destructor interacting with PR<> when i make "delete myobj" from anywhere. i also use complexer pointertypes like a PRH<class T> which incorporates a share-holder concept. I use them all mostly like normal pointers. The additional pointer properties are determined a the definition location. The software is "contaminated" with those "automatic links" and they really do a good and efficient job preventing me from getting jumbled in the complex connection structure of the program. Also the robustness of the software gained very much from that concept.
I can hardly imagine to rewrite such software not having this automatic link concept at hand.
With my lisp knowledge i do not see a handsome way to realize that concept in lisp. I hope someone out there can tell me that its possible anyhow.
There are some other scruples rooted mainly in the hiding of the pointer type in lisp. but if someone can tell me a way to realize those automatic links those scruples may also disapear. Bjarne Stroustroup said, that it is dangerous to restrict language means just for the intention of preventing from bugs (this prevention comes better from the bottom up way: restrict where you like it). This is the reason for example why JAVA is not a universal language suited for really complex structured programs, though many poeple fascinated by the great network and platform independent gui features think that.
My summarizing question is: Does the hiding of the pointer type and garbage collection in the bottom up lisp language throw a shadow on the space of expressiveness (analog to an area of disclosure of computability in the sense of Goedel :-)) which comes apparent even on the high level like for example in the above mentioned problem? Or is it possible to enlight those area from the backdoor?
Or: Lisp is great on managing symbols and semantics of symbols. This includes symbols which other languages have in the "code segment". But is Lisp also great on connections (which do not suit in the hierarchy paradigm)?
On Tue, 18 May 1999 13:02:58 +0200, Robert Kiendl <rkie...@gmx.net> wrote:
...
> In a simulation software i wrote (c++), there exist various objects (for > example MInfoAtom), where from many locations in the program (other > dynamic objects, globals, ...) is pointed to. Sometimes there is reason > the destroy those pointed-to-objects. the locations which point to > should be informed (for example - to set the pointers to 0).
...
Perhaps you want something like weak pointers? Many GC'ed languages have a notion of weak pointers. Basically, these pointers are not traversed for the purposes of deciding what to retain when GCing. The result is that if you throw away all normal references to an object, the object will be GC'ed despite any weak references, and the weak references will be replaced by NIL or somesuch value.
So one of your data structures might be modelled as, say, a "weak table" containing your deleteable objects. Then when the responsible part of the program deletes an object (e.g. by removing it from a normal table, leaving no other references), the weak table will be updated by the GC to contain NIL.
Search your Lisp vendor's documentation for "weak".
* Robert Kiendl wrote: > I can hardly imagine to rewrite such software not having this automatic > link concept at hand. > With my lisp knowledge i do not see a handsome way to realize that > concept in lisp. I hope someone out there can tell me that its possible > anyhow.
I think what you are after is an object which keeps track of all the objects that reference it, so that when you `destroy' the object, it can tell all the people that refer to it that it has been destroyed so (in C++) you don't end up with dangling pointers.
Well, in Lisp you never destroy an object or end up with dangling pointers as a result of that, but things along these lines are still fairly useful, so you can at some point say `I want this to be GCable now'.
I can see several ways of doing this in Common Lisp, of slightly varying amounts of portability.
The simplest translation of your technique is to write a class which deals with dependencies in this way, and then mix this in to all the classes you are interested in. Then the `I want to make this GCable' operation on this object would end up doing something like:
(map-dependents #'(lambda (d) (remove-reference d o)) o)
For this kind of thing it's quite nice if dependents are `weak' in some way, so that an object doesn't end up holding onto references to all sorts of transient things. There is a dependency maintenance protocol in the Art of the Metaobject Protocol book, and I have a version of that somewhere if anyone is interested.
Another, slightly clunky, approach is to have the interesting object be one extra indirection away, so you can then nullify that indirection. This is kind of steam weak-pointers -- the people referencing the object still end up pointing to something, but that something is hopefully a bunch cheaper than the original object (for instance, a fixnum).
I often do this with hashtables (I've just typed this in, please don't take it too seriously):
With an implementation like this, assuming you stick to less than a fixnum-worth of objects, the avatars are pretty cheap, so the main space cost is the hashtable, and the time cost is the hash lookup on each reference.
Another, non-portable, approach would be to use weak pointers for the references. This is still quite hard work because you have to make sure there is at least one strong reference to the object unless you *really* want to be living in C++-land...
These approaches -- certainly the dependency stuff -- are pretty close to some of the things that the design patterns people talk about.
> My summarizing question is: Does the hiding of the pointer type and > garbage collection in the bottom up lisp language throw a shadow on the > space of expressiveness (analog to an area of disclosure of > computability in the sense of Goedel :-)) which comes apparent even on > the high level like for example in the above mentioned problem? Or is it > possible to enlight those area from the backdoor?
atwo...@bronze.CS.ORST.EDU (John Atwood) writes: > I'm seeking a way to format a file or list of files according to one > of a small set of pre-defined styles. Emacs probably does this, but > I don't use emacs much; how would this be done in emacs?
Emacs will _indent_ an entire buffer if you do C-x h to mark the buffer and then C-M-\ to indent the region.
"Marc Battyani" <Marc_Batty...@csi.com> writes: > I find finalization very useful for some UI objects like fonts pens > brushes OpenGL display lists etc.. > When they are collected you have to free the underlying OS object also. > It's generally not worthwhile to bother to release them as soon > as possible.
Is there a limit on the number of allocated stuff like that? The main problem I always saw was that if your OS had a limit, you could reach that limit before garbage collection. But I agree when that's not the case it can be useful.
-- Lieven Marchand <m...@bewoner.dma.be> If there are aliens, they play Go. -- Lasker
* Lieven Marchand <m...@bewoner.dma.be> | Is there a limit on the number of allocated stuff like that? The main | problem I always saw was that if your OS had a limit, you could reach | that limit before garbage collection. But I agree when that's not the | case it can be useful.
if the OS has such a limit, you should get an OS, or make the functions that allocate such objects cause a garbage collection, if you can't get an OS. other people's limitations are to be overcome, not succumbed to.
#:Erik -- @1999-07-22T00:37:33Z -- pi billion seconds since the turn of the century
* Lieven Marchand wrote: > Is there a limit on the number of allocated stuff like that? The main > problem I always saw was that if your OS had a limit, you could reach > that limit before garbage collection. But I agree when that's not the > case it can be useful.
However it's essentially trivial to cause hitting that limit to invoke a GC.
Erik Naggum <e...@naggum.no> writes: > * Lieven Marchand <m...@bewoner.dma.be> > | The main problem I always saw was that if your OS had a limit, you > | could reach that limit before garbage collection.
> if the OS has such a limit, you should get an OS, or make the > functions that allocate such objects cause a garbage collection, > if you can't get an OS. other people's limitations are to be > overcome, not succumbed to.
The OS I'm currently using (Linux) has for instance, maximum number of open files per process, maximum number of child processes per process, and a whole number of similar limits, the values of some of which can be queried by sysconf() and the value of others who can only be guessed at. Is your limitless OS available somewhere?
-- Lieven Marchand <m...@bewoner.dma.be> If there are aliens, they play Go. -- Lasker
> > In a way garbage collection is destruction automation. Theoretically > > finalization is missing, although I wonder how useful it is in > > practice. If you want to control stuff when an object becomes > > unreachable, timing is usually also important. You don't want to wait > > with closing file descriptors until your application exits, you want > > to do it fairly soon after a stream object becomes unreachable.
> I find finalization very useful for some UI objects like fonts pens > brushes OpenGL display lists etc.. > When they are collected you have to free the underlying OS object also. > It's generally not worthwhile to bother to release them as soon > as possible.
so ANSI common lisp really seems not to have means for real destruction automation. in fact that UI object example was just a hook for triggering the right focus: determinism. it misses the point discussing if we can release such object sooner or later.
that PR-thing i mentioned uses the destruction automatism for data structural purposes. deterministic finalization is used there to rise essential information! also this PR-thing was just one example. PR is not just used for set references to nil, but also for more complex signals. the second point was that PR is a type-expansion of a c++-pointer type. so both things i wanted to mainly discuss in that letter come togehter in that PR-example. thats why i brought this example: Both things - destruction automation and the bottom up exposial of the pointer type to the hands of the programmer - especially if they are used together they provide some new space of expressiveness in the computer language.
a few other guys brought into play that weak-vectors and so .... those are not much more than fine excuse. 1. they do not allow that program driven kill action. you'd have to have alle "normal" references "weak" except one which "holds" and wich you have again to publish widely to allow that kill access (indexes ... §=$%?!). thats not applicable. 2. you have to worry about those special weak things (vectorref. even for one element, ...). every location where you reference the object to get into trouble. you cannot even automate this things completely by a macro, because in lisp you cannot automate the behaviour of the type. so you have to fumble in every sourcecode location, you cannot automate this by one declaration (->hiding of the pointer type). 3. it is not ANSI - by good reason. it is thought for memory management not as a structural mean.
in fact i still have no idea how to transfer essential data structure concepts of the simulation-code i mentioned into lisp without giving up all the elegance - strange that those problems are not a point of speed or low-level.
one little help would be at least if lisp would allow to explitely kill a thing, setting all references to nil by force. because of the GC the information for the necessary backlinks should be present in nearly every implementation (am i right?). this capability would catch at least the simplest behaviour of that PR-example.
so after receiving tons of new capabilities turning to lisp i'am getting aware of some essential lacks (im comparison to e.g. c++) which you cannot excuse with a c++-mindset. those lacks arise from - weak destruction automation - hiding of the nature of pointers - mostly missing type-automation at the point of declaration/instantiation also some bad encapsulation in CLOS (especially: no implizit object context in methods) forces you to write some things redundantly, but this is minor
< that PR-thing i mentioned uses the destruction automatism for data < structural purposes. deterministic finalization is used there to rise < essential information!
There is a program that allows common lisp to be `deterministic', but I cannot remeber the name right now - it's very popular and mentioned quite often on this news group though...
[...]
< one little help would be at least if lisp would allow to explitely kill < a thing, setting all references to nil by force.
(defvar *thing* "this is a thing")
(setq *thing* nil).
< so after receiving tons of new capabilities turning to lisp i'am getting < aware of some essential lacks (im comparison to e.g. c++) which you < cannot excuse with a c++-mindset. those lacks arise from < - weak destruction automation
What is `weak destruction automation'?
< - mostly missing type-automation at the point of [...]
Could you give an example of `type-automation'? I have read the phrase `type-automation', before but could not make sense of it. Have you tried using the macro facility for automation of `type'?
* Robert Kiendl wrote: > one little help would be at least if lisp would allow to explitely kill > a thing, setting all references to nil by force. because of the GC the > information for the necessary backlinks should be present in nearly > every implementation (am i right?). this capability would catch at least > the simplest behaviour of that PR-example.
I still don't see why you can't do this with a dependency mechanism, which must be basically similar to what you are doing in C++:
(defun kill (x &rest args) (map-dependents #'(lambda (d) (apply #'release d x args)) x) (arrange-for-os-stuff-to-be-freed-for x) (values))
X may actually not go away at this point, but all the people who referenced it no longer do so (assuming that's what RELEASE does).
> that PR-thing i mentioned uses the destruction automatism for data > structural purposes. deterministic finalization is used there to rise > essential information! > also this PR-thing was just one example. PR is not just used for set > references to nil, but also for more complex signals. > the second point was that PR is a type-expansion of a c++-pointer type. > so both things i wanted to mainly discuss in that letter come togehter > in that PR-example. thats why i brought this example: Both things - > destruction automation and the bottom up exposial of the pointer type to > the hands of the programmer - especially if they are used together they > provide some new space of expressiveness in the computer language.
....
I don't see the point of all this. If you want to keep references to parent/child or friend objects you can. I do this very often. Then when you destroy/close /modify etc this object you can have the other ones receive notifications automatically .
> in fact i still have no idea how to transfer essential data structure > concepts of the simulation-code i mentioned into lisp without giving up > all the elegance
I've done it and now it's much more elegant than in C++.(and without bugs)
> so after receiving tons of new capabilities turning to lisp i'am getting > aware of some essential lacks (im comparison to e.g. c++) which you > cannot excuse with a c++-mindset. those lacks arise from > - weak destruction automation
No see above.
> - hiding of the nature of pointers
Good!
> - mostly missing type-automation at the point of > declaration/instantiation
Good too. In Lisp type information is used when needed. You don't have to bother with this if you don't want to.
> also some bad encapsulation in CLOS (especially: no implizit object > context in methods) forces you to write some things redundantly, but > this is minor
This is not a lack but a feature. Generic functions are not methods on an object.
i hope it is not too boring for you all but my problems representing some structures in lisp are still pending. (maybe i should read more books, but if one puts my nose on the right point ...)
so i have to bring up some simple c++ example which lights one of the problems. the world of anmials often best animates programmers understanding so ...
[ the points of attention should be my skruples/concerns about lisp mentioned in the last mail:
>- weak destruction automation >- hiding of the nature of pointers >- mostly missing type-automation at the point of
declaration/instantiation ]
//(some Pxx code is at the end of the file if necessary)
//in our model we have birds
class MBase : public IPR, public IPH, public ... {...}; class MBird : public MBase { ...
};
// we also have some world containers like class MSky : public ... { ... map<int, PRH<MBird> > birds ; // here they are d3_dictionary<MXYZ, PR<MBase> > space; // another index pqueue<double, PR<MBird> > interesting; // some of them i'm working on
// we have some nice access:
MBird* lookup(int id); list<MBase*> lookup(MShere& s) { ... care a little about NULL birds ... }
// we do some normal work
void step() { forall(MBird* it, birds) if (it) it->fly(); }
...
};
// somewhere is a camera class VSky : public CView ... { P<MSky> sky; // we hold a sky // we have marked some class VMark { ... list<PR<MBird> > birds; } mark; ...
};
// somewhere is a hunter class MHunter { PE<MGun> gun; PR<MBird> focus; P<MBird> cage; MBird* aim() { return focus ? focus->danger++, focus : NULL }; //// speed annot.: if p is of type PR<> "p->a++" still compiles //// to one machine instruction "inc dword ptr [eax+4]" with -O2 //// despite of all that template stuff the compiler has to digest bool shoot(MBird* bird) { ... bird->pr_message("quiiick"); return gun ? delete bird, true : false; // occasionally one disapears } ...
};
// there is also some global listener static class __TEMP__ : public PR<MBird> { virtual void message(char* s) {... protocol << s; ...} ...
there are just few points that interest me about that shit:
- wenn sky terminates all birds also disapear immediately: no sky no birds. no one can even "quiick" any more. the thing that makes it is the "..PRH.." in the map-definition. it says: you map will hold the birds. [ in case a Java&Basic-GC-fan is out there: there is not any single bird-delete in any of the "..."s. map<> and PRH<> do that; and please do not rise the circular-list-discussion here; if i say hold i mean hold and not "don't worry" ] sometimes this is not totally correct; a bird can be both in the sky and in a cage, in case such bird persists though there is no sky (the ecologist may excuse that strange model but i want so).
in lisp - shure - i know how to hold: the GC does it by default anyway. [ i am just concerned a little bit about that uncertainty at the falling edge. may be spreading some gc-calls helps. someone told from other deterministic programs for lisp ...? but all this can discussed after first reading the rest ... ] but look at all the other bird-refs beside that single PRH. if i do them in lisp as normal things they all hold: the marker, the focus (, the index containers if not sky completely destructs), ... i do not want them to hold and i do not get rid of that shit 100's of cross referenced birds. the hunter aims at that virtual reality bird and so on ... so i have to act: the only way _not_ to hold (99% of the other references above :-)) i know now is using that non-ANSI weak stuff. [ if i use that, do i have to everywhere fumble svref's on the tables where i access that references? (macro or not, i have to act at the access points?) your weak-let i think is for traversed functional bodies. or also for closures? but that temp and local locations are not so troubling here. can i also do so at object positions [CLOS-slots, lying arround lists, ...] and let automate at the access points: e.g. using (slot-value c 'my-variable) somewhere 5 kilometers source code away not knowing if a weak-vector is in or not when passing to the next action?! (the opposite i meant with "fumble": you have to at least trigger a read-char-macro, use special access functions (-> i constantly must know about the weak nature) or so near the access point) but anyway who wants express such essential stuff non-ANSI ? ] or may be someone lateron tells me how to completely rearrange that whole shit totally different in a lisp-oo-way, and all i said here about holding and reflecting is completely unnecessary. so lets go further ...
- in that code above everywhere i handle and move around the birds by simply by using them in the natural way (it->fly(), focus ? , assignments, constructions, intermediate lists, p->a++, if (gun) ... . special behaviour is expressed solely at the definition point. i think e.g.: my bird-refs in VMark must be informed, i use PR<>; my cage must share-hold the bird, i use P<>; my gun belongs to MHunter, so use PE<>; ... i feed this concepts in once (in an aspect-oriented programming style!?) and do not think a lot about at the other 1000 times i access and use the variables.
- i can shoot down a bird somewhere in the code in a consistent manner. [ thats why i asked in the last mail for a _kill_ for lisp things and for that backlinks in the GC-mechanism i think the backlinks exist at least in case of that weak stuff? ] [ this is not done by (defvar *thing* "this is a thing") (setq *thing* nil) like someone mentioned; that doesn't kill the thing but the reference. its of course also not automated to do "delete" in the shoot-func, i am not willing to automate a thing i intentionally want to rise. a future AI program may be able to automate this but not this simple code. what's automated is what happens in response to delete bird: ] the whole shit is informed of the disapearing bird (immediatly). neither in the shoot func nor in MBird-class i made explicite control for the necessary backlinks. i just fed in the above mentioned concepts at some declaration points. elsewhere i just wrote normal natural c++. [ microphone is also informed with other stuff than "delete" though it is just a type-expansion of a pointer; this is just an example to show what i mean with hiding of pointer in lisp. the pointer-thing in c++ can be treated as real type; of course one can do that message shit other ways by writing some extra complex protocol code into MBird and a Listener Class, establishing that multiple doubly link stuff, and so on. but the normal pointers are already there anyway and i simple type-expand that PR-mechansim in three local lines not fumbling anywhere else and can route the message through the same infrastructure than the delete. ] - because mentioned things are automated in the types it is hard to write bugs concerning the interconnection properties. if a basic/java programmer wants to represent above simple structure - for example that shoot-capability - he gets crazy keeping track of the interconnection semantics. this guides to that question in my first mail: "But is lisp also great on connections (which do not suit in the hierarchy paradigm)?" in my first mail.
so looking for example at that shit c++-example above and wanting to convert the structes (in a nonredundant style! if i have to explicitely write out interconnection semantics throughout the code or spread macro-chars and special access-operators selectively over the mass of the code i am not far from Basic&Java.) to lisp there must be done something. may be in a completely different lisp way? how to ... thats was and is my question and i still don't see the way. in some real c++ programs also some other aspectes of that 3 mentioned points on the top of this file are important, but the key i think is rooted in that bird example.
the lisp macros - down to the read-char-dispatch macros - are the best things in lisp. allowing nearly everything in the functional sense. here they must be drilled to make data-type-automation. and perhaps they must be drilled to trick the limitation introduced by the coercion to the garbage collecting (at least in ANSI CL). must they? if yes how?
robert
PS: weaved in the above example is a way C++-mm resembles and even overcomes GC (by pure c++), thereby differentiating on the needs of the objects. that is possible because c++ is providing languange level access to the pointer type (some overloading features) and allows to really expand behaviour of types especially that pointer type. in opposite to for example java which has to live in the sandbox of wood-blocks-LEGO(R)-OOP. so it is also possible to bring up destruction automation for high-level purposes stressed in the above example.
this 3 (or 4) things discussed in this file are of course only 3 of lets say 100 things a good programming language has to feature, most of them best occupied by lisp (for a programmer who needs / wants / can deal with those feateares in order to gain extra speeed) thats why i use lisp. my question is if those things i stressed above form a sort of "base vector" for programming languages which is not (so easily) substituable by combinations of other "vectors". (i am not a mathematician!)
i really don't give up lisp. i just want discuss 3 aspects. and i think lisp is a living language: you can discuss about or at least ask questions.
------------------------------ Paul Foley schrieb:
< - i can shoot down a bird somewhere in the code in a consistent manner. < [ < thats why i asked in the last mail for a _kill_ for lisp things and for < that backlinks in the GC-mechanism < i think the backlinks exist at least in case of that weak stuff? < ] < [ this is not done by (defvar *thing* "this is a thing") < (setq *thing* nil)
< like someone mentioned; that doesn't kill the thing but the reference. < its of course also not automated to do "delete" in the shoot-func, < i am not willing to automate a thing i intentionally want to rise. < a future AI program may be able to automate this but not this simple < code. what's automated is what happens in response to delete bird: < ]
I don't understand C++, so I don't know what your code does, but I think I understand what you're trying to say. What I was trying to demonstrate was that lisp provides no explicit, automatic pointer manipulation; they're just handled automatically.
Perhaps this is more along the lines of what you're looking for?
> i hope it is not too boring for you all but my > problems representing some structures in lisp are still pending. > (maybe i should read more books, but if one puts my nose on the > right point ...)
Well, I will suggest two solutions, both of which you invite! :)
First, "more books": The Art of the Metaobject Protocol, ISBN 0-262-61074-4. That and a Lisp that fully supports the Metaobject Protocol will get you going. And, FWIW, I rolled something of my own under a Lisp that did not have such full MOP support and it would achieve the same...Lisp can do anything. :)
With AMOP you can cook up slots that register themselves as "interested parties" of cooperating slot-values, then write a function called #'c++-delete to notify them when you want something to be forgtten.
Second, "may be someone lateron tells me how to completely rearrange that whole..."
Hello! :) It strikes me you are mixing inappropriately wildly different levels of expression. Specifically, you think the C++ 'delete' operator is a way to kill a virtual bird.
No way! But C/C++ are so close to the hardware--and efficiency has historically been so important--that programmers have always not only expressed virtual states as machine states, but they make the natural mistake of then thinking the machine impelementation is the same as the virtual implementation, such that, getting back to the bird, 'delete-ing' it should kill it (in the virtual world).
I could not follow all of what you wrote (I am doing this on company time) but I also wonder why you have so many pointers to this bird lying around, but that doesn't matter.
Anyway, to a certain extent your question was not about how to kill birds nor even how to manage internal dependencies, but about what Lisp can do, so I think you might want to dig into AMOP if you have not already.
I didn't really understand the C++ stuff in this thread (it's over 10 years since I did anything with C++ other than stare horrified at it). However I was inspired to try and see if I understood what was going on, as some of it seems to be reasonably close to things I already have.
Unfortunately the code I actually use is not releasable -- it doesn't actually quite do this stuff and in any case it's pretty crufty stuff I'm not that proud of. The attached code is just something I typed in last night and shouldn't be taken very seriously. There's 2 layers of it and a trivial example which uses the upper layer.
The bottom layer is a simple dependency class -- you can add & remove (slowly) dependents and map over them.
The next layer is a class which can be told about people who `know about it' and on being told to remove itself it will tell them do do the appropriate thing (this uses double dispatch to avoid something like the awful visitor pattern). This also has a scope macro -- WITH-REMOVABLE-OBJECTS which will call the REMOVE-OBJECT GF at the end of the dynamic scope of the macro. Of course they still actually get heap-allocated, perhaps some DYNAMIC-EXTENT declarations and a very bright compiler could eliminate this (if you even wanted to).
;;;; Objects which have dependents. ;;; A fairly rudimentary implementation. ;;;
(defgeneric add-dependent (thing dependent) (:documentation "Register DEPENDENT as a dependent of THING, a subclass of DEPENDENCY-MIXIN. Return DEPENDENT."))
(defgeneric remove-dependent (thing dependent) (:documentation "Remove a dependent of THING, a subclass of DEPENDENCY-MIXIN. Return non-NIL if the thing was actually a dependent, tested with EQL"))
(defgeneric map-dependents (fn thing) (:documentation "Map FN, a function of one argument, over the dependencies of THING. Return THING."))
;;;; `Removable' objects (bad name). ;;; These are things which keep track of their owners and can inform ;;; them when they are removed. They do this by virtue of being ;;; DEPENDENCY-MIXINs. ;;;
(defgeneric add-object-to (remob to) (:documentation "Add REMOB, an instance of a subclass of REMOVABLE-OBJECT, to TO. No primary method is defined for this GF -- you need to define one for your classes. REMOB will learn that TO knows about it via an after method. Return value unspecified (up to you)."))
(defgeneric remove-object-from (remob from &optional internalp) (:documentation "Remove REMOB, an instance of a subclass of REMOVABLE-OBJECT, from FROM. No primary method is defined for this GF -- you need to define one for your classes. The optional third argument is used internally and should not be supplied in user-calls to this. REMOB will already have learnt that it is being removed when the primary method runs. Return value unspecified (up to you)."))
(defgeneric remove-object (remob &optional junkp) (:documentation "Remove a REMOVABLE-OBJECT. If the optional JUNKP argument is given then the object will be removed in a quick & dirty way, suitable if it's about to be thrown away. No primary method is defined for this GF -- you need to define one for your classes. All the dependents of REMOB will be told to remove it before the primary method runs. Return value unspecified (up to you)."))
(defmethod remove-object ((b bird) &optional junkp) ;; Must define this. (declare (ignorable junkp)) ;need to do this better (values))
(defclass birded-object () ;; something that contains birds, like a sky or cage. ((birds :initform '() :accessor birds)))
(defmethod add-object-to ((b bird) (to birded-object)) ;; double dispatch, this whole thing would be a pain without this, you'd need ;; something like visitor. (push b (birds to)))
(defmethod remove-object-from ((b bird) (from birded-object) &optional secret) ;; REMOVE-OBJECT-FROM' contract is buggy, either the optioal argument ;; needs better explained or it needs better concealed. (declare (ignorable secret)) (setf (birds from) (delete b (birds from))))
;;; Tracing
(defmethod add-object-to :before ((b bird) (to birded-object)) (format t "~& Adding ~A bird to ~A~%" (name-of b) (name-of to)))
(defmethod remove-object-from :before ((b bird) (to birded-object) &optional annoying) (declare (ignorable annoying)) (format t "~& Removing ~A bird from ~A~%" (name-of b) (name-of to)))
(defclass sky (birded-object named-object) () (:default-initargs :name "the sky"))
(defclass cage (birded-object named-object) () (:default-initargs :name "a golden cage"))
(defun test-bird () (let ((s (make-instance 'sky :name "cloudy sky")) (c (make-instance 'cage :name "rusty cage"))) (with-removable-objects ((b (make-instance 'bird :name "Louise"))) (add-object-to b s) ;the bird is in the sky (add-object-to b c) ;and the cage (remove-object-from b s) (add-object-to b s) (values s c) ;; (and here the bird gets removed from the sky and the cage) )))