I'm working on a simple object database built on top of a relational
database. My current design is to have a "storable" mixin class which
defines two methods, STORE and RETRIEVE, which do the obvious things.
A problem with this simple design is that it places a burden on the user
to keep track of the relative states of the objects in memory and in the
DB, and to decide when to STORE and RETRIEVE them. It would be nice to
make this transparent so that any reference to a slot of a STORABLE object
would transparently synchronize to the DB. I had envisioned implementing
this using a :before method on the slot-value and (setf slot-value)
methods which would check to make sure everything was in sync.
Is there a better way to do it? Is this a job for the MOP?
My goal here is not only to make something functional, but also to
highlight the power of CLOS with something that will really knock the
socks off the Java people, so I really want to do this the Right Way.
E.
In article <··························@k-137-79-50-101.jpl.nasa.gov>,
·········@jpl.nasa.gov (Erann Gat) wrote:
>I had envisioned implementing
> this using a :before method on the slot-value and (setf slot-value)
> methods which would check to make sure everything was in sync.
That should read MY-SLOT-VALUE and (SETF MY-SLOT-VALUE). SLOT-VALUE is
not a generic function.
E.
·········@jpl.nasa.gov (Erann Gat) writes:
> In article <··························@k-137-79-50-101.jpl.nasa.gov>,
> ·········@jpl.nasa.gov (Erann Gat) wrote:
>
>>I had envisioned implementing
>> this using a :before method on the slot-value and (setf slot-value)
>> methods which would check to make sure everything was in sync.
>
> That should read MY-SLOT-VALUE and (SETF MY-SLOT-VALUE). SLOT-VALUE is
> not a generic function.
That would end up requiring a transaction on every store!
In article <············@ccs.neu.edu>, Joe Marshall <···@ccs.neu.edu> wrote:
> ·········@jpl.nasa.gov (Erann Gat) writes:
>
> > In article <··························@k-137-79-50-101.jpl.nasa.gov>,
> > ·········@jpl.nasa.gov (Erann Gat) wrote:
> >
> >>I had envisioned implementing
> >> this using a :before method on the slot-value and (setf slot-value)
> >> methods which would check to make sure everything was in sync.
> >
> > That should read MY-SLOT-VALUE and (SETF MY-SLOT-VALUE). SLOT-VALUE is
> > not a generic function.
>
> That would end up requiring a transaction on every store!
TANSTAAFL. But 1) it requires an update, not a transaction and 2) I have
a scheme whereby multiple updates get queued up and only written to the DB
when they're actually needed.
E.
"Erann Gat" <·········@jpl.nasa.gov> wrote in message
·······························@k-137-79-50-101.jpl.nasa.gov...
> In article <············@ccs.neu.edu>, Joe Marshall <···@ccs.neu.edu>
wrote:
> > That would end up requiring a transaction on every store!
>
> TANSTAAFL. But 1) it requires an update, not a transaction and 2) I have
> a scheme whereby multiple updates get queued up and only written to the DB
> when they're actually needed.
If you write to the DB (that is any standard generic modern SQL DB, and
specifically excluding mySQL), you get a transaction for every write no
matter what. The distinction being whether the transaction persists or
commits at the end of the write.
The other way, depending on what you want to do, is to simply flag the
entire object (and, perhaps the slot) for update and then on your Big Commit
you can either flush the entire thing, or if your noting slots, do a smart
update.
Obviously, one is simpler and more basic to do than the other.
It also depends on whether you want to be able to rollback object changes.
(setf object (make-instance 'persistent-user-class))
(setf (userid-key-slot object) "ID1234")
(retrieve object)
(print (first-name object))
"BOB"
(begin-transaction)
(setf (first-name object) "SAM")
(rollback-transaction)
(print (first-name object))
???
These are some of the fun problems persistence framework guys get to fight
with :-), and how "transparent" persistence really is/isn't.
Regards,
Will Hartung
(·····@msoft.com)
In article <···············@ID-197644.news.uni-berlin.de>, "Will Hartung"
<·····@msoft.com> wrote:
> "Erann Gat" <·········@jpl.nasa.gov> wrote in message
> ·······························@k-137-79-50-101.jpl.nasa.gov...
> > In article <············@ccs.neu.edu>, Joe Marshall <···@ccs.neu.edu>
> wrote:
> > > That would end up requiring a transaction on every store!
> >
> > TANSTAAFL. But 1) it requires an update, not a transaction and 2) I have
> > a scheme whereby multiple updates get queued up and only written to the DB
> > when they're actually needed.
>
> If you write to the DB (that is any standard generic modern SQL DB, and
> specifically excluding mySQL)
I am in fact using mySQL precisely for that reason. I want a simple,
efficient, indexed persistent store, not ACID.
E.
Newer versions of mysql have two modes.
One supports rollback so excluding it would be wrong if I understand you
right.
On Fri, 19 Mar 2004 14:10:31 -0800, Will Hartung <·····@msoft.com> wrote:
> If you write to the DB (that is any standard generic modern SQL DB, and
> specifically excluding mySQL), you get a transaction for every write no
> matter what. The distinction being whether the transaction persists or
> commits at the end of the write.
--
Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
·········@jpl.nasa.gov (Erann Gat) writes:
> In article <············@ccs.neu.edu>, Joe Marshall <···@ccs.neu.edu> wrote:
>
>> ·········@jpl.nasa.gov (Erann Gat) writes:
>>
>> > In article <··························@k-137-79-50-101.jpl.nasa.gov>,
>> > ·········@jpl.nasa.gov (Erann Gat) wrote:
>> >
>> >>I had envisioned implementing
>> >> this using a :before method on the slot-value and (setf slot-value)
>> >> methods which would check to make sure everything was in sync.
>> >
>> > That should read MY-SLOT-VALUE and (SETF MY-SLOT-VALUE). SLOT-VALUE is
>> > not a generic function.
>>
>> That would end up requiring a transaction on every store!
>
> TANSTAAFL. But 1) it requires an update, not a transaction and 2) I have
> a scheme whereby multiple updates get queued up and only written to the DB
> when they're actually needed.
But you said before:
``It would be nice to make this transparent so that any reference to
a slot of a STORABLE object would transparently synchronize to the DB.''
Either the lisp system is synchronized or it isn't.
--
~jrm
In article <············@comcast.net>, Joe Marshall
<·············@comcast.net> wrote:
> ·········@jpl.nasa.gov (Erann Gat) writes:
>
> > In article <············@ccs.neu.edu>, Joe Marshall <···@ccs.neu.edu> wrote:
> >
> >> ·········@jpl.nasa.gov (Erann Gat) writes:
> >>
> >> > In article <··························@k-137-79-50-101.jpl.nasa.gov>,
> >> > ·········@jpl.nasa.gov (Erann Gat) wrote:
> >> >
> >> >>I had envisioned implementing
> >> >> this using a :before method on the slot-value and (setf slot-value)
> >> >> methods which would check to make sure everything was in sync.
> >> >
> >> > That should read MY-SLOT-VALUE and (SETF MY-SLOT-VALUE). SLOT-VALUE is
> >> > not a generic function.
> >>
> >> That would end up requiring a transaction on every store!
> >
> > TANSTAAFL. But 1) it requires an update, not a transaction and 2) I have
> > a scheme whereby multiple updates get queued up and only written to the DB
> > when they're actually needed.
>
> But you said before:
> ``It would be nice to make this transparent so that any reference to
> a slot of a STORABLE object would transparently synchronize to the DB.''
>
> Either the lisp system is synchronized or it isn't.
Or both. Sometimes core and disk are synchronized and sometimes they are
not, much like virtual memory or file buffers. What I mean by
transparency is that when you decide you want to render the current state
of things to disk you can simply call "sync" and everything gets taken
care of for you without you having to know or care about what is and is
not being written. If you make changes and forget to call sync, or Lisp
crashes, then you lose, just as you lose if your unix crashes with
unflushed file buffers. But Lisp never crashes, so that's not really a
problem :-)
E.
·········@jpl.nasa.gov (Erann Gat) writes:
> I'm working on a simple object database built on top of a relational
> database. My current design is to have a "storable" mixin class which
> defines two methods, STORE and RETRIEVE, which do the obvious things.
Would (un)commonSQL do the job?
--
Rahul Jain
·····@nyct.net
Professional Software Developer, Amateur Quantum Mechanicist
In article <··············@nyct.net>, Rahul Jain <·····@nyct.net> wrote:
> ·········@jpl.nasa.gov (Erann Gat) writes:
>
> > I'm working on a simple object database built on top of a relational
> > database. My current design is to have a "storable" mixin class which
> > defines two methods, STORE and RETRIEVE, which do the obvious things.
>
> Would (un)commonSQL do the job?
Too complicated for my tastes. Also, it didn't work in MCL the last time
I checked.
E.
·········@jpl.nasa.gov (Erann Gat) writes:
> In article <··············@nyct.net>, Rahul Jain <·····@nyct.net> wrote:
>
>> ·········@jpl.nasa.gov (Erann Gat) writes:
>>
>> > I'm working on a simple object database built on top of a relational
>> > database. My current design is to have a "storable" mixin class which
>> > defines two methods, STORE and RETRIEVE, which do the obvious things.
>>
>> Would (un)commonSQL do the job?
>
> Too complicated for my tastes. Also, it didn't work in MCL the last time
> I checked.
>
Well, what your asking for _IS_ complicated. We wrote a set of
wrappers around Xanaly's CommonSQL to make it look even
more "transparent", and I tell you it was seriously hairy and
still somewhat brittle. (Of course, we cared about ACID etc.).
What about PLOB! ?
Also, look into the berkeley-db interface which might be nicer
for simple stuff than the whole-hog SQL.
In article <··············@memetrics.com>, Alain Picard
<············@memetrics.com> wrote:
> ·········@jpl.nasa.gov (Erann Gat) writes:
>
> > In article <··············@nyct.net>, Rahul Jain <·····@nyct.net> wrote:
> >
> >> ·········@jpl.nasa.gov (Erann Gat) writes:
> >>
> >> > I'm working on a simple object database built on top of a relational
> >> > database. My current design is to have a "storable" mixin class which
> >> > defines two methods, STORE and RETRIEVE, which do the obvious things.
> >>
> >> Would (un)commonSQL do the job?
> >
> > Too complicated for my tastes. Also, it didn't work in MCL the last time
> > I checked.
> >
>
> Well, what your asking for _IS_ complicated.
No, I don't think so. (And I back up that assertion with the fact that
I've got a lot of it working already.) But I'm curious, what is it that
you think I'm asking for that's complicated?
> What about PLOB! ?
Doesn't run on MCL, and probably can't be ported because MCL doesn't
support the full MOP and plob seems to rely on that (at least judging from
a cursory glance at the docs).
> Also, look into the berkeley-db interface which might be nicer
> for simple stuff than the whole-hog SQL.
A good suggestion, but I've already got a mySQL interface up and running,
so I'm going to stick with that.
E.
Erann Gat wrote:
>
> ...
>
> > What about PLOB! ?
>
> Doesn't run on MCL, and probably can't be ported because MCL doesn't
> support the full MOP and plob seems to rely on that (at least judging from
> a cursory glance at the docs).
when i last asked, there was also the issue with the license for the
underlying database.
...
·········@jpl.nasa.gov (Erann Gat) writes:
> In article <··············@memetrics.com>, Alain Picard
> <············@memetrics.com> wrote:
>
>> ·········@jpl.nasa.gov (Erann Gat) writes:
>>
>> > In article <··············@nyct.net>, Rahul Jain <·····@nyct.net> wrote:
>> >
>> >> ·········@jpl.nasa.gov (Erann Gat) writes:
>> >>
>> >> > I'm working on a simple object database built on top of a relational
>> >> > database. My current design is to have a "storable" mixin class which
>> >> > defines two methods, STORE and RETRIEVE, which do the obvious things.
>> >>
>> >> Would (un)commonSQL do the job?
>> >
>> > Too complicated for my tastes. Also, it didn't work in MCL the last time
>> > I checked.
>> >
>>
>> Well, what your asking for _IS_ complicated.
>
> No, I don't think so. (And I back up that assertion with the fact that
> I've got a lot of it working already.) But I'm curious, what is it that
> you think I'm asking for that's complicated?
Hum... maybe I shouldn't have said that. Maybe I should have
said: what you _want_, but may not know you want, is complicated.
To be honest, maybe that should say: what I wanted (and implemented)
turned out to be far more complicated that I originally thought.
In article <··············@memetrics.com>, Alain Picard
<············@memetrics.com> wrote:
> Hum... maybe I shouldn't have said that. Maybe I should have
> said: what you _want_, but may not know you want, is complicated.
Ah. Well, obviously I wouldn't know about that :-)
E.
Rahul Jain <·····@nyct.net> writes:
> ·········@jpl.nasa.gov (Erann Gat) writes:
>
> > I'm working on a simple object database built on top of a
> > relational database. My current design is to have a "storable"
> > mixin class which defines two methods, STORE and RETRIEVE, which
> > do the obvious things.
>
> Would (un)commonSQL do the job?
Has the OP tried looking at "object prevalence"?
http://www.advogato.org/article/398.html
http://www-106.ibm.com/developerworks/library/wa-objprev/
Some Lisp code at:
http://www.prevayler.org/wiki.jsp?topic=PrevaylerPortsToOtherLanguages
I first read about it in "Rebel With A Cause":
http://homepage.mac.com/svc/RebelWithACause/index.html
--
David Magda <dmagda at ee.ryerson.ca>, http://www.magda.ca/
Because the innovator has for enemies all those who have done well under
the old conditions, and lukewarm defenders in those who may do well
under the new. -- Niccolo Machiavelli, _The Prince_, Chapter VI
In article <··············@number6.magda.ca>, David Magda
<··················@ee.ryerson.ca> wrote:
> Rahul Jain <·····@nyct.net> writes:
>
> > ·········@jpl.nasa.gov (Erann Gat) writes:
> >
> > > I'm working on a simple object database built on top of a
> > > relational database. My current design is to have a "storable"
> > > mixin class which defines two methods, STORE and RETRIEVE, which
> > > do the obvious things.
> >
> > Would (un)commonSQL do the job?
>
> Has the OP tried looking at "object prevalence"?
I have now.
Object prevalence seems to me no different than dumping a Lisp image,
which most Lisps provide by default. Seems like yet another example of
Java folks reinventing a thirty year old wheel (notwithstanding that it
was then adapted by a Common Lisper). I am at a loss to understand what
functionality Common Lisp prevalence is supposed to provide that an image
dump does not.
E.
·········@jpl.nasa.gov (Erann Gat) writes:
> I am at a loss to understand what functionality Common Lisp
> prevalence is supposed to provide that an image dump does not.
Upgrading to a new implementation?
--
Alan Shutko <···@acm.org> - I am the rocks.
Calvin: Calvin? Who's Calvin? I'm *Stupendous Man*!
On Sat, 20 Mar 2004 13:16:45 -0800, ·········@jpl.nasa.gov (Erann Gat) wrote:
> In article <··············@number6.magda.ca>, David Magda
> <··················@ee.ryerson.ca> wrote:
>
>> Has the OP tried looking at "object prevalence"?
>
> I have now.
>
> Object prevalence seems to me no different than dumping a Lisp
> image, which most Lisps provide by default. Seems like yet another
> example of Java folks reinventing a thirty year old wheel
> (notwithstanding that it was then adapted by a Common Lisper). I am
> at a loss to understand what functionality Common Lisp prevalence is
> supposed to provide that an image dump does not.
1. In several CL implementations saving an image will force you to
quit your application.
2. Saving an image will most likely write megabytes of data to the
disk.
So you're saying that each time the state of one of your persistent
objects changes you want to do this? Or rather you are implying that
the folks working on (Common Lisp) object prevalence are dumb enough
to do this? Looks to me like you haven't really understood what you
read and instead preferred to make bold claims...
Note that I'm not saying that object prevalence is or is not a good
solution for object persistence. I just fail to see how this is "no
different than dumping a Lisp image."
Edi.
·········@jpl.nasa.gov (Erann Gat) writes:
[...]
> I am at a loss to understand what functionality Common Lisp
> prevalence is supposed to provide that an image dump does not.
Depending on your data set you may not want to dump everything.
Do a whole dump at low traffic areas, but do quick
serialization/transaction logs as objects are changed. This way you
have your data as of 4am, but have all the changes that occured
throughout the day should the server go own at 11pm.
In your serialization code you code also have synchoronization code
between servers so that you can do load balancing and have hot-spares
of services.
If some corruption of the data should also occur for whatever reason
you can go a step-through of the transaction log up to the point data
was still clean.
Just some thoughts; they may or may not be useful in your particular
situation.
--
David Magda <dmagda at ee.ryerson.ca>, http://www.magda.ca/
Because the innovator has for enemies all those who have done well under
the old conditions, and lukewarm defenders in those who may do well
under the new. -- Niccolo Machiavelli, _The Prince_, Chapter VI
In article <··············@number6.magda.ca>, David Magda
<··················@ee.ryerson.ca> wrote:
> ·········@jpl.nasa.gov (Erann Gat) writes:
> [...]
> > I am at a loss to understand what functionality Common Lisp
> > prevalence is supposed to provide that an image dump does not.
>
> Depending on your data set you may not want to dump everything.
>
> Do a whole dump at low traffic areas, but do quick
> serialization/transaction logs as objects are changed. This way you
> have your data as of 4am, but have all the changes that occured
> throughout the day should the server go own at 11pm.
>
> In your serialization code you code also have synchoronization code
> between servers so that you can do load balancing and have hot-spares
> of services.
>
> If some corruption of the data should also occur for whatever reason
> you can go a step-through of the transaction log up to the point data
> was still clean.
>
> Just some thoughts; they may or may not be useful in your particular
> situation.
Ah. Well, there's already a bunch of stuff built-in to Lisp that lets you
do all that too, starting with PRINT and READ. (There's a reason
*print-readably*, *print-circle* and make-load-form exist.)
As for transaction logs, why do you need anything more than, say,:
(defmacro define-logged-function (name args &body body)
`(defun ,name ,args
(write-to-transaction-log (list ',name ,@args))
,@body))
To play back a log you just call LOAD.
It all still seems like a reinvented wheel to me.
E.
Erann Gat wrote:
> I'm working on a simple object database built on top of a relational
> database. My current design is to have a "storable" mixin class which
> defines two methods, STORE and RETRIEVE, which do the obvious things.
>
> A problem with this simple design is that it places a burden on the user
> to keep track of the relative states of the objects in memory and in the
> DB, and to decide when to STORE and RETRIEVE them. It would be nice to
> make this transparent so that any reference to a slot of a STORABLE object
> would transparently synchronize to the DB. I had envisioned implementing
> this using a :before method on the slot-value and (setf slot-value)
> methods which would check to make sure everything was in sync.
SLOT-VALUE and (SETF SLOT-VALUE) are not generic functions, according to
the hyperspec.
> Is there a better way to do it? Is this a job for the MOP?
Yes, it sounds like a classical example for the MOP. You could write
methods on SLOT-VALUE-USING-CLASS and (SETF SLOT-VALUE-USING-CLASS).
See e.g. "User-Level Language Crafting: Introducing the Metaobject Protocol"
by Andreas Paepcke for examples of how you could do this. Also, if I'm
not mistaken, the UncommonSQL library does some MOP hackery to achieve
something similar to what you want.
Arthur Lemmens
Erann Gat wrote:
> I'm working on a simple object database built on top of a relational
> database. My current design is to have a "storable" mixin class which
> defines two methods, STORE and RETRIEVE, which do the obvious things.
>
[...]
> Is there a better way to do it? Is this a job for the MOP?
Yes, probably. Make sure to read the paper by Andreas Paepcke. It
includes a detailed sketch how to add such a persistence layer by way of
the MOP. Even if you don't want to use the MOP, it should give you the
right ideas.
Pascal
--
1st European Lisp and Scheme Workshop
June 13 - Oslo, Norway - co-located with ECOOP 2004
http://www.cs.uni-bonn.de/~costanza/lisp-ecoop/
In article <············@newsreader2.netcologne.de>, Pascal Costanza
<········@web.de> wrote:
> Make sure to read the paper by Andreas Paepcke.
Which one? He's written about ten million papers.
E.
Erann Gat wrote:
> In article <············@newsreader2.netcologne.de>, Pascal Costanza
> <········@web.de> wrote:
>
>>Make sure to read the paper by Andreas Paepcke.
>
> Which one? He's written about ten million papers.
"User-level language crafting - Introducing the CLOS Metaobject
Protocol", http://www-db.stanford.edu/~paepcke/shared-documents/mopintro.ps
Pascal
--
1st European Lisp and Scheme Workshop
June 13 - Oslo, Norway - co-located with ECOOP 2004
http://www.cs.uni-bonn.de/~costanza/lisp-ecoop/
Erann Gat wrote:
> My goal here is not only to make something functional, but also to
> highlight the power of CLOS with something that will really knock the
> socks off the Java people, so I really want to do this the Right Way.
This last paragraph sounds interesting for me.
I am trying to collect as many examples as possible to see where Lisp is
in fact "better" than Java. As a Lisp beginner I did'nt really encounter
such situations. As soon I presented an experienced Java coder the Lisp
code he easily brought up very similar solutions - perhaps my examples
have been to easy so far.
But anyways, some people will ask why one first needs uuuullltra complex
problems so that Lisp has some slight advantages over Java (or Python)..
if it is so great one should find some advantages earlier.
Will you post your resulting code when you are done with your program?
Regards,
Andr�
--
"Andr� Thieme" <······································@justmail.de> wrote in
message ·················@ulric.tng.de...
> I am trying to collect as many examples as possible to see where Lisp is
> in fact "better" than Java. As a Lisp beginner I did'nt really encounter
> such situations. As soon I presented an experienced Java coder the Lisp
> code he easily brought up very similar solutions - perhaps my examples
> have been to easy so far.
Its always "easy" to counter simple examples with simple examples. "Oh, well
we just do xyz..."
> But anyways, some people will ask why one first needs uuuullltra complex
> problems so that Lisp has some slight advantages over Java (or Python)..
> if it is so great one should find some advantages earlier.
The reason is that you get to some point where the inertia of CLs
capabilities will break the Java Camels back.
Typically, all of the benefits of CL tend to be very incremental.
A "simple" example is "Aspect Oriented Programming".
Lisp already has some of these abilities "built in". A lot of Lisps have an
"advice" facility, (TRACE...) is a specialized example of that. Then you
have the before, after, and around methods of CLOS "for free". (And yes I
know there is more to AOP than advice and around methods.)
But even if it did not, by the nature of the system, they could be added
using standard mechanisms, including macros.
With Java, they had to write either a new compiler (AspectJ), or they have
to augment using XML, custom Classloaders, and byte code hacking (this is
how JBoss does their system). Neither one uses the Java Language itself to
implement AOP (yes, I know they were implemented in Java, but you don't
write AOP source in Java, you use AJ, or XML). The Java Language hit some
wall that they could not cross and at that point they hopped over and start
digging in and undermining the system from the outside in. As a language,
Java is simply not extensible. The JVM, of course, is quite flexible as
shown by assorted other languages that are not Java implemented on top of
it.
Lisp is extensible. Java is not. Not every task needs that extensibility,
but on the other hand, you don't need to implement large scale systems like
AOP to take advantage of the CL extensiblity. You can do it with little bits
on a daily basis. Layer upon layer, bit by bit. The beauty is that CL rises
to the level of abstraction necessary for the developer to be most
productive, which is different for each developer, and different for each
project. Java can only rise so high then stops, and it stops hard. All of a
sudden what would have been an incremental bit of abstraction in CL is now a
whole new project and tool suite for Java. CL "goes to 11".
A simple example of that is the new Java (1.5) has a new 'for' statement.
Yippee!
In CL those who want a new looping construct can create one and move on. A
simple example is the contentious IF* macro from Franz. No committees, no
papers, no votings, no new compilers, nothing. Need it? Use it, move on.
Your quality of life as a developer has just been bumped up an inch. One
less nit to pick and aggravate you when your day is going bad.
And to be fair, some don't value this property. It is safe to say that folks
may well value a standard Servlet container and binary platform portability
over being able to define a new 'for' loop or seemlessly implement AOP.
Regards,
Will Hartung
(·····@msoft.com)
In article <············@ulric.tng.de>, =?ISO-8859-1?Q?Andr=E9_Thieme?=
<······································@justmail.de> wrote:
> Will you post your resulting code when you are done with your program?
Yes, of course. But that would be "if", not "when." This is a
low-priority project.
E.
On Fri, 19 Mar 2004 23:17:17 +0100, Andr� Thieme <······································@justmail.de> wrote:
> I am trying to collect as many examples as possible to see where
> Lisp is in fact "better" than Java. As a Lisp beginner I did'nt
> really encounter such situations. As soon I presented an experienced
> Java coder the Lisp code he easily brought up very similar solutions
> - perhaps my examples have been to easy so far.
What? The Java guys now have macros, a CLOS-like object system,
HANDLER-BIND, and UNWIND-PROTECT? I must have missed a couple of
memos...
BTW, do you want to impress other coders or to you want to code
yourself?
> But anyways, some people will ask why one first needs uuuullltra
> complex problems so that Lisp has some slight advantages over Java
> (or Python).. if it is so great one should find some advantages
> earlier.
As long as you just hang around on Usenet and ask other people to
explain to you why Lisp is "better" you won't get anywhere. Read some
books, look at real code (I mean larger systems not two-liners), write
your own programs. Make sure you have at least a general understanding
about what each chapter of the CLHS is about. Then, after some months,
ask yourself again if you really need someone to hold your hand and
convince you that Lisp is the silver bullet.
Or just stay with Java and Python. Certainly everything that can be
done in Lisp can be done in these languages.
<http://en.wikipedia.org/wiki/Turing-complete>
Edi.
Edi Weitz wrote:
> wake up
Yes yes, you are right...
I ordered Norvigs "Paradigms of Artificial Intelligence Programming" and
should concentrate more on deepening my knowledge and do my analysis and
comparisons about languages later.
Andr�
--
Andr� Thieme <······································@justmail.de> writes:
> Erann Gat wrote:
>
> > My goal here is not only to make something functional, but also to
> > highlight the power of CLOS with something that will really knock the
> > socks off the Java people, so I really want to do this the Right Way.
>
> This last paragraph sounds interesting for me.
> I am trying to collect as many examples as possible to see where Lisp is
> in fact "better" than Java. As a Lisp beginner I did'nt really encounter
> such situations. As soon I presented an experienced Java coder the Lisp
> code he easily brought up very similar solutions - perhaps my examples
> have been to easy so far.
> But anyways, some people will ask why one first needs uuuullltra complex
> problems so that Lisp has some slight advantages over Java (or Python)..
> if it is so great one should find some advantages earlier.
Well, obviously Lisp evolved on complex problems! If Lisp programmer
had been trying to solve easy problems for 50 years, you'd be writting:
echo "Hello World!"
instead of all these parentheses!
--
__Pascal_Bourguignon__ http://www.informatimago.com/
There is no worse tyranny than to force a man to pay for what he doesn't
want merely because you think it would be good for him.--Robert Heinlein
http://www.theadvocates.org/
On Fri, 19 Mar 2004 11:17:51 -0800, ·········@jpl.nasa.gov (Erann Gat)
wrote:
>My goal here is not only to make something functional, but also to
>highlight the power of CLOS with something that will really knock the
>socks off the Java people, so I really want to do this the Right Way.
The Right Way is to use proxies. Anything you want to do with a
stored object, such as use it in a calculation or whatever, should be
done by a function which takes a proxy of the stored object as an
argument.
The storage behavior of an object, e.g. whether it gets updated in the
database after every operation, whether it gets cached, etc., is
implemented as features of the proxies. The program should be able to
tune the proxies, i.e. tell some proxies to use caching behavior and
others to not, etc.
This is the Right Way because it's at a higher level of abstraction
where the power of CLOS pays off better. You don't have to concern
yourself constantly with low level details of storage behavior, but
only when actually designing the storage behavior and tuning it.
When thinking about proxies at the application logic level, you don't
even think of them as proxies, but rather as the stored objects
themselves. The proxy is the object, for the purposes of the higher
level application code.
Also keep in mind that a stored object may actually occupy several
database tables. You want to be thinking of the objects as objects,
not as database records. The database records are the low level
details of the object, and should come to mind only when you need to
focus on such a low level of abstraction for the purpose of working on
the implementation of those low level details.
This doesn't establish Lisp as superior to Java for this purpose, but
it's the right direction to go, to get the full power of an object
database. Once you start going in that direction, you will find
plenty of good uses for the power of CLOS.
--
My email address is ··········@ISP.net where Name is
eric, number is 9000, and ISP is earthlink.
In article <··································@4ax.com>, Eric Smith
<·········@sig.txt> wrote:
> On Fri, 19 Mar 2004 11:17:51 -0800, ·········@jpl.nasa.gov (Erann Gat)
> wrote:
>
> >My goal here is not only to make something functional, but also to
> >highlight the power of CLOS with something that will really knock the
> >socks off the Java people, so I really want to do this the Right Way.
>
> The Right Way is to use proxies.
Actually, I think the Right Way is to make the objects *be* proxies for
themselves :-) The idea is that an object that inherits from STORABLE
ends up having two states, one where its slots are known not to contain
correct data (you can think of this as the proxy state) and one where they
do (the non-proxy "real object" state). If a reference is made to a slot
while it's in the proxy state, it updates itself from the DB
(transparently of course) which switches it into the non-proxy state, at
which point the calculation can proceed normally.
> Also keep in mind that a stored object may actually occupy several
> database tables.
Why? I was planning on one table per class, with one column per class
instance slot, and a separate table for class slots (maybe -- I don't use
class slots much, so I haven't given this a whole lot of thought).
> You want to be thinking of the objects as objects,
> not as database records. The database records are the low level
> details of the object, and should come to mind only when you need to
> focus on such a low level of abstraction for the purpose of working on
> the implementation of those low level details.
Yes, I agree, but I'm still curious why you think a single object might
span multiple tables.
E.
On Fri, 19 Mar 2004 23:48:25 -0800, ·········@jpl.nasa.gov (Erann Gat)
wrote:
>Yes, I agree, but I'm still curious why you think a single object might
>span multiple tables.
One situation where that happens is when you're using a legacy
database and you want your program to deal with stored objects instead
of data tables, while an old mainframe in another room continues to
deal with the same data the way it always did.
Regardless, the point is to think of the stored objects as objects.
When you think in terms of slots or records, you're at a low level of
abstraction. Instead of thinking in terms of getting data from the
stored object for an operation, think in terms of having the stored
object itself participate in the operation.
--
My email address is ··········@ISP.net where Name is
eric, Number is 9000, and ISP is earthlink.
On Fri, 19 Mar 2004 23:48:25 -0800, ·········@jpl.nasa.gov (Erann Gat)
wrote:
>Why? I was planning on one table per class, with one column per class
>instance slot, and a separate table for class slots (maybe -- I don't use
>class slots much, so I haven't given this a whole lot of thought).
What if one of the slots has a list with up to a million items in it?
A relational database column is normally of a fixed size.
--
My email address is ··········@ISP.net where Name is
eric, Number is 9000, and ISP is earthlink.
In article <··································@4ax.com>, Eric Smith
<·········@sig.txt> wrote:
> On Fri, 19 Mar 2004 23:48:25 -0800, ·········@jpl.nasa.gov (Erann Gat)
> wrote:
>
> >Why? I was planning on one table per class, with one column per class
> >instance slot, and a separate table for class slots (maybe -- I don't use
> >class slots much, so I haven't given this a whole lot of thought).
>
> What if one of the slots has a list with up to a million items in it?
> A relational database column is normally of a fixed size.
That's a not an issue of having one object stored across multiple tables,
that an issue of how to store a cons cell in the database. That is indeed
a very thorny issue, and one I haven't quite decided how to handle yet.
But it seems to me to be an orthogonal issue to that of storing a single
object across multiple tables.
E.
·········@jpl.nasa.gov (Erann Gat) writes:
> In article <··································@4ax.com>, Eric Smith
> <·········@sig.txt> wrote:
>
> > On Fri, 19 Mar 2004 11:17:51 -0800, ·········@jpl.nasa.gov (Erann Gat)
> > wrote:
> >
> > >My goal here is not only to make something functional, but also to
> > >highlight the power of CLOS with something that will really knock the
> > >socks off the Java people, so I really want to do this the Right Way.
> >
> > The Right Way is to use proxies.
>
> Actually, I think the Right Way is to make the objects *be* proxies for
> themselves :-) The idea is that an object that inherits from STORABLE
> ends up having two states, one where its slots are known not to contain
> correct data (you can think of this as the proxy state) and one where they
> do (the non-proxy "real object" state). If a reference is made to a slot
> while it's in the proxy state, it updates itself from the DB
> (transparently of course) which switches it into the non-proxy state, at
> which point the calculation can proceed normally.
(:brainstorming-mode
How about using kenny's cells for this? It sounds like exactly
the kind of thing that they do well.)
In article <··············@germany.igpm.rwth-aachen.de>, Mario S. Mommer
<········@yahoo.com> wrote:
> ·········@jpl.nasa.gov (Erann Gat) writes:
> > In article <··································@4ax.com>, Eric Smith
> > <·········@sig.txt> wrote:
> >
> > > On Fri, 19 Mar 2004 11:17:51 -0800, ·········@jpl.nasa.gov (Erann Gat)
> > > wrote:
> > >
> > > >My goal here is not only to make something functional, but also to
> > > >highlight the power of CLOS with something that will really knock the
> > > >socks off the Java people, so I really want to do this the Right Way.
> > >
> > > The Right Way is to use proxies.
> >
> > Actually, I think the Right Way is to make the objects *be* proxies for
> > themselves :-) The idea is that an object that inherits from STORABLE
> > ends up having two states, one where its slots are known not to contain
> > correct data (you can think of this as the proxy state) and one where they
> > do (the non-proxy "real object" state). If a reference is made to a slot
> > while it's in the proxy state, it updates itself from the DB
> > (transparently of course) which switches it into the non-proxy state, at
> > which point the calculation can proceed normally.
>
> (:brainstorming-mode
>
> How about using kenny's cells for this? It sounds like exactly
> the kind of thing that they do well.)
Could be. I got the impression Cells did a lot more than that though,
that they were like a spreadsheet, computing a dependency graph for
expressions and propogating changes throughout the graph. Not that that's
not a useful thing, but it seems a little heavyweight for what I'm trying
to do. But I'll take a look. Kenny, you want to weight in here?
E.
Mario S. Mommer wrote:
> ·········@jpl.nasa.gov (Erann Gat) writes:
>
>>In article <··································@4ax.com>, Eric Smith
>><·········@sig.txt> wrote:
>>
>>
>>>On Fri, 19 Mar 2004 11:17:51 -0800, ·········@jpl.nasa.gov (Erann Gat)
>>>wrote:
>>>
>>>
>>>>My goal here is not only to make something functional, but also to
>>>>highlight the power of CLOS with something that will really knock the
>>>>socks off the Java people, so I really want to do this the Right Way.
>>>
>>>The Right Way is to use proxies.
>>
>>Actually, I think the Right Way is to make the objects *be* proxies for
>>themselves :-) The idea is that an object that inherits from STORABLE
>>ends up having two states, one where its slots are known not to contain
>>correct data (you can think of this as the proxy state) and one where they
>>do (the non-proxy "real object" state). If a reference is made to a slot
>>while it's in the proxy state, it updates itself from the DB
>>(transparently of course) which switches it into the non-proxy state, at
>>which point the calculation can proceed normally.
>
>
> (:brainstorming-mode
>
> How about using kenny's cells for this? It sounds like exactly
> the kind of thing that they do well.)
I guess I could have jumped in sooner, on the OP where Erann wondered if
a :before method on some my-slot-value method would be a good idea.
<jumpin>
The Cells /code/ might be useful as an example, since the functionality
faces some of the same problems. And the answer to the original question
about using a :before method on some slot-value wrapper is...
Yeah, but it is not a mere :before method on my-slot-value.
my-slot-value and (setf my-slot-value) become huge little subsystems
with all sorts of work to do before somewhere deep in the bowels of the
code they actually invoke slot-value or its setf.
That's Cells, where the function name happens to be md-slot-value, "md"
short for "model". As for a persistent ODB...
As a user of Franz's nifty AllegroStore I heard enough about the
difficult issues in efficient, multi-user DBs (ODB or RDB) to have some
confidence that the my-slot-value for that will also end up being its
own little world. :before won't cut it, and there is no point in :around
since you have to write an implementation wrapper anyway so it is
already :around slot-value.
As for Cells itself, well, I had fun /marrying/ Cells and AllegroStore
such that peristent instances interacted with each other and/or GUI
views of them. As for implementing an ODB using them, I do not know
enough about the issues to say much. But I sense the problems are
orthogonal, and that PCells (persistent Cells) would be something again
built atop some independent effort to solve all the hairy problems of
robust, efficient, multi-user, networked DBs.
</jumpin>
kt
--
http://tilton-technology.com
Why Lisp? http://alu.cliki.net/RtL%20Highlight%20Film
Your Project Here! http://alu.cliki.net/Industry%20Application
Erann Gat wrote:
>
> In article <··································@4ax.com>, Eric Smith
> <·········@sig.txt> wrote:
>
> > On Fri, 19 Mar 2004 11:17:51 -0800, ·········@jpl.nasa.gov (Erann Gat)
> > wrote:
> >
> > >My goal here is not only to make something functional, but also to
> > >highlight the power of CLOS with something that will really knock the
> > >socks off the Java people, so I really want to do this the Right Way.
> >
> > The Right Way is to use proxies.
>
> Actually, I think the Right Way is to make the objects *be* proxies for
> themselves :-) The idea is that an object that inherits from STORABLE
> ends up having two states, one where its slots are known not to contain
> correct data (you can think of this as the proxy state) and one where they
> do (the non-proxy "real object" state). If a reference is made to a slot
> while it's in the proxy state, it updates itself from the DB
> (transparently of course) which switches it into the non-proxy state, at
> which point the calculation can proceed normally.
>
a third alternative is to flip the suggested proxy architecture around, to
offer low impedance access to the data model object and to interpose a proxy
between it and the database. this is the architecture which jdo realizes.
...
·········@jpl.nasa.gov (Erann Gat) writes:
>
> Yes, I agree, but I'm still curious why you think a single object might
> span multiple tables.
Inheritance.
--
It would be difficult to construe Larry Wall, in article
this as a feature. <·····················@netlabs.com>
In article <··············@memetrics.com>, Alain Picard
<············@memetrics.com> wrote:
> ·········@jpl.nasa.gov (Erann Gat) writes:
>
> >
> > Yes, I agree, but I'm still curious why you think a single object might
> > span multiple tables.
>
> Inheritance.
Could you elaborate on that please? It is not obvious to me why
inheritance would cause a single object to span multiple tables.
E.
Erann Gat wrote:
>>Inheritance.
>
> Could you elaborate on that please? It is not obvious to me why
> inheritance would cause a single object to span multiple tables.
Assume a simple hierarchy.
(defclass c1 ()
(x y))
(defclass c2 (c1)
(z))
How do you store instances of such classes? There are basically the
following schemes:
a) Create one table with columns x, y and z for both c1 and c2.
b) Create two tables c1 and c2.
c) Create one table with columns x and y for both c1 and c2. Another
table delta-c2 only stores the additional z contained in c2 instances.
There is no clear winner here, because it all depends on how many
instances of each class you expect to have, whether inserts / updates
occur often, what information is frequently requested, whether you want
to optimize for speed or for space, and so on...
Pascal
--
1st European Lisp and Scheme Workshop
June 13 - Oslo, Norway - co-located with ECOOP 2004
http://www.cs.uni-bonn.de/~costanza/lisp-ecoop/
In article <············@newsreader2.netcologne.de>, Pascal Costanza
<········@web.de> wrote:
> Erann Gat wrote:
>
> >>Inheritance.
> >
> > Could you elaborate on that please? It is not obvious to me why
> > inheritance would cause a single object to span multiple tables.
>
> Assume a simple hierarchy.
>
> (defclass c1 ()
> (x y))
>
> (defclass c2 (c1)
> (z))
>
> How do you store instances of such classes? There are basically the
> following schemes:
>
> a) Create one table with columns x, y and z for both c1 and c2.
>
> b) Create two tables c1 and c2.
>
> c) Create one table with columns x and y for both c1 and c2. Another
> table delta-c2 only stores the additional z contained in c2 instances.
>
> There is no clear winner here
Seems to me (b) is the clear winner.
>, because it all depends on how many
> instances of each class you expect to have, whether inserts / updates
> occur often, what information is frequently requested, whether you want
> to optimize for speed or for space, and so on...
Sorry, but I don't see any realistic circumstances under which (a) or (c)
offer any substantial advantage. (Remember, we're only talking about a
persistent store here. We're not trying to optimize for anything other
than store and retrieve.) Maybe I'm just being thick. Could you describe
a scenario where (a) or (c) win over (b)?
E.
·········@jpl.nasa.gov (Erann Gat) writes:
> In article <············@newsreader2.netcologne.de>, Pascal Costanza
> <········@web.de> wrote:
>
>> Erann Gat wrote:
>>
>> >>Inheritance.
>> >
>> > Could you elaborate on that please? It is not obvious to me why
>> > inheritance would cause a single object to span multiple tables.
>>
>> Assume a simple hierarchy.
>>
>> (defclass c1 ()
>> (x y))
>>
>> (defclass c2 (c1)
>> (z))
>>
>> How do you store instances of such classes? There are basically the
>> following schemes:
>>
>> a) Create one table with columns x, y and z for both c1 and c2.
>>
>> b) Create two tables c1 and c2.
>>
>> c) Create one table with columns x and y for both c1 and c2. Another
>> table delta-c2 only stores the additional z contained in c2 instances.
>>
>> There is no clear winner here
>
> Seems to me (b) is the clear winner.
>
>>, because it all depends on how many
>> instances of each class you expect to have, whether inserts / updates
>> occur often, what information is frequently requested, whether you want
>> to optimize for speed or for space, and so on...
>
> Sorry, but I don't see any realistic circumstances under which (a) or (c)
> offer any substantial advantage. (Remember, we're only talking about a
> persistent store here. We're not trying to optimize for anything other
> than store and retrieve.) Maybe I'm just being thick. Could you describe
> a scenario where (a) or (c) win over (b)?
(a) select all instances of c1 (which includes all instances of c2)
(c) find the object ids of all c1 with slot x = 10 (which again includes instances of c2)
-Peter
--
Peter Seibel ·····@javamonkey.com
Lisp is the red pill. -- John Fraser, comp.lang.lisp
In article <··············@javamonkey.com>, Peter Seibel
<·····@javamonkey.com> wrote:
> ·········@jpl.nasa.gov (Erann Gat) writes:
>
> > In article <············@newsreader2.netcologne.de>, Pascal Costanza
> > <········@web.de> wrote:
> >
> >> Erann Gat wrote:
> >>
> >> >>Inheritance.
> >> >
> >> > Could you elaborate on that please? It is not obvious to me why
> >> > inheritance would cause a single object to span multiple tables.
> >>
> >> Assume a simple hierarchy.
> >>
> >> (defclass c1 ()
> >> (x y))
> >>
> >> (defclass c2 (c1)
> >> (z))
> >>
> >> How do you store instances of such classes? There are basically the
> >> following schemes:
> >>
> >> a) Create one table with columns x, y and z for both c1 and c2.
> >>
> >> b) Create two tables c1 and c2.
> >>
> >> c) Create one table with columns x and y for both c1 and c2. Another
> >> table delta-c2 only stores the additional z contained in c2 instances.
> >>
> >> There is no clear winner here
> >
> > Seems to me (b) is the clear winner.
> >
> >>, because it all depends on how many
> >> instances of each class you expect to have, whether inserts / updates
> >> occur often, what information is frequently requested, whether you want
> >> to optimize for speed or for space, and so on...
> >
> > Sorry, but I don't see any realistic circumstances under which (a) or (c)
> > offer any substantial advantage. (Remember, we're only talking about a
> > persistent store here. We're not trying to optimize for anything other
> > than store and retrieve.) Maybe I'm just being thick. Could you describe
> > a scenario where (a) or (c) win over (b)?
>
> (a) select all instances of c1 (which includes all instances of c2)
>
> (c) find the object ids of all c1 with slot x = 10 (which again includes
instances of c2)
Ah. Well, both of these are beyond the scope of what I'm trying to do.
Finding all the instances of a class is a facility that does not exist in
Common Lisp, and is orthogonal to the issue of building a persistent
object store. (To say nothing of the fact that it is still not clear why
having single objects span multiple tables would be a win. The only
advantage I see is that you could get all the info in a single query
instead of 1+N queries where N is the number of subclasses of the class of
interest. Since N tends to be small in actual practice, the benefits of
the other designs still seem to me to be marginal at best.)
E.
Erann Gat wrote:
>>a) Create one table with columns x, y and z for both c1 and c2.
>>
>>b) Create two tables c1 and c2.
>>
>>c) Create one table with columns x and y for both c1 and c2. Another
>>table delta-c2 only stores the additional z contained in c2 instances.
>>
>>There is no clear winner here
>
> Seems to me (b) is the clear winner.
This can only be the case if you have specific usage patterns in mind
that we don't know about.
> Sorry, but I don't see any realistic circumstances under which (a) or (c)
> offer any substantial advantage. (Remember, we're only talking about a
> persistent store here. We're not trying to optimize for anything other
> than store and retrieve.) Maybe I'm just being thick. Could you describe
> a scenario where (a) or (c) win over (b)?
a) (change-class obj 'c2)
c) Many of the slots are unbound most of the time. (The AMOP book gives
such an example as a motivation for hashtable-based classes.)
If b) is the clear winner in your case that's of course fine. However,
you might also want to be prepared for questions wrt alternative
object<->table mappings from the people you present your solution to.
(Don't know what the actual setting is, though.)
Pascal
--
1st European Lisp and Scheme Workshop
June 13 - Oslo, Norway - co-located with ECOOP 2004
http://www.cs.uni-bonn.de/~costanza/lisp-ecoop/
In article <············@newsreader2.netcologne.de>, Pascal Costanza
<········@web.de> wrote:
> Erann Gat wrote:
>
> >>a) Create one table with columns x, y and z for both c1 and c2.
> >>
> >>b) Create two tables c1 and c2.
> >>
> >>c) Create one table with columns x and y for both c1 and c2. Another
> >>table delta-c2 only stores the additional z contained in c2 instances.
> >>
> >>There is no clear winner here
> >
> > Seems to me (b) is the clear winner.
>
> This can only be the case if you have specific usage patterns in mind
> that we don't know about.
That's likely true. Like someone else said, I probably want things that I
don't know that I want :-)
> > Sorry, but I don't see any realistic circumstances under which (a) or (c)
> > offer any substantial advantage. (Remember, we're only talking about a
> > persistent store here. We're not trying to optimize for anything other
> > than store and retrieve.) Maybe I'm just being thick. Could you describe
> > a scenario where (a) or (c) win over (b)?
>
> a) (change-class obj 'c2)
It seems to me that you're making the tacit assumption that obj is a C1.
Be that as it may, in the worst case this would cause the object to move
from whatever table it's in to the C2 table. That's not an expensive
operation.
> c) Many of the slots are unbound most of the time. (The AMOP book gives
> such an example as a motivation for hashtable-based classes.)
So? Are you trying to save disk space? In an age where 100 GB hard
drives are commonplace that seems like the height of premature
optimization.
> If b) is the clear winner in your case that's of course fine. However,
> you might also want to be prepared for questions wrt alternative
> object<->table mappings from the people you present your solution to.
> (Don't know what the actual setting is, though.)
I'm not trying to pick a fight here. I'm genuinely trying to understand
why you think that (a) and (c) can sometimes be big wins. I just don't
see it.
E.
·········@jpl.nasa.gov (Erann Gat) writes:
[ . . . ]
> I'm not trying to pick a fight here. I'm genuinely trying to
> understand why you think that (a) and (c) can sometimes be big wins.
> I just don't see it.
I've been following this thread with some interest because I've
had to solve this problem in both C++ and Java (yes, I realize that my
karmic burden is heavy). Just to stir the pot a bit, one of the
requirements of the toolkits I developed was compatibility with legacy
database schemas. Object state could be spread over a number of
tables and one table could hold part of the state of multiple types.
Do you have this requirement? Do you anticipate having it in the
future? Is there any reason for your design not to allow it?
Regards,
Patrick
------------------------------------------------------------------------
S P Engineering, Inc. | The experts in large scale distributed OO
| systems design and implementation.
···@spe.com | (C++, Java, ObjectStore, Oracle, CORBA, UML)
In article <··············@localhost.localdomain>, Patrick May
<···@spe.com> wrote:
> ·········@jpl.nasa.gov (Erann Gat) writes:
> [ . . . ]
> > I'm not trying to pick a fight here. I'm genuinely trying to
> > understand why you think that (a) and (c) can sometimes be big wins.
> > I just don't see it.
>
> I've been following this thread with some interest because I've
> had to solve this problem in both C++ and Java (yes, I realize that my
> karmic burden is heavy). Just to stir the pot a bit, one of the
> requirements of the toolkits I developed was compatibility with legacy
> database schemas. Object state could be spread over a number of
> tables and one table could hold part of the state of multiple types.
> Do you have this requirement?
No.
> Do you anticipate having it in the future?
No.
> Is there any reason for your design not to allow it?
Yes. This is a persistent object store that happens to use a relational
database in the implementation. It is not intended to be a general OO
interface to a relational database. I am not out to reinvent commonSQL.
I want to keep this lightweight.
Because this is an object store, I want to preserve object identity as
much as possible. For example, if X is a list, I want it to be the case
that:
(eq (cdr (retrieve (store x))) (retrieve (store (cdr x)))) --> T
This requires me to impose a rigid design on the database schema I'm using
to store objects. I'm essentially building something that looks like a
Lisp heap on disk. Nearly everything is an integer foreign key (which is
to say, a pointer), and to make it work the keys all have to conform to
certain conventions that any legacy DB is extremely unlilkely to adhere
to.
That said, I do have a simple table->object mapping that works on any
table and simply presents each row as an object with a slot for each
column. All the values are assumed to be atomic types. If you want to
create an object mapping across multiple tables you can do that by using a
database that provides views.
E.
Erann Gat wrote:
> So? Are you trying to save disk space? In an age where 100 GB hard
> drives are commonplace that seems like the height of premature
> optimization.
"640K ought to be enough for everyone." - Bill Gates
;)
Pascal
P.S.: I am only giving you second-hand knowledge. I am not a database
expert myself. What seems to be an important factor in large databases
is that schema evolution is very expensive, so it's hard to tune a
database after you have identified a bottleneck. So it seems to be
worthwhile to have a good idea what your usage patterns are going to be.
My conclusion would rather be to think hard about avoiding a relational
database as long as possible, and for example store information as
s-expressions in text files instead.
--
1st European Lisp and Scheme Workshop
June 13 - Oslo, Norway - co-located with ECOOP 2004
http://www.cs.uni-bonn.de/~costanza/lisp-ecoop/
Pascal Costanza <········@web.de> writes:
> P.S.: I am only giving you second-hand knowledge. I am not a database
> expert myself. What seems to be an important factor in large databases
> is that schema evolution is very expensive, so it's hard to tune a
> database after you have identified a bottleneck. So it seems to be
> worthwhile to have a good idea what your usage patterns are going to
> be. My conclusion would rather be to think hard about avoiding a
> relational database as long as possible, and for example store
> information as s-expressions in text files instead.
Right. Or if you really have to put your objects into a RDBMS, store
them in BLOBS, and only add a few "key" columns for indexing.
--
__Pascal_Bourguignon__ http://www.informatimago.com/
There is no worse tyranny than to force a man to pay for what he doesn't
want merely because you think it would be good for him.--Robert Heinlein
http://www.theadvocates.org/
·········@jpl.nasa.gov (Erann Gat) writes:
> So? Are you trying to save disk space? In an age where 100 GB hard
> drives are commonplace that seems like the height of premature
> optimization.
My disks, be them 10 MB, 10 GB or 160 GB, seem to always be 99% full... :-)
--
__Pascal_Bourguignon__ http://www.informatimago.com/
There is no worse tyranny than to force a man to pay for what he doesn't
want merely because you think it would be good for him.--Robert Heinlein
http://www.theadvocates.org/
Pascal Bourguignon <····@thalassa.informatimago.com> writes:
> ·········@jpl.nasa.gov (Erann Gat) writes:
> > So? Are you trying to save disk space? In an age where 100 GB
> > hard drives are commonplace that seems like the height of
> > premature optimization.
>
> My disks, be them 10 MB, 10 GB or 160 GB, seem to always be 99%
> full... :-)
Parkinson's Law of Data:
Data expands to fill the space available for storage.
http://www.faqs.org/docs/jargon/P/Parkinson's-Law-of-Data.html
Works for automobiles and the expansion of highways/roads (especially
evident in California I think).
Parkinson's Law:
Work expands so as to fill the time available for its completion.
http://www.heretical.com/miscella/parkinsl.html
He had several others: http://www.globalideasbank.org/socinv/SIC-117.HTML
--
David Magda <dmagda at ee.ryerson.ca>, http://www.magda.ca/
Because the innovator has for enemies all those who have done well under
the old conditions, and lukewarm defenders in those who may do well
under the new. -- Niccolo Machiavelli, _The Prince_, Chapter VI
"Erann Gat" <·········@jpl.nasa.gov> wrote in message
·······························@192.168.1.51...
> In article <··············@memetrics.com>, Alain Picard
> <············@memetrics.com> wrote:
>
> > ·········@jpl.nasa.gov (Erann Gat) writes:
> >
> > >
> > > Yes, I agree, but I'm still curious why you think a single object
might
> > > span multiple tables.
> >
> > Inheritance.
>
> Could you elaborate on that please? It is not obvious to me why
> inheritance would cause a single object to span multiple tables.
>
> E.
Assume you have a class partner with slots a b c
This class is subclassed by the classes person with additional slot d and
company with additional slot e.
One possible scheme for persistence is to use three database tables
1 partner with columns a, b and c (lets assume that a is the pkey)
2 person with a and d
3 company with a and e
An instance of person or company obviously spans 2 tables.
If have also seen that people model this case just with one table
partner
with columns a, b, c, d ,e and a technical column f to indicate to the
corresponding class
A third solution uses two tables person and company (assuming that partner
is abstract)
but this has the disadvatage that all columns of partner have to be
duplicated in the tables person and company
saludos
Karsten
In article <····················@news-reader.eresmas.com>, "Karsten Poeck"
<························@wanadoo.es> wrote:
> A third solution uses two tables person and company (assuming that partner
> is abstract)
> but this has the disadvatage that all columns of partner have to be
> duplicated in the tables person and company
Why is that a disadvantage?
E.
Erann Gat <·········@jpl.nasa.gov> wrote:
+---------------
| "Karsten Poeck" <························@wanadoo.es> wrote:
| > A third solution uses two tables person and company (assuming that partner
| > is abstract)
| > but this has the disadvatage that all columns of partner have to be
| > duplicated in the tables person and company
|
| Why is that a disadvantage?
+---------------
Think about the implications of a superclass with slots with class
allocation. If you duplicate those slots in multiple subclass tables,
you immediately run up against a consistency-constraint problem.
It seems to me that if you're look at the data with an "Entity/Relationship"
viewpoint (E-R modelling), than the the entities work o.k. as objects,
but only for a completely flat class hierarchy. The M:N relationships
don't at all (and 1:N and N:1 relationships are just subsets of those).
Try this concrete example: What would your object hierarchy have to
look like so that when a company changed the location of one of its
offices ("Branch #N"), all of the work addresses for employees who
work at that branch automatically change to match the new location?
Classically, in a 3NF database that information is *not* stored in
the employee record, but in a separate "company/branch#/address" table.
(Sound like a superclass with class-allocated slots?)
-Rob
-----
Rob Warnock <····@rpw3.org>
627 26th Avenue <URL:http://rpw3.org/>
San Mateo, CA 94403 (650)572-2607
In article <······················@speakeasy.net>, ····@rpw3.org (Rob
Warnock) wrote:
> Erann Gat <·········@jpl.nasa.gov> wrote:
> +---------------
> | "Karsten Poeck" <························@wanadoo.es> wrote:
> | > A third solution uses two tables person and company (assuming that partner
> | > is abstract)
> | > but this has the disadvatage that all columns of partner have to be
> | > duplicated in the tables person and company
> |
> | Why is that a disadvantage?
> +---------------
>
> Think about the implications of a superclass with slots with class
> allocation. If you duplicate those slots in multiple subclass tables,
> you immediately run up against a consistency-constraint problem.
The issue of class-allocated slots seems to me completely orthogonal to
the issue of inheritance. Obviously you want to store the class-allocated
slots on a per-class basis, not on a per-instance basis, but that's true
whether or not the class inherits or is inherited from.
So I guess I'll buy class-allocated slots as an example of where an
instance is stored in multiple (two) tables. (I don't actually use
class-allocated slots myself. They don't provide any useful functionality
IMO. If someone wants to contest that they should probably start a new
thread.)
> It seems to me that if you're look at the data with an "Entity/Relationship"
> viewpoint (E-R modelling), than the the entities work o.k. as objects,
> but only for a completely flat class hierarchy. The M:N relationships
> don't at all (and 1:N and N:1 relationships are just subsets of those).
>
> Try this concrete example: What would your object hierarchy have to
> look like so that when a company changed the location of one of its
> offices ("Branch #N"), all of the work addresses for employees who
> work at that branch automatically change to match the new location?
> Classically, in a 3NF database that information is *not* stored in
> the employee record, but in a separate "company/branch#/address" table.
> (Sound like a superclass with class-allocated slots?)
I've never heard of a company changing the location of one of its
offices. That would require some major structural engineering. I have
heard of companies moving all the employees at one work location to a
different work location, but that is not at all the same thing.
But be that as it may, to me that sounds like an instance slot in the
employee class that contains a work-location object, which in turn
contains a work address. Many employees would probably share the same (in
the sense of eq) work location.
E.
·········@jpl.nasa.gov (Erann Gat) writes:
> I've never heard of a company changing the location of one of its
> offices. That would require some major structural engineering. I have
> heard of companies moving all the employees at one work location to a
> different work location, but that is not at all the same thing.
I thought you lived in California. I understand that offices can move
quite a bit around there.
--
~jrm
In article <············@comcast.net>, Joe Marshall
<·············@comcast.net> wrote:
> ·········@jpl.nasa.gov (Erann Gat) writes:
>
> > I've never heard of a company changing the location of one of its
> > offices. That would require some major structural engineering. I have
> > heard of companies moving all the employees at one work location to a
> > different work location, but that is not at all the same thing.
>
> I thought you lived in California. I understand that offices can move
> quite a bit around there.
Touche :-)
E.
Alain Picard <············@memetrics.com> writes:
> ·········@jpl.nasa.gov (Erann Gat) writes:
>
> >
> > Yes, I agree, but I'm still curious why you think a single object might
> > span multiple tables.
>
> Inheritance.
Or attribute lists (for lack of a better term at the moment).
Consider an object with a slot whose value is a list of properties, that
don't deserve to be objects in their own right. In the relational world,
those properties are going to be in a child table.
I found this child-table business to be the most annoying part of implementing
a relational data store for a CLOS application. Not insurmountable, just
annoying...
-- Ray
In article <·············@cableone.THIS_PART_IS_BOGUS.net>, Ray Fink
<···@cableone.THIS_PART_IS_BOGUS.net> wrote:
> Alain Picard <············@memetrics.com> writes:
>
> > ·········@jpl.nasa.gov (Erann Gat) writes:
> >
> > >
> > > Yes, I agree, but I'm still curious why you think a single object might
> > > span multiple tables.
> >
> > Inheritance.
>
> Or attribute lists (for lack of a better term at the moment).
> Consider an object with a slot whose value is a list of properties, that
> don't deserve to be objects in their own right. In the relational world,
> those properties are going to be in a child table.
I would not consider this to be a single object that spans multiple tables
but rather an object with a slot whose value is a different object of a
different type, which is therefore unsurprisingly stored in a different
table.
> I found this child-table business to be the most annoying part of implementing
> a relational data store for a CLOS application. Not insurmountable, just
> annoying...
What did you find annoying about it? Is it just writing out all the WHERE
clauses in the query statements, or was it something else?
E.
·········@jpl.nasa.gov (Erann Gat) writes:
> In article <·············@cableone.THIS_PART_IS_BOGUS.net>, Ray Fink
> <···@cableone.THIS_PART_IS_BOGUS.net> wrote:
...snipped..
> > Or attribute lists (for lack of a better term at the moment).
> > Consider an object with a slot whose value is a list of properties, that
> > don't deserve to be objects in their own right. In the relational world,
> > those properties are going to be in a child table.
>
> I would not consider this to be a single object that spans multiple tables
> but rather an object with a slot whose value is a different object of a
> different type, which is therefore unsurprisingly stored in a different
> table.
Fair enough. But do note this other object (the list) is itself a compound
object, constituting multiple rows in another table.
> > I found this child-table business to be the most annoying part of implementing
> > a relational data store for a CLOS application. Not insurmountable, just
> > annoying...
>
> What did you find annoying about it? Is it just writing out all the WHERE
> clauses in the query statements, or was it something else?
Annoying (1) in the sense of tedious, WHERE clauses and such, and
(2) that I felt that my implementation didn't abstract away enough of
those details. The implementation of the base class was about
1200 lines of code (including comments and whitespace), client classes
had to provide another ten or twenty lines of code for each slot that
referenced a child table, including defining the SELECT statements, in
addition to declarations for the relational table name and slot-to-field
mapping.
On a positive note, that code has been in daily production use
(with small transaction volumes) since 1992, and working quite nicely.
-- Ray
For tangential reasons, somebody at my lab has been looking into
"object-relational mapping" technology. We looked mostly at Java stuff.
Things like Hibernate, Castor and CocoBase.
The mapping itself is tricky on a conceptual level that goes beyond
CLOS. A CLOS based mapping would be nice to have.
Cheers
Marco
Ray Fink wrote:
> Alain Picard <············@memetrics.com> writes:
>
>
>>·········@jpl.nasa.gov (Erann Gat) writes:
>>
>>
>>>Yes, I agree, but I'm still curious why you think a single object might
>>>span multiple tables.
>>
>>Inheritance.
>
>
> Or attribute lists (for lack of a better term at the moment).
> Consider an object with a slot whose value is a list of properties, that
> don't deserve to be objects in their own right. In the relational world,
> those properties are going to be in a child table.
>
> I found this child-table business to be the most annoying part of implementing
> a relational data store for a CLOS application. Not insurmountable, just
> annoying...
>
> -- Ray
>
Marco Antoniotti <·······@cs.nyu.edu> writes:
> For tangential reasons, somebody at my lab has been looking into
> "object-relational mapping" technology. We looked mostly at Java
> stuff. Things like Hibernate, Castor and CocoBase.
>
> The mapping itself is tricky on a conceptual level that goes beyond
> CLOS. A CLOS based mapping would be nice to have.
openmcl + (clos <-> Objective-C in latest version) + MacOSX WebObject.
--
__Pascal_Bourguignon__ http://www.informatimago.com/
There is no worse tyranny than to force a man to pay for what he doesn't
want merely because you think it would be good for him.--Robert Heinlein
http://www.theadvocates.org/
Ray Fink <···@cableone.THIS_PART_IS_BOGUS.net> writes:
> Or attribute lists (for lack of a better term at the moment).
> Consider an object with a slot whose value is a list of properties, that
> don't deserve to be objects in their own right. In the relational world,
> those properties are going to be in a child table.
If the objects in the list are all "simple", what I did was
to create a "db-huge-lisp-object" table type, where I could
stuff any lisp object which supports a READ/WRITE operation.
Thus you could do things like
(setf (some-slot my-persistent-obj) (list 1 2 3))
for example.
Is that what you meant?
Alternately, if you contain a list of other persistent objects,
all of some unified superclass, you could do
(make-instance* owner 'sub-foo-1 :initargs...)
(make-instance* owner 'sub-foo-2 :initargs...)
(make-instance* owner 'sub-foo-3 :initargs...)
and later, in a new image, say
(foos owner)
=> (#<sub-foo-1 "Joe"> #<sub-foo-2 "Sam"> #<sub-foo-2 "Sally">)
This requires objects to "transmogrify" from their superclass
into their real (final) class at time of instantiation.
This is part of the "magic I needed but didn't know I needed"
when I started writing this whole mess. :-)
Alain Picard <············@memetrics.com> writes:
> Ray Fink <···@cableone.THIS_PART_IS_BOGUS.net> writes:
>
> > Or attribute lists (for lack of a better term at the moment).
> > Consider an object with a slot whose value is a list of properties, that
> > don't deserve to be objects in their own right. In the relational world,
> > those properties are going to be in a child table.
>
> If the objects in the list are all "simple", what I did was
> to create a "db-huge-lisp-object" table type, where I could
> stuff any lisp object which supports a READ/WRITE operation.
> Thus you could do things like
> (setf (some-slot my-persistent-obj) (list 1 2 3))
> for example.
>
> Is that what you meant?
Not quite. I meant working with a relational database, normalized,
specifically so the data from the Lisp application would be
accessible to conventional desktop tools (e.g. ODBC to Excel, Access,
etc). Thus it was necessary to store data in a format that could be
understood _and_ manipulated by non-Lisp applications... plain old
NUMBER and VARCHAR2 and other "plain vanilla" SQL datatypes. So
to use your example above, there are three records in the child table
(SOME_SLOT), each with a foreign key column that leads back to
the parent object.
> Alternately, if you contain a list of other persistent objects,
> all of some unified superclass, you could do
>
> (make-instance* owner 'sub-foo-1 :initargs...)
> (make-instance* owner 'sub-foo-2 :initargs...)
> (make-instance* owner 'sub-foo-3 :initargs...)
>
> and later, in a new image, say
>
> (foos owner)
> => (#<sub-foo-1 "Joe"> #<sub-foo-2 "Sam"> #<sub-foo-2 "Sally">)
>
> This requires objects to "transmogrify" from their superclass
> into their real (final) class at time of instantiation.
>
> This is part of the "magic I needed but didn't know I needed"
> when I started writing this whole mess. :-)
Erann Gat wrote:
> I'm working on a simple object database built on top of a relational
> database. My current design is to have a "storable" mixin class which
> defines two methods, STORE and RETRIEVE, which do the obvious things.
[...]
> Is there a better way to do it? Is this a job for the MOP?
Another hint, taking into account that you are using MCL: It's true that
MCL doesn't support the whole MOP, but IIRC the introspection stuff is
pretty much complete. You could traverse the class hierarchy and find
all accessor methods, and then just define before/after/around methods
on those and require that accesses do not go through SLOT-VALUE for
those classes. (Accessor methods for standard classes are required to be
instances of STANDARD-ACCESSOR-METHOD.)
> My goal here is not only to make something functional, but also to
> highlight the power of CLOS with something that will really knock the
> socks off the Java people, so I really want to do this the Right Way.
Another completely different approach would be to use the object
prevalence framework at http://homepage.mac.com/svc/prevalence/readme.html
Pascal
--
1st European Lisp and Scheme Workshop
June 13 - Oslo, Norway - co-located with ECOOP 2004
http://www.cs.uni-bonn.de/~costanza/lisp-ecoop/
In article <············@newsreader2.netcologne.de>, Pascal Costanza
<········@web.de> wrote:
> Another completely different approach would be to use the object
> prevalence framework at http://homepage.mac.com/svc/prevalence/readme.html
Thanks for the pointer. This seems very similar to what I'm doing except
that I'm going to an RDB instead of XML (which are actually very similar
if you think about it.)
E.
Erann Gat wrote:
> My goal here is not only to make something functional, but also to
> highlight the power of CLOS with something that will really knock the
> socks off the Java people, so I really want to do this the Right Way.
As a lisp liking Java person, here are the points you need to address to
de-sock a Java-head:
Remember in your comparison that the 'Java way' is not REP but making
the program from source files with a build utility, often integrated
with an IDE.
In Java, there is no MOP built into the language spec. (There again, in
ANSI common lisp, ditto). There is, instead, a mechanism for specifying
metadata about packages, classes, fields, and methods, called javadoc.
Javadocs consist of a descriptive string, and certain attribute tags.
This metadata is processed by a separate processor to the compiler, as
part of the build process for the project. Sun provides mechanisms and a
parser for people to extend this metadata by adding their own attributes.
In Java, there is no 'slot-accessor'. Inside a class, the fields are
accessed using the field access primitives. Outside, mutable fields
should be accessed using accessor function pairs. This isn't enforced by
the language, but strongly encouraged by every Java text I've seen.
For example, the class
(defclass foo () ( (foo :type integer) ) )
Would be written in Java as:
class Foo {
int foo;
public int getFoo () {
return this.foo;
}
public void setFoo (int foo) {
this.foo = foo;
}
}
Whereas the version with public field access:
class Foo {
public int foo;
}
Would be used where a struct would be in C, rather than as a full blown
class.
As part of the Java build process, tools such as AspectJ, XDoclet and
the many DB binding tools process either the code before the javac
compiler, or the byte code after. XDoclet uses the Sun-sanctioned
metadata mechanism, for example to using the hibernate DB mechanism the
attribute:
/**
* @hibernate.class
* table="FOO"
*/
Is added to the jdoc, and the doclet processor called as part of the
build process. This adds the function calls to the source code before or
after the property accessors to ensure the
Javadocs are the mechanism where metadata for the elements that are
available in the MOP is stored in Java source code for compile-time
processing. There is a separate mechanism, the reflection API, for
getting metadata at runtime.
After the ·@' <java-legal-name>, the syntax is not restricted by the
doclet spec, instead the text to the next @ is returned as a string.
This is to be changed in version 1.5 of java, where user defined @tags
are allowed in the source code proper, and will be available via the
reflection API at runtime.
The processors that process the compile-time metadata are not specified
in the language, but Sun provides a framework to add them to. The build
process itself is not specified in the language, but one build tool,
Ant, is ubiquitous.
[java head off: It is a less than good thing that the only extension
mechanism in java (version<=1.4) is by adding to the jdocs, and that
jdoc sections are tagged in a manner such that the javac compiler
re-uses the /* to ignore, allowing people confuse them with comments.]
Using a freely available DB binding framework (hibernate), the
'standard' java build tool (Ant), the Java language's metadata mechanism
(doctags) and an implementation of a doctag processor (XDoclet), what is
needed to implement the functionality in Java is
- one XML element added to the build script to invoke the metadata
processor.
- two lines of metadata added per class to specify the database table.
That is what is the 'competition' is for this kind of functionality in a
typical Java development environment. Much as I like the REP loop, I
don't believe that for commercial scale development lispers don't use
build scripts. The difference being that the scripts are in the same
language, and the metadata is in the same syntax, and available at
runtime in a consistent manner.
Ultimately, as frameworks for binding Java classes to DBs are at an
advanced state of functionality, and have all the transaction queuing
etc. sorted, I think the case for lisp in this comes down to:
- one step compile has real advantage over separate build steps in a
script
- one syntax for metadata, build script and program better than
distinct syntaxes
and maybe:
Metadata available in consistent manner at runtime
Though for the simple 'persist to db' cases the metadata isn't required.
As for 'ease of use to a typical programmer who can use the available
tools', 'number of lines of code to do it', or 'impact when retro
fitting to existing app' don't seem to be where you can gain in this use
case.
Pete
In article <························@news-text.dial.pipex.com>, Pete
Kirkham <·················@cafemosaic.co.uk> wrote:
> Erann Gat wrote:
> > My goal here is not only to make something functional, but also to
> > highlight the power of CLOS with something that will really knock the
> > socks off the Java people, so I really want to do this the Right Way.
> As a lisp liking Java person, here are the points you need to address to
> de-sock a Java-head:
>
> Remember in your comparison that the 'Java way' is not REP but making
> the program from source files with a build utility, often integrated
> with an IDE.
Yes, there is a long-standing tension between two fundamentally different
philosophies about how code should be written. The first philosophy is
that you should have two distinct phases, design and coding, and that
design should be complete before coding begins. The second is that design
and coding should be continually blended in what is often termed
"exploratory programming" or "rapid prototyping". The argument against
the design-first-code-second philosophy is that requirement creep and
other nasty surprises are inevitable, and that these are responsible for
the chronic schedule and budget overruns that are notorious in the
industry. The argument against exploratory programming is that if you
don't plan ahead you are essentially hill-climbing and will inevitably end
up with a sub-optimal solution. This was all discussed at length here a
few months ago.
This becomes a dichotomy only if you believe that programmers are by
nature so undisciplined that the only way to keep from falling into the
hill-climbing trap is to deny them the ability to make changes easily. If
you believe that then you will never see Lisp as a good thing because
providing the ability to make changes easily is the whole point. But I am
not interested in arguing about Lisp vs. Java on Java's terms. Of course
Lisp will lose if you start with the premise that its central feature is
in fact a bug. But I think that there are people who will look at that
page-long explanation of all you have to do to bind Java objects to
databases and compare that to:
(defclass foo (storable) ...)
(store (make-instance foo))
and start to entertain the idea that maybe there is a point to having all
those irritating silly parentheses after all.
E.
Erann Gat wrote:
> Yes, there is a long-standing tension between two fundamentally different
> philosophies about how code should be written. The first philosophy is
> that you should have two distinct phases, design and coding, and that
> design should be complete before coding begins.
This is irrelevant to the edit-build-run loop vs the REP loop; you can
design then code lisp if you choose, you can (with an iterative cycle of
milliseconds) edit-build-run Java. Faster machines, incremental
compilers, and hot-swapping classes whilst debugging make iterative
development with compiled languages the norm. I find that once you are
dealing with over a hundred lines of code you have to use an editor for
lisp too, so the difference today is do I drag&drop (load "foo.lisp")
into the rep loop or hit the recompile-and-run button.
[Java head off:
That said, when I'm doing 'explorative programming' rather than
'iterative development' ie at the beginning of a problem making a spike
solution with a most a few hundred lines of code, lisp wins every time.
But for that problems that size, you don't care about DB persistence.]
> But I think that there are people who will look at that
> page-long explanation of all you have to do to bind Java
> objects to databases and
(in your argument to those you wish to convince) Summarize the tools
available for metadata based programming in a typical commercial lisp
environment, the existing frameworks for DB persistence, and the way
such frameworks interact with the environment. Is it less than a page long?
> compare that to:
>
> (defclass foo (storable) ...)
>
> (store (make-instance foo))
>
> and start to entertain the idea that maybe there is a point to having all
> those irritating silly parentheses after all.
==>
Error: While computing the class precedence list of the class
named STORABLE.
Are you sure you don't need to have any libraries or frameworks imported
into your development environment like I explained you do in Java? ;-)
It seems that once per project you will need to tell your lisp
environment to include the framework you're designing, which is no more
or less painful with Java environments, and tell each of your storable
classes which tables to store what slots to. If you only allow the
facility to store to the table with the name of the class, to store all
slots, and to fields with the same name as the slots, then your
persistence mechanism breaks each time you refactor your classes, and
you break the capability for iterative development. You won't get Java
people to switch to a mechanism that takes that away from them- it's the
key thing that Java sells itself on. That is _why_ the Java version is
more verbose, by including attributes that set the table mapping- so
that you can do iterative development.
Assuming you have your framework installed and your environment set up
as previously described, then you have only need add the single
@hibernate.class tag to the class to enable persistence, and the rest of
_code_ you have to add to use it is describing the mapping of classes to
tables and java fields to db fields. So the benefit, by the above
example, is that with lisp it takes 8 less characters per class- the
difference between typing 'storable' and ·@hibernate.class[return]'.
That really isn't a big gain. Maybe a second of developer time for the
typing, with the same amount of cognative effort per class whatever the
framework. Clients will spend far more time deciding on the mapping than
typing the code.
This is not to say that doing transparent persistence using a lisp MOP
won't take you less time to develop than it took the developers to build
the Java tools (as you have the metadata accessible directly, and the
:before and :after built in rather than having to write parsers and byte
code manipulation routines to add it), nor that there is a theoretical
cleanness in having it all in the one environment rather than using
scripting tools, frameworks, postprocessors etc. But IMO this seems a
case where a framework built on top of lisp can only be shown to be as
good as, not better than, the existing frameworks available.
Are you selling this to the framework writers or the application
writers? If the former, then the ease of development for the framework
is significant. If you are selling lisp on the facilities to extend the
language then it wins. If you are using those facilities to create a
framework that has the same functionality as other frameworks in other
languages, then the judgement is made on the functionality of the
framework, and it's at best going to be a dead heat.
Using either the hypothetical lisp library or the extant Java tools, the
application developer has to set up the environment once per project,
type one word per class to mark it as storable, and specify the
mappings. The differences in syntax are insignificant, as most the
effort is deciding the mappings, not typing them.
In a commercial environment where things like DB persistence matter,
convincing someone to change to something that is only as-good-as the
current frameworks (that have many thousands of users, so are proven,
and easier recruitment of people with experience, etc., etc.), you'll
find it's better the devil you know. If you are selling to framework
providers, then they will want to create frameworks that integrate with
the most client developer's toolsets (unless they are intentionally
targetting a niche market), which again means lisp loses.
I really don't think this is a killer app for lisp.
My first post-student job was reimplementing an application based on a
persistant object DB from lisp in C++ as it didn't meet performance
expectations on late 386 based PCs. As it panned out, most of the
development time ended up creating a scripting language that allowed the
end users to customize it. That sort of customizability thing is much
closer to killer lisp than a persistence framework, which costs the app
developer about the same effort in either lisp or Java to use. The same
applies to the language manipulation tools and cross compilers I work on
now- lisp has the better features for these applications.
If you want to write a persistence framework in lisp, do so; just do it
because you think it will be something worthwhile in and of itself (or
you need the money and can persuade someone to pay you for it). Doing it
to impress (or depress) developers who use other languages (often
because they have to for reasons beyond their control) doesn't seem a
good thing.
Pete
In article <························@news-text.dial.pipex.com>, Pete
Kirkham <·················@cafemosaic.co.uk> wrote:
> > (defclass foo (storable) ...)
> >
> > (store (make-instance foo))
> >
> > and start to entertain the idea that maybe there is a point to having all
> > those irritating silly parentheses after all.
>
> ==>
> Error: While computing the class precedence list of the class
> named STORABLE.
>
> Are you sure you don't need to have any libraries or frameworks imported
> into your development environment like I explained you do in Java? ;-)
Well, of course you'd have to have the code loaded. Duh!
Where's Erik when we need him?
E.
·········@jpl.nasa.gov (Erann Gat) writes:
>
> Where's Erik when we need him?
>
> E.
Did not you help drive him off when he came back
a few months ago?
marc
In article <··············@bogomips.optonline.net>, Marc Spitzer
<········@optonline.net> wrote:
> ·········@jpl.nasa.gov (Erann Gat) writes:
>
> >
> > Where's Erik when we need him?
> >
> > E.
>
> Did not you help drive him off when he came back
> a few months ago?
How would I know? I have no particular insight into his motivations, and
he doesn't confide in me. I note in passing that he announced his
departure in a thread in which I was not participating, so I think it's
possible that the answer to your question could in fact be "no".
Not that any of this matters.
E.
> In article <··············@bogomips.optonline.net>, Marc Spitzer
> <········@optonline.net> wrote:
>
>
>>·········@jpl.nasa.gov (Erann Gat) writes:
>>
>>
>>>Where's Erik when we need him?
>>>
>>>E.
>>
>>Did not you help drive him off when he came back
>>a few months ago?
No, that was Barry, but only as the proximate cause. The ultimate causes
were c.l.l. being bad for Erik's health, and Erik being bright enough to
figure that out. He should try again every year, tho.
Erann did nothing other than be himself, and that is one of his charms.
I consider him the second constant of the universe, after the speed of
light in a vacuum.
kt
--
Home? http://tilton-technology.com
Cells? http://www.common-lisp.net/project/cells/
Cello? http://www.common-lisp.net/project/cello/
In article <······················@twister.nyc.rr.com>, Kenny Tilton
<·······@nyc.rr.com> wrote:
> > In article <··············@bogomips.optonline.net>, Marc Spitzer
> > <········@optonline.net> wrote:
> >
> >
> >>·········@jpl.nasa.gov (Erann Gat) writes:
> >>
> >>
> >>>Where's Erik when we need him?
> >>>
> >>>E.
> >>
> >>Did not you help drive him off when he came back
> >>a few months ago?
>
> No, that was Barry, but only as the proximate cause. The ultimate causes
> were c.l.l. being bad for Erik's health, and Erik being bright enough to
> figure that out. He should try again every year, tho.
>
> Erann did nothing other than be himself, and that is one of his charms.
> I consider him the second constant of the universe, after the speed of
> light in a vacuum.
Erik also did nothing other than to be himself. Likewise for Marc, Barry,
Pascal, and even you, Kenny.
Not least among the many ironies of the recurring internecine sniping on
c.l.l. is that all the participants are so much alike.
E.
Erann Gat wrote:
> In article <······················@twister.nyc.rr.com>, Kenny Tilton
> <·······@nyc.rr.com> wrote:
>>Erann did nothing other than be himself, and that is one of his charms.
>>I consider him the second constant of the universe, after the speed of
>>light in a vacuum.
>
>
> Erik also did nothing other than to be himself.
Wrong, in the sense I meant. Anyone paying attention could see a
different approach to Usenet, though some things carried over. The
brusque treatment of a newby was an unfortunate classic, but other
things were a sea change in tactics. Hell, he even offered me public
advice here on how I could deal better with you clowns, a moment I rank
with having John McCarthy use my laptop at ILC 2003 (have I mentioned
that? recently, I mean) and Paul Graham sending me a (buggy!) tip* on
soccer defense for my RoboCup team.
kenny the groupie
* "The defensive player simply always stays between the ball and the goal."
--
Home? http://tilton-technology.com
Cells? http://www.common-lisp.net/project/cells/
Cello? http://www.common-lisp.net/project/cello/
In article <······················@twister.nyc.rr.com>,
Kenny Tilton <·······@nyc.rr.com> wrote:
> Hell, he even offered me public
> advice here on how I could deal better with you clowns, a moment I rank
> with having John McCarthy use my laptop at ILC 2003 (have I mentioned
> that? recently, I mean) and Paul Graham sending me a (buggy!) tip* on
> soccer defense for my RoboCup team.
>
> kenny the groupie
>
> * "The defensive player simply always stays between the ball and the goal."
kenny the groupie, if these are the kinds of tips that
apply to RoboCup, there are more to be found on the
basketball courts and soccer fields where 4-8 year old
children play. I coach my young daughters and their
classmates, and it's amazing how this forces you to
articulate every rule and heuristics which you, as a
skilled player, long ago internalized -- automatized and
forgot. Less than three weeks ago I was admonishing
my kindergarten team to "stay between your girl and
the goal" while playing defense. Well, not admonishing,
but yelling at the top of my lungs from the sideline.
According to Sashank Varma <····@vanderbilt.edu>:
> In article <······················@twister.nyc.rr.com>,
> Kenny Tilton <·······@nyc.rr.com> wrote:
> > * "The defensive player simply always stays between the ball and the goal."
>
> Less than three weeks ago I was admonishing
> my kindergarten team to "stay between your girl and
> the goal" while playing defense. Well, not admonishing,
> but yelling at the top of my lungs from the sideline.
There is no offside rule in kindergarten and computer football?
--
Ng Pheng Siong <····@netmemetic.com>
http://firewall.rulemaker.net -+- Firewall Change Management & Version Control
http://sandbox.rulemaker.net/ngps -+- Open Source Python Crypto & SSL
Ng Pheng Siong wrote:
> According to Sashank Varma <····@vanderbilt.edu>:
>
>>In article <······················@twister.nyc.rr.com>,
>> Kenny Tilton <·······@nyc.rr.com> wrote:
>>
>>>* "The defensive player simply always stays between the ball and the goal."
>>
>>Less than three weeks ago I was admonishing
>>my kindergarten team to "stay between your girl and
>>the goal" while playing defense. Well, not admonishing,
>>but yelling at the top of my lungs from the sideline.
Ah, fond memories of going "Bobby Knight"* on Rita Amicucci, actually
kicking a chair when five seconds after being admonished to stick like
glue to her opponent I located her some fifty feet away from her
opponent who was scoring a lay-up**.
I swear to god the opposing coach came over to try to calm me down. :)
> There is no offside rule in kindergarten and computer football?
Or professional football. It is always a judgment call, so if Rinaldo
were offsides but over at the sideline, back to the goal, and crying for
his Mommy, the ref would not make the call. Or so I hear.
kt
* American basketball coach who once threw a chair across a basketball
court, choked a student for perceived rudeness, and generally was a
poster boy for high blood pressure.
** Easy two points in basketball.
--
Home? http://tilton-technology.com
Cells? http://www.common-lisp.net/project/cells/
Cello? http://www.common-lisp.net/project/cello/
Why Lisp? http://alu.cliki.net/RtL%20Highlight%20Film
Your Project Here! http://alu.cliki.net/Industry%20Application
From: Barry Wilkes
Subject: Re: Who Killed Whom?
Date:
Message-ID: <fzc0aukn.fsf@acm.org>
Kenny Tilton <·······@nyc.rr.com> writes:
<snip>
> ...
> laptop at ILC 2003 (have I mentioned that? recently, I mean) and Paul Graham
> sending me a (buggy!) tip* on soccer defense for my RoboCup team.
>
> kenny the groupie
>
> * "The defensive player simply always stays between the ball and the goal."
>
Pah! Americans and football! Lookup the Offside rule. Study 'The Arsenal
Back-Four' (until fairly recently anyway).
Tony Adams should clearly spend his retirement helping Kenny.
;)
Barry.
Barry Wilkes wrote:
> Kenny Tilton <·······@nyc.rr.com> writes:
> <snip>
>
>>...
>>laptop at ILC 2003 (have I mentioned that? recently, I mean) and Paul Graham
>>sending me a (buggy!) tip* on soccer defense for my RoboCup team.
>>
>>kenny the groupie
>>
>>* "The defensive player simply always stays between the ball and the goal."
>>
>
> Pah! Americans and football! Lookup the Offside rule. Study 'The Arsenal
> Back-Four' (until fairly recently anyway).
<heh-heh> You guys all missed the bug! And it has nothing to do with
offsides rules. I'll give you one more try.
kt
--
Home? http://tilton-technology.com
Cells? http://www.common-lisp.net/project/cells/
Cello? http://www.common-lisp.net/project/cello/
Kenny Tilton <·······@nyc.rr.com> wrote in message news:<······················@twister.nyc.rr.com>...
> <heh-heh> You guys all missed the bug! And it has nothing to do with
> offsides rules. I'll give you one more try.
Hmm, the the defensive player should stay in between IF POSSIBLE?
A defensive player should occasionally go on offense, such as near the
end when losing?
I'm assuming soccer is anything like hockey, which I do like.
Tayssir John Gabbour wrote:
> Hmm, the the defensive player should stay in between IF POSSIBLE?
>
> A defensive player should occasionally go on offense, such as near the
> end when losing?
No, you are looking for a great, honking bug, not at the level of
unsound soccer strategy ("the best defense is a good offense"), but
rather at the level of ("oh, jeez, hang on...") if this advice were to
be followed:
"when the other team has the ball, your players should simply stay
between the ball and their own goal"
btw, i am also not talking about that formulation leaving unspecified
how to determine purely by sight that "the other team has the ball".
kt
--
Home? http://tilton-technology.com
Cells? http://www.common-lisp.net/project/cells/
Cello? http://www.common-lisp.net/project/cello/
Why Lisp? http://alu.cliki.net/RtL%20Highlight%20Film
Your Project Here! http://alu.cliki.net/Industry%20Application
Kenny Tilton <·······@nyc.rr.com> wrote in message news:<······················@twister.nyc.rr.com>...
> No, you are looking for a great, honking bug, not at the level of
> unsound soccer strategy ("the best defense is a good offense"), but
> rather at the level of ("oh, jeez, hang on...") if this advice were to
> be followed:
Hmm, I guess that rules out guarding BOTS instead of just the ball?
Because you want to obstruct potential lines to goal (and passing
lanes) instead of the current line to goal.
Kenny Tilton <·······@nyc.rr.com> writes:
> Tayssir John Gabbour wrote:
>
> > Hmm, the the defensive player should stay in between IF POSSIBLE?
> > A defensive player should occasionally go on offense, such as near
> > the
> > end when losing?
>
> No, you are looking for a great, honking bug, not at the level of
> unsound soccer strategy ("the best defense is a good offense"), but
> rather at the level of ("oh, jeez, hang on...") if this advice were to
> be followed:
>
> "when the other team has the ball, your players should simply stay
> between the ball and their own goal"
When the ball is at all near the goal, the location of "the goal"
is pretty poorly defined. Naive implementation of this rule could
lead to the edges of the goal being undefended.
The goal extends into the pitch. If the ball (with an attacking
player) is "past the goalmouth" then there is no point in having
defending players between the ball and the goal, since it can't
get in directly.
If all your players are independently trying to get between the
ball and the goal then they will collide with each other and end up
in a heap.
I don't believe any of these is what you're looking for :-).
--
Gareth McCaughan
.sig under construc
Gareth McCaughan wrote:
> Kenny Tilton <·······@nyc.rr.com> writes:
>
>
>>Tayssir John Gabbour wrote:
>>
>>
>>>Hmm, the the defensive player should stay in between IF POSSIBLE?
>>>A defensive player should occasionally go on offense, such as near
>>>the
>>>end when losing?
>>
>>No, you are looking for a great, honking bug, not at the level of
>>unsound soccer strategy ("the best defense is a good offense"), but
>>rather at the level of ("oh, jeez, hang on...") if this advice were to
>>be followed:
>>
>> "when the other team has the ball, your players should simply stay
>>between the ball and their own goal"
>
>
> When the ball is at all near the goal, the location of "the goal"
> is pretty poorly defined.
I think being on the bisector of the angle from the ball and between the
goalposts is optimal. that's where I had the goalie go, anyway.
Naive implementation of this rule could
> lead to the edges of the goal being undefended.
>
> The goal extends into the pitch. If the ball (with an attacking
> player) is "past the goalmouth" then there is no point in having
> defending players between the ball and the goal, since it can't
> get in directly.
>
> If all your players are independently trying to get between the
> ball and the goal then they will collide with each other and end up
> in a heap.
Possibly, but they will certainly end up in a line from the ball to the
goal.
>
> I don't believe any of these is what you're looking for :-).
>
I am giving partial credit for the heap of players answer.
:)
kt
--
Home? http://tilton-technology.com
Cells? http://www.common-lisp.net/project/cells/
Cello? http://www.common-lisp.net/project/cello/
Why Lisp? http://alu.cliki.net/RtL%20Highlight%20Film
Your Project Here! http://alu.cliki.net/Industry%20Application
Kenny Tilton wrote:
>>> * "The defensive player simply always stays between the ball and the
>>> goal."
> <heh-heh> You guys all missed the bug! And it has nothing to do with
> offsides rules. I'll give you one more try.
I'll bite.
- the goal is the defenders goal
- there is (hopefully) more than one defensive player
- if the ball is free, the defender should run towards
the ball to gain control of it
- what happens if the defenders goalie has the ball?
- the player is slower than the ball leading to:
Attacker1 ---ball---> Attacker2
---Defender--->
X
goal
While the ball move from attacker 1 to attacker 2,
attacker 1 runs towards and the goal and is free
to receive the ball from attacker 2 at the spot
marked X. There is now a possibility for a free shot.
--
Jens Axel S�gaard
Jens Axel S�gaard wrote:
> Kenny Tilton wrote:
>
>>>> * "The defensive player simply always stays between the ball and the
>>>> goal."
>
>
>> <heh-heh> You guys all missed the bug! And it has nothing to do with
>> offsides rules. I'll give you one more try.
>
>
> I'll bite.
fyi, i could not make out if the following was a scenario constituting
one guess or separate guesses. They look separate.
>
> - the goal is the defenders goal
Did you mean "attacker's"? No, it is not a trick question. "The other
team has the ball, my players should stay between the ball and the goal
they defend" was his advice.
>
> - there is (hopefully) more than one defensive player
Correct solution ingredient #1!
>
> - if the ball is free, the defender should run towards
> the ball to gain control of it
Maybe if they are a few yards away, but the shortest distance to a line
is a perpendicular to that line, so if there are big distances involved
they get between the ball and the goal faster by running to the line,
not the ball.
Anyone starting to see why RoboCup was so much fun?
>
> - what happens if the defenders goalie has the ball?
Then Paul's rule flips and applies to the other team. And again, this
was a solid bug, not a trick one.* I understood Paul meant that the
attackers had crossed midfield.
This is not to say I was not guilty of bugs at this so-called trick
level, just that in this case the bug I am after does not rely on
forgetting that a goalie is a player or something of that ilk.
>
> - the player is slower than the ball leading to:
>
> Attacker1 ---ball---> Attacker2
>
>
> ---Defender--->
>
> X
Correct solution ingredient #2! Tho this in its own right is indeed a
problem because the soccer server tracks player exertion and applies a
fatigue factor before executing player commands.
Mix, stir, bake and....?
kt
--
Home? http://tilton-technology.com
Cells? http://www.common-lisp.net/project/cells/
Cello? http://www.common-lisp.net/project/cello/
Why Lisp? http://alu.cliki.net/RtL%20Highlight%20Film
Your Project Here! http://alu.cliki.net/Industry%20Application
From: Marco Antoniotti
Subject: Re: All your fullbacks are belong to us [was Re: Who Killed Whom?]
Date:
Message-ID: <HK38c.123$IJ5.110187@typhoon.nyu.edu>
It is well known that you need one attacker (usually no. 9) one goalie
and 9 defenders in soccer. I do not understand all the complications
you are putting up.
Cheers
Marco
PS. Of course it helps if the attacker is named Rossi or Schillaci or
Vieri or Totti and he is in a state of grace the day of the match :)
Kenny Tilton wrote:
>
>
> Jens Axel S�gaard wrote:
>
>> Kenny Tilton wrote:
>>
>>>>> * "The defensive player simply always stays between the ball and
>>>>> the goal."
>>
>>
>>
>>> <heh-heh> You guys all missed the bug! And it has nothing to do with
>>> offsides rules. I'll give you one more try.
>>
>>
>>
>> I'll bite.
>
>
> fyi, i could not make out if the following was a scenario constituting
> one guess or separate guesses. They look separate.
>
>>
>> - the goal is the defenders goal
>
>
> Did you mean "attacker's"? No, it is not a trick question. "The other
> team has the ball, my players should stay between the ball and the goal
> they defend" was his advice.
>
>>
>> - there is (hopefully) more than one defensive player
>
>
> Correct solution ingredient #1!
>
>>
>> - if the ball is free, the defender should run towards
>> the ball to gain control of it
>
>
> Maybe if they are a few yards away, but the shortest distance to a line
> is a perpendicular to that line, so if there are big distances involved
> they get between the ball and the goal faster by running to the line,
> not the ball.
>
> Anyone starting to see why RoboCup was so much fun?
>
>>
>> - what happens if the defenders goalie has the ball?
>
>
> Then Paul's rule flips and applies to the other team. And again, this
> was a solid bug, not a trick one.* I understood Paul meant that the
> attackers had crossed midfield.
>
> This is not to say I was not guilty of bugs at this so-called trick
> level, just that in this case the bug I am after does not rely on
> forgetting that a goalie is a player or something of that ilk.
>
>
>>
>> - the player is slower than the ball leading to:
>>
>> Attacker1 ---ball---> Attacker2
>>
>>
>> ---Defender--->
>>
>> X
>
>
> Correct solution ingredient #2! Tho this in its own right is indeed a
> problem because the soccer server tracks player exertion and applies a
> fatigue factor before executing player commands.
>
> Mix, stir, bake and....?
>
> kt
>
From: Coby Beck
Subject: Re: All your fullbacks are belong to us [was Re: Who Killed Whom?]
Date:
Message-ID: <c3qd6b$2hfi$1@otis.netspace.net.au>
> > Kenny Tilton wrote:
> >
> >>>> * "The defensive player simply always stays between the ball and the
> >>>> goal."
> >
> >
> >> <heh-heh> You guys all missed the bug! And it has nothing to do with
> >> offsides rules. I'll give you one more try.
> >
> Mix, stir, bake and....?
>
> kt
...and you have your typical elementary school soccer game! A great big
honking crowd of players *all together* running around in a group after the
ball instead of playing their opponents and their positions.
--
Coby Beck
(remove #\Space "coby 101 @ big pond . com")
Coby Beck wrote:
>>>Kenny Tilton wrote:
>>>
>>>
>>>>>>* "The defensive player simply always stays between the ball and the
>>>>>>goal."
>>>
>>>
>>>><heh-heh> You guys all missed the bug! And it has nothing to do with
>>>>offsides rules. I'll give you one more try.
>>>
>>Mix, stir, bake and....?
>>
>>kt
>
>
> ...and you have your typical elementary school soccer game! A great big
> honking crowd of players *all together* running around in a group after the
> ball instead of playing their opponents and their positions.
Pretty close. I wrote back, "Doesn't that mean all my players end up in
a /line/ between the ball and the goal?". So he sent along a patch. I
think it was to stay between the player you are marking and the goal.
And, yes, TeamKenny played a lot of kindergarten soccer, and when I
tried to teach them zone play, oh my....
:)
kt
--
Home? http://tilton-technology.com
Cells? http://www.common-lisp.net/project/cells/
Cello? http://www.common-lisp.net/project/cello/
Why Lisp? http://alu.cliki.net/RtL%20Highlight%20Film
Your Project Here! http://alu.cliki.net/Industry%20Application
·········@jpl.nasa.gov (Erann Gat) writes:
> Not least among the many ironies of the recurring internecine sniping on
> c.l.l. is that all the participants are so much alike.
Well, I'm no rocket scientist.
--
It would not be too unfair to any language to refer to Java as a
stripped down Lisp or Smalltalk with a C syntax.
--- Ken Anderson
http://openmap.bbn.com/~kanderso/performance/java/index.html
Erann Gat wrote:
> In article <························@news-text.dial.pipex.com>, Pete
> Kirkham <·················@cafemosaic.co.uk> wrote:
>>Are you sure you don't need to have any libraries or frameworks imported
>>into your development environment like I explained you do in Java? ;-)
>
> Well, of course you'd have to have the code loaded. Duh!
So, having established that you need to do at least as much as what you
have to do in Java (set up environment, define mappings if not using
default), where is the big advantage?
A system that only allows mapping to a fresh DB isn't going to impress
anyone using Java, when one of the principle areas of application of
Java is putting front ends onto legacy business systems.
> Where's Erik when we need him?
Could you explain the reference?
Pete
In article <························@news-text.dial.pipex.com>, Pete
Kirkham <·················@cafemosaic.co.uk> wrote:
> Erann Gat wrote:
>
> > In article <························@news-text.dial.pipex.com>, Pete
> > Kirkham <·················@cafemosaic.co.uk> wrote:
> >>Are you sure you don't need to have any libraries or frameworks imported
> >>into your development environment like I explained you do in Java? ;-)
> >
> > Well, of course you'd have to have the code loaded. Duh!
>
> So, having established that you need to do at least as much as what you
> have to do in Java (set up environment, define mappings if not using
> default), where is the big advantage?
The advantage is that to do all that that took you a whole page of text to
describe is done in Lisp by:
(require 'gat-object-store)
> > Where's Erik when we need him?
>
> Could you explain the reference?
No. But I'm sure someone else will.
E.
Erann Gat wrote:
> The advantage is that to do all that that took you a whole page of text to
> describe is done in Lisp by:
>
> (require 'gat-object-store)
To a Java audience, I could have said is 'all that you do is install
XDoclet and hibernate, cut&paste the Ant tasks to call them, then add an
@hibernate.class doctag for each class you wish to map to the DB'. As
this is a lisp news group, I felt more detail appropriate.
My understanding of require is that there is a precondition that you
already have installed the required package in a place where your
environment can access it. So even in lisp there is still more to do
than you seem willing to admit.
It can take less that 15 minutes to set up XDoclet and Hibernate per
project. Compare that to the loss of functionality you propose by only
supporting one default mapping to a database, and it seems you will lose
your argument. Even if it takes your best developer a week to set up a
library, it is worth it in the really real world if the project gains
more functionality than that developer can create in that time.
If your only purpose is to impress a Java developer audience, why are
you not addressing the points I made? 'Duh' as a response to being
shown a counter example to your position isn't going to knock anyone's
socks off.
Someone posted a link to one of Eric's posts. Taking that as a lead, are
you implying that you can show that a one-man project that uses CLOS/MOP
can be calmly and rationally shown to better than the existing Java
solutions?
Taking your particular case, independent of the other arguments about
language elegance (lisp wins hands down), edit-compile-run vs REP (true
when you worked with punch cards, but had disappeared by the mid'90s
with incremental compile and better debuggers), and a belief that Java
is fixed to the waterfall development model (simply untrue), some things
you can compare on are functionality, cost, integration, reliability,
scalability, architecture.
Functionality:
The solution you are proposing has massively reduced functionality with
respect to the usecases that typical Java systems require.
A typical Java application that requires DB persistence will be a web
front end to an existing database. You can't use your solution for that.
Cost:
Once placed in a location accessible to the environment, the solution
you are proposing can be installed easily with single command, instead
of taking maybe half an hour for the Java version.
The cost per class is about the same- typing 'storable' vs.
·@hibernate.class'
Integration:
The solution you are proposing transparently integrates with the lisp
environment. It does not integrate with business infrastructures, such
as existing databases.
The existing Java solutions (well, hibernate anyway) require an explicit
update command. They can be configured to operate with existing business
infrastructures.
Reliability:
There's always FUD about anything that is different. Hard to measure.
Scalability:
Hitting a DB for every slot access kills performance. Once you have
start caching your transactions, it may well be that the naive approach
fails. If you restrict yourself to only the information available
through the MOP, without providing any mechanism for the user to
customize the mapping and the caching, then it is very unlikely your
scheme will scale. Once you realise that you don't want overhead on
every access, maybe transparency isn't that important.
Architecture:
Java applications separate persistence mechanisms into a different layer
to the type hierarchy which encapsulates the business logic.
If you are arguing with people familiar with layered architectures and
unfamiliar with the CLOS style of OO, then you need to explain why you
are polluting the type space with orthogonal concerns. There is no such
thing as a mixin in Java; this was intentional, not an omission.
I've already replied with application areas that IMO lisp does 'win'
over less dynamic environments, but yet another lisp programmer showing
how to make cool toys isn't going to win any serious software engineer
over.
Lisp can be better than Java, but to convince people you have to address
their concerns, not your own. No-one programs in Java if they believe
language-level elegance is more important that practicality. Concorde
was more elegant and capable on its own terms than any other airliner,
but it couldn't survive in the market.
Pete
In article <························@news-text.dial.pipex.com>, Pete
Kirkham <·················@cafemosaic.co.uk> wrote:
> If your only purpose is to impress a Java developer audience
But it isn't. That isn't even my main purpose, let alone my only purpose.
> why are you not addressing the points I made?
Because the points you made are all non-sequiturs, as they are all based
on incorrect assumptions. (Well, they weren't all non-sequiturs. At
least one of them, that you'd get an error if you tried to use the
software without loading it, was just plain stupid. I pretty much tuned
out at that point.)
My primary purpose is to build a tool for a particular need that I have
for which I have not found anything adequate off the shelf. I have found
in building this tool that it is turning out to be easier than I expected
(and I wasn't expecting it to be particularly hard) and that has caused me
to up-scope a little to include the possibility of turning it into a
little showcase piece. But I don't expect anyone to be impressed by what
it does, because it isn't going to do anything that you can't do in Java
(or anything else for that matter) by simply doing X, Y and Z. But the
point, of course, will be that you don't have to do X, Y and Z. Some
people will get it, and some people won't, and I have absolutely no
interest in arguing with the latter (especially not here on c.l.l.). I
have finally learned what an old mentor of mine meant when he once told
me, "Never try to teach a pig to sing. It wastes your time and annoys the
pig."
E.
"Erann Gat" <·········@jpl.nasa.gov> wrote in message
·······························@192.168.1.51...
> In article <························@news-text.dial.pipex.com>, Pete
> Kirkham <·················@cafemosaic.co.uk> wrote:
>
> > If your only purpose is to impress a Java developer audience
>
> But it isn't. That isn't even my main purpose, let alone my only purpose.
>
...
> But I don't expect anyone to be impressed by what
> it does, because it isn't going to do anything that you can't do in Java
> (or anything else for that matter) by simply doing X, Y and Z.
...
"Never try to teach a pig to sing. It wastes your time and annoys the
> pig."
>
> E.
Come off it. In your post that started this thread you said:
"My goal here is not only to make something functional, but also to
highlight the power of CLOS with something that will really knock the
socks off the Java people, so I really want to do this the Right Way."
People tried to tell you what you had to do to knock the socks off the Java
people because that's what you said you wanted to do. You found it was hard,
now you don't want to do it, and because of that we are pigs?
Where is Mike (Godwin) when you need him?
"a" <···@def.gh> writes:
> Come off it. In your post that started this thread you said:
>
> "My goal here is not only to make something functional, but also to
> highlight the power of CLOS with something that will really knock the
> socks off the Java people, so I really want to do this the Right Way."
Yeah, he did say that. But he did not post a complete product spec.
Who doesn't leave out information from time to time in usenet posts?
> People tried to tell you what you had to do to knock the socks off the Java
> people because that's what you said you wanted to do. You found it was hard,
> now you don't want to do it, and because of that we are pigs?
I don't think there is a reason to be offended here. I'm sure he
ment that Java people are pigs metaphorically. I doubt he expects
any Java people to be made into bacon.
> Where is Mike (Godwin) when you need him?
I think he made some reference to Hitler and then disappeared.
--
Those who do not remember the history of Lisp are doomed to repeat it,
badly.
> (dwim x)
NIL
In article <·····················@sea-read.news.verio.net>, "a"
<···@def.gh> wrote:
> "Erann Gat" <·········@jpl.nasa.gov> wrote in message
> ·······························@192.168.1.51...
> > In article <························@news-text.dial.pipex.com>, Pete
> > Kirkham <·················@cafemosaic.co.uk> wrote:
> >
> > > If your only purpose is to impress a Java developer audience
> >
> > But it isn't. That isn't even my main purpose, let alone my only purpose.
> >
> ...
> > But I don't expect anyone to be impressed by what
> > it does, because it isn't going to do anything that you can't do in Java
> > (or anything else for that matter) by simply doing X, Y and Z.
> ...
> "Never try to teach a pig to sing. It wastes your time and annoys the
> > pig."
> >
> > E.
>
> Come off it. In your post that started this thread you said:
>
> "My goal here is not only to make something functional, but also to
> highlight the power of CLOS with something that will really knock the
> socks off the Java people, so I really want to do this the Right Way."
>
> People tried to tell you what you had to do to knock the socks off the Java
> people because that's what you said you wanted to do. You found it was hard,
> now you don't want to do it, and because of that we are pigs?
Are you not familiar with the concept of metaphor?
Here's another piece of advice I'm going to follow now: never argue with
an anonymous troll. Goodbye, "a".
E.
Erann Gat wrote:
> But it isn't. That isn't even my main purpose, let alone my only purpose.
It was the purpose you stated for showing it to Java developers.
> Because the points you made are all non-sequiturs, as they are all based
> on incorrect assumptions. (Well, they weren't all non-sequiturs. At
> least one of them, that you'd get an error if you tried to use the
> software without loading it, was just plain stupid. I pretty much tuned
> out at that point.)
The point was that you have to do more than you say you were letting on
to use your system. You 'tuned it out' by making it the only point you
responded to.
I don't see that suggesting that people will not be impressed by tools
that do not solve _their_ problems an non-sequitur.
> I have finally learned what an old mentor of mine meant when he once told
> me, "Never try to teach a pig to sing. It wastes your time and annoys the
> pig."
Then I will take your advice and stop trying to teach you what my
experience is of the points you need to address when arguing for using
lisp in a Java shop. Instead, I suspect that when you showcase your
framework you will find that the audience remains unimpressed, as when
they raise the same points about business integration, functionality,
cost benefit, support for legacy systems and so on, you refuse to answer
them, and call them pigs.
Assuming your email indicates your place of work (mine is my 'home'
one), please don't do your showcasing at Nasa, as I work writing
aerospace system engineering r&d tools, sometimes on joint projects. I
would prefer not to have my implementation language preferences
restricted by bad experiences with arrogant twits who also use lisp.
It's a hard enough sell as it is, even when you do understand the
business needs that drive people to use Java.
Pete
In article <························@news-text.dial.pipex.com>, Pete
Kirkham <·················@cafemosaic.co.uk> wrote:
> The point was that you have to do more than you say you were letting on
> to use your system.
That you have to load software in order to use it is universally
understood. To point out that a program won't work if you haven't loaded
it not constructive, it's trolling.
> You 'tuned it out' by making it the only point you responded to.
Life is short.
> I don't see that suggesting that people will not be impressed by tools
> that do not solve _their_ problems an non-sequitur.
It's a non-sequitur because it's also self-evident. I already know that
some people will not be impressed by what I'm doing. That does not mean
that their views have any merit, it just means that they are not part of
my intended audience. Like I said, some people will get it, and some
people won't, and life is too short to worry about the latter. (And even
if the set of people who get it turns out to be empty then I'll still have
built a tool that meets *my* needs, which is my main goal here after all.)
> Assuming your email indicates your place of work (mine is my 'home'
> one), please don't do your showcasing at Nasa, as I work writing
> aerospace system engineering r&d tools, sometimes on joint projects. I
> would prefer not to have my implementation language preferences
> restricted by bad experiences with arrogant twits who also use lisp.
> It's a hard enough sell as it is, even when you do understand the
> business needs that drive people to use Java.
You mean, as opposed to bad experiences with people who call people they
don't know "arrogant twits"?
BTW, son, you might want to do a little homework before you lecture me
about selling Lisp to NASA.
E.
> BTW, son, you might want to do a little homework before you lecture me
> about selling Lisp to NASA.
Ok, I went and googled your CV.
I'd got the impression from your first post that you didn't have much
lisp experience, and you were in the 'kid with a new toy' phase and
going to show it off to some colleagues who were using the current state
of Java tools to talk to databases. I'm sorry I'd got the wrong
impression, and was talking at the wrong level, and in the wrong way.
I don't have a CV on-line, but I spent nearly a decade as a researcher
on kms tool support for aerospace applications at the University of
York, UK, and for the last few years in the Research&Tech dept. of BAE
Air Systems. Mix of technologies, lisp, C++, Java, ADA; some code
analysis, formal verification, and code generation of flight software
from models.
An area I'm currently working is taking systems engineering models-
ISO10303/AP233, together with various simulation languages, and
generating qualitative models for system health monitoring from them. I
know of no current use of lisp in NASA for this- everything seems to be
UML and XML, with OWL leanings; the only data-driven lisp project I was
aware of in the qualitative modelling field (Livingstone) has been
transitioned to C++.
Too many of the systems and tools I've seen are closed worlds to data-
the monitoring systems are hand crafted to implicitly capture one
system's model, not derived from the information already available in
the organisation. At best, there's a point and click GUI, at worst it's
all hard coded. Deriving the model, and verifying it, over and over
again for each tool or monitoring system is prohibitively expensive for
complex systems or variable configurations, so getting things to talk to
the organisation is the key selling point.
In the business Java&XML world, a similar driver exists with talking
with existing databases and applications in the organisation. It is what
most people use Java for, so for in most IDEs you can set it up in five
minutes using a wizard, then forget about it.
So I thought I was discussing with a naive lisper who don't 'get' that
the really big thing in the Java world is being able to talk to data
sources you don't control, and was going to end up looking like a twit;
and was playing 'gosling's advocate' to that position. Hopefully we'll
understand each other better in future.
Pete
In article <························@news-text.dial.pipex.com>, Pete
Kirkham <·················@cafemosaic.co.uk> wrote:
> > BTW, son, you might want to do a little homework before you lecture me
> > about selling Lisp to NASA.
> Ok, I went and googled your CV.
Actually, the relevant item for the matter at hand isn't on my CV.
> I'm sorry I'd got the wrong
> impression, and was talking at the wrong level, and in the wrong way.
Yes, and now you're apologising for the wrong thing. Rudeness is rudeness
irrespective of what happens to be on the CV of the person you're being
rude to.
Nonetheless, I'll accept the apology in the spirit in which (I assume) it
was offered. I'm sorry I called you "son".
> So I thought I was discussing with a naive lisper who don't 'get' that
> the really big thing in the Java world is being able to talk to data
> sources you don't control
Lisp is actually *vastly* better than Java when it comes to talking to
data sources you don't control. But that's not what I happen to be
working on right now. (To say nothing of the fact that I do not accept
your authority to speak on behalf of the entire Java user community.)
> and was going to end up looking like a twit;
Heh, I appreciate your concern. God knows there have been times when such
concerns have been warranted. But in this particular case you might want
to take a look in the mirror.
> and was playing 'gosling's advocate' to that position. Hopefully we'll
> understand each other better in future.
I'm pretty sure I understood you well enough. You're saying something
along the lines of "You can only impress Java programmers by doing X"
(where X seems to be something like "talking to data sources you don't
control"). The details don't matter because it is easy to see that you
are (almost certainly) wrong simply by the form of the claim. Java
programmers are too diverse (and research into such things is too sparse)
for such a claim to be plausible. It is therefore IMO not worthy of
serious consideration. Also, as I've now said many times, impressing
someone would be a nice bonus, but it's not the primary goal.
E.
Pete Kirkham wrote:
> Taking your particular case, independent of the other arguments about
> language elegance (lisp wins hands down), edit-compile-run vs REP (true
> when you worked with punch cards, but had disappeared by the mid'90s
> with incremental compile and better debuggers)...
Well, let's not get carried away. The mid-90's is exactly when I went on
my Dynamic-Or-Bust! search and by luck rolled to stop in a crater
containing Lisp.
Symantec C++ had abandoned the incremental linkage of Think C, and I was
looking at 30 seconds between runs, incremental compile notwithstanding.
Unacceptable. Especially since I have never met a line of (my own) code
I like, so I am always refactoring. (And in C that meant trouble if I
wanted to change a header in a precompiled header.) I would have to
propagate everything everywhere every time before ever seeing the
outcome. The existence of refactoring aids for static languages are just
a measure of how big a problem that is, not an advantage.
It is nice that Java has gotten better, but it does /not/ support
dynamic development the way a dynamic language does. That would be Groovy.
kt
--
Home? http://tilton-technology.com
Cells? http://www.common-lisp.net/project/cells/
Cello? http://www.common-lisp.net/project/cello/
Why Lisp? http://alu.cliki.net/RtL%20Highlight%20Film
Your Project Here! http://alu.cliki.net/Industry%20Application
From: Joe Marshall
Subject: Re: Java Is A Dynamic Language?
Date:
Message-ID: <fzc07fjx.fsf@ccs.neu.edu>
Kenny Tilton <·······@nyc.rr.com> writes:
> It is nice that Java has gotten better, but it does /not/ support
> dynamic development the way a dynamic language does. That would be
> Groovy.
Java combines the dynamicity of a batch compiler with the performance
of a byte-code interpreter. They *almost* got it.
Joe Marshall wrote:
> Kenny Tilton <·······@nyc.rr.com> writes:
>
>>It is nice that Java has gotten better, but it does /not/ support
>>dynamic development the way a dynamic language does. That would be
>>Groovy.
>
> Java combines the dynamicity of a batch compiler with the performance
> of a byte-code interpreter. They *almost* got it.
Since JDK 1.4, Java supports dynamic replacement of methods. If I
correctly understand the JDK 1.5 docs, the latter even supports dynamic
replacement of whole classes. These features are part of the debugging
APIs, and they are made use of in Java IDEs. I think Java programmers
have made use of this for quite some time by now.
They do not provide an update protocol yet. This means that existing
instances of a class will continue to adhere to the old definition of a
replaced class. For methods, currently executing methods are not
replaced. So only newly created instances and newly invoked methods are
affected. IIRC, there is some minimal support for breaking out of an
executing method and restarting it in order to make the new definition
effective, though.
More of these things seem to be available in Sun's research labs, but
they are quite nervous about these features. That's why they lock them
up in debugging APIs and make only some of them available, and only when
you have the right permissions. The flexibility of CLOS + MOP is by far
still not matched.
Nevertheless, incremental development with Java is possible nowadays.
The compiler provided with the Eclipse IDE, for example, even allows
executing programs that don't completely compile.
Pascal
--
1st European Lisp and Scheme Workshop
June 13 - Oslo, Norway - co-located with ECOOP 2004
http://www.cs.uni-bonn.de/~costanza/lisp-ecoop/
Joe Marshall wrote:
> Java combines the dynamicity of a batch compiler with the performance
> of a byte-code interpreter. They *almost* got it.
Or, as I saw it expressed somewhere or other:
Java. The elegant simplicity of C++. The blazing speed of Smalltalk.
--
Gareth McCaughan
.sig under construc
Kenny Tilton wrote:
> It is nice that Java has gotten better, but it does /not/ support
> dynamic development the way a dynamic language does. That would be Groovy.
>
> kt
No, it supports incremental development. The question got raised because
Erann Gat said it required all of your design to be fixed before
coding. It has a sufficient iteration time for cases where you're far
enough on for code-test-code, but for the initial exploration stage I
find lisp's zero time cycle better.
Pete
Pete Kirkham wrote:
> Kenny Tilton wrote:
>
>> It is nice that Java has gotten better, but it does /not/ support
>> dynamic development the way a dynamic language does. That would be
>> Groovy.
>>
>> kt
>
>
> No, it supports incremental development. The question got raised because
> ...
I was reacting simply to the mid-90s being identified as the time when
REPLs (not that I would characterize it that way, since I never touch
that window, but I think I know what you mean) lost their advantage over
(yes) the fine IDEs around static languages.
kt
--
Home? http://tilton-technology.com
Cells? http://www.common-lisp.net/project/cells/
Cello? http://www.common-lisp.net/project/cello/
Why Lisp? http://alu.cliki.net/RtL%20Highlight%20Film
Your Project Here! http://alu.cliki.net/Industry%20Application
Kenny Tilton wrote:
> I was reacting simply to the mid-90s being identified as the time when
> REPLs (not that I would characterize it that way, since I never touch
> that window, but I think I know what you mean) lost their advantage over
> (yes) the fine IDEs around static languages.
>
> kt
If I recall the dates correctly, SGI were demonstrating C++ with dynamic
recompile & eval of running code to university labs around '95.
Commercially, Microsoft Visual C++ version 5 had 'edit and continue',
which came out early '97, VisualCafe for Java, again '97, had dynamic
update of classes whilst debugging, using a custom virtual machine. The
feature was lost in Java after version 1.2, when Sun standardized the
debugging APIs, then came back a few years later with 1.4. Over 94-97
for the minor-edit-build cycle debugging that was a drag before then got
sufficiently fast that it became interactive. So maybe mid-to-late 90s
would be more accurate.
Pete
Pete Kirkham <·················@cafemosaic.co.uk> wrote in message news:<························@news-text.dial.pipex.com>...
> Commercially, Microsoft Visual C++ version 5 had 'edit and continue',
> which came out early '97
Edit and continue appeared in MSVC++ v6, and it was around 1998/99.
--
Eugene
I've heard of an interactive C++ development environment called Sabre
that was available in the late 80s. Also, wasn't Harlequin's non-Lisp
IDE (forget the name) for C++? That was the early 90s. The problem
with most of these approaches is that they're only available while
you're debugging in the IDE, and there's really no way to ship that
with finished applications (not that they couldn't come up with one,
but then it would be just like Lisp). This is a problem for graphics
applications - for example, Wavefront's Maya (written in C++) has an
interactive interpreter for a custom-designed extension language
called MEL. The interpreter isn't fast enough for many applications,
and since there's no compiler for MEL (I suspect this is because
Greenspun's 10th is a very expensive law to comply with) what happens
very frequently is that many extensions originally prototyped in MEL
have to be re-written in C++ (which are loaded as libraries). In
contrast, compiling extensions in Izware's Mirai is just compile-file
(if you've got the developer's kit, which comes with the Allegro CL
compiler). Which brings me to my next point: this interactive
functionality is available in almost half a dozen free CL
systems. C.l.l. trolls often complain that nobody gives them useful
stuff for nothing, but this is where Lisp has C/C++ beat - all those
interactive environments are pricey (and there's not that many
available anymore). There's really nothing similar for GCC (if there
is, please tell me about it - I really could use something like that
right now).
Vladimir
Vladimir Sedach <·································@cpsc.ucalgary.ca> writes:
> [dynamic IDEs] Also, wasn't Harlequin's non-Lisp
> IDE (forget the name) for C++? That was the early 90s.
ITYM Lucid's Energize. That was for C++. Harlequin didn't buy that
from Lucid, just the Lisp (which didn't have an IDE, but is now
available with (an old version of) the LW IDE as Liquid CL). Of
course, Harlequin did have a non-Lisp IDE as well: for Dylan -- but
obviously that was a very dynamic language from the start. Clear now?
--
Pekka P. Pirinen
Don't force it, get a larger hammer.
From: Lieven Marchand
Subject: Re: Java Is A Dynamic Language? [was Re: CLOS and databases]
Date:
Message-ID: <87vfkmvw9r.fsf@wyrd.be>
···············@globalgraphics.com (Pekka P. Pirinen) writes:
> ITYM Lucid's Energize. That was for C++. Harlequin didn't buy that
> from Lucid, just the Lisp (which didn't have an IDE, but is now
> available with (an old version of) the LW IDE as Liquid CL). Of
> course, Harlequin did have a non-Lisp IDE as well: for Dylan -- but
> obviously that was a very dynamic language from the start. Clear now?
Harlequin had a fairly nice port of their LW IDE for their
implementation of ML just before they became Xanalys. It made a very
static language like ML usable. I wonder what became of it. Did it
ever make it into a product or got bought by another company?
--
An amateur practices until he gets it right,
A professional practices until she can't get it wrong.
Lieven Marchand <···@wyrd.be> writes:
> Harlequin had a fairly nice port of their LW IDE for their
> implementation of ML just before they became Xanalys. It made a very
> static language like ML usable. I wonder what became of it. Did it
> ever make it into a product or got bought by another company?
Sadly, no. MLWorks was briefly available from Harlequin, but after
the company was reorganized, it was dropped and no one was interested
in taking it (unlike Harlequin Dylan, which was taken up by Functional
Objects, Inc., founded by key members of the Dylan team).
--
Pekka P. Pirinen
The great problem with Lisp is that it is just good enough to keep us
from developing something really good. - Alan Kay
Pete Kirkham <·················@cafemosaic.co.uk> writes:
> If your only purpose is to impress a Java developer audience, why are
> you not addressing the points I made? 'Duh' as a response to being
> shown a counter example to your position isn't going to knock anyone's
> socks off.
>
> ...
>
> Lisp can be better than Java, but to convince people you have to
> address their concerns, not your own. No-one programs in Java if they
> believe language-level elegance is more important that
> practicality. Concorde was more elegant and capable on its own terms
> than any other airliner, but it couldn't survive in the market.
>
I've experienced the frustration of loving Common Lisp but being forced
to write Java because (1) the client wants it or because (2) there are
lots and lots and lots of packages for Java that simply do not exist
in Common Lisp. Just try to google several combinations between
'Common Lisp _whatever_' and 'Java _whatever_' and watch the
difference. And the difference tends to get bigger. There are so
many Java programmers and so much money in the Java camp that is
really difficult to beat them with Common Lisp.
However, you can take advantage of (several) of the good Common Lisp
features and, at the same time, take advantage of the Java libraries
and frameworks that are being developed. My proposed solution is the
Linj language which is a Common Lisp-like language that is translated
into good-looking Java code (the "good-looking" part is only for Java
programmers :-). You can write Linj programs that take advantage of
whatever Java solutions you can get but you can also write syntactic
abstractions (I mean, Common Lisp macros) to hide all the mess.
Just to give you an example, let's take a look at the problem being
discussed (adding persistence to a program). How many solutions exist
for Common Lisp? Few and usually they are presented as research
papers. How many solutions in Java? Many and they are being used in
practice by 'normal' Java programmers.
Instead of reinventing the wheel in Common Lisp, Linj takes the
approach of using what is available "as is" but it allows you to make
it look nicer by using Common Lisp features such as macros, method
combination, generic arithmetic, etc.
Just to give you an example, I'll use the Prevayler approach to
persistence that someone suggested previously and I'll explain how to
use the Prevayler package in Linj. There are some demos at the
Prevayler site so I'll just pick the simplest one: the idea is to
create a program that computes prime numbers but that might be aborted
and restarted again at any time. Prevayler works by adding
persistence not to the program data but to the program actions. This
means that each action that modifies the state of the program must be
reified into the Command pattern (recent versions use the Transaction
interface), which is a PITA because it makes your code look ugly. In
Common Lisp, we use macros to hide patterns, so that's what we will do
here.
Here is the program: (I added a few obvious comments...)
----------------------------------------------------------------
(import org.prevayler.*) ;;We will use the prevayler package
(defun main ()
(let ((prime-keeper (new 'Vector))) ;;a generic Java container
(add prime-keeper 2)
(let ((prevayler ;;make it prevalent
(prevail prime-keeper "PRIMES"))
(prime-candidate ;;extract the last generated prime
(1+ (the long (last-element prime-keeper)))))
(loop
(when (prime-p prime-candidate)
;;make a transaction to save this prime
(keep-prime prevayler prime-candidate)
(format t "New prime found: ~A.~%" prime-candidate))
(incf prime-candidate)))))
;;The next function just tests if a given number is prime
(defun prime-p (number)
(declare (long number))
(if (or (<= number 2)
(zerop (rem number 2)))
nil
(let ((square (ceiling (sqrt number))))
(do ((factor 3 (+ factor 2)))
((> factor square) t)
(when (zerop (rem number factor))
(return nil))))))
;;The only missing part is the "transaction" keep-prime:
(deftransaction keep-prime ((store Vector) number-to-keep)
(add store number-to-keep))
----------------------------------------------------------------
The previous program uses the macros 'prevail' and 'deftransaction'
that I will explain latter.
Linj can translate the previous program into the following Java
program.
----------------------------------------------------------------
import java.io.IOException;
import java.util.Date;
import java.util.Vector;
import linj.Bignum;
import org.prevayler.*;
public class PrimesVector extends Object {
// methods
public static void main(String[] outsideArgs) throws IOException, ClassNotFoundException {
Vector primeKeeper = new Vector();
primeKeeper.add(Bignum.valueOf(2));
Prevayler prevayler = PrevaylerFactory.createPrevayler(primeKeeper, "PRIMES");
long primeCandidate = ((Number)primeKeeper.get(primeKeeper.size() - 1)).longValue() + 1;
while (true) {
if (primeP(primeCandidate)) {
keepPrime(prevayler, Bignum.valueOf(primeCandidate));
System.out.print("New prime found: " + primeCandidate + "." + '\n');
}
++primeCandidate;
}
}
public static boolean primeP(long number) {
if ((number <= 2) || ((number % 2) == 0)) {
return false;
} else {
double square = Math.ceil(Math.sqrt(number));
for (int factor = 3; factor <= square; factor = factor + 2) {
if ((number % factor) == 0) {
return false;
}
}
return true;
}
}
public static void keepPrime(Prevayler store, Object numberToKeep) {
store.execute(new KeepPrime(numberToKeep));
}
}
class KeepPrime extends Object implements Transaction {
// constructors
public KeepPrime(Object numberToKeep) {
this.numberToKeep = numberToKeep;
}
// methods
public void executeOn(Object prevalentSystem, Date date) {
Vector store = (Vector)prevalentSystem;
store.add(numberToKeep);
}
// slots
protected Object numberToKeep;
}
----------------------------------------------------------------
You can now compile it and execute it:
----------------------------------------------------------------
$ javac PrimesVector.java
$ java PrimesVector
New prime found: 3.
New prime found: 5.
New prime found: 7.
New prime found: 11.
...
New prime found: 409.
New prime found: 419.
[CONTROL-c, aborting the computation]
$ java PrimesVector
Reading PRIMES/0000000000000000001.transactionLog...
New prime found: 431.
New prime found: 433.
...
New prime found: 887.
New prime found: 907.
[CONTROL-c, aborting the computation]
$ java PrimesVector
Reading PRIMES/0000000000000000001.transactionLog...
Reading PRIMES/0000000000000000082.transactionLog...
New prime found: 911.
New prime found: 919.
...
New prime found: 1151.
New prime found: 1153.
[CONTROL-c, aborting the computation]
$ java PrimesVector
Reading PRIMES/0000000000000000001.transactionLog...
Reading PRIMES/0000000000000000082.transactionLog...
Reading PRIMES/0000000000000000155.transactionLog...
New prime found: 1163.
New prime found: 1171.
New prime found: 1181.
...
----------------------------------------------------------------
Quite simple, IMHO.
What's missing? The above mentioned macros 'prevail' and
'deftransaction'. The good news is that they are just plain Common
Lisp macros:
----------------------------------------------------------------
(defmacro prevail (expr &optional (name "prevail"))
`(in (the prevayler-factory)
(create-prevayler ,expr ,name)))
(defmacro deftransaction (name ((store store-class) &rest arglist) &body body)
`(progn
(defun ,name (,store ,@arglist)
(declare (prevayler store))
(execute ,store (new ',name ,@(mapcar #'extract-name&type arglist))))
(defclass ,name (transaction object)
,(mapcar #'(lambda (arg)
`(,arg))
arglist)
(:body
(defnew ,arglist
,@(mapcar #'(lambda (arg)
`(setf (slot-value this ',arg) ,arg))
arglist))
(defmethod execute-on (prevalent-system/object date/date)
(let ((,store (the ,store-class prevalent-system)))
,@body))))))
----------------------------------------------------------------
Although there are some Linj-specific code fragments, it's mostly
Common Lisp.
The above macros are just a simple attempt at hiding all that annoying
but necessary Java code. You could use other strategies, of course.
I'm planning to release Linj but I'm still unsure about the best
licence. Suggestions are welcome.
Best regards,
Ant�nio Leit�o.
[PS: I'm afraid I have a problem in my news feed. I couldn't see my own
article so I decided to post it a second time. Sorry if you
have already seen this.]
Antonio Menezes Leitao wrote:
> I'm planning to release Linj but I'm still unsure about the best
> licence. Suggestions are welcome.
I am really looking forward to it!
If we get tools to generate code for Java, C++, C#, Perl, php and Python
then I think most Lispers will have the opportunity to use CL in their
jobs (and noone will find out about it *g*).
It only should be given, that these tools allow a similar productivity
as one would without them (or better).
Andr�
--
=?ISO-8859-1?Q?Andr=E9_Thieme?= <······································@justmail.de> writes:
>
> Antonio Menezes Leitao wrote:
>
> > I'm planning to release Linj but I'm still unsure about the best
> > licence. Suggestions are welcome.
>
> I am really looking forward to it!
> If we get tools to generate code for Java, C++, C#, Perl, php and Python
> then I think most Lispers will have the opportunity to use CL in their
> jobs (and noone will find out about it *g*).
> It only should be given, that these tools allow a similar productivity
> as one would without them (or better).
Well, if you want to investigate tools somewhat like that, I could
direct you to our Stella system. It is not a Common Lisp to Java or C++
translator, but it does allow you to work in a restricted Lisp-like
language while delivering native Java or C++ or even Common Lisp code.
http://www.isi.edu/isd/LOOM/Stella/index.html
Working with the Common Lisp version, you get all of the dynamic linking
and redefinition features that one normally expects. The input syntax
is familiar S-expressions and macros are supported. The object system
is much more constrained than CLOS, though, because of a design goal to
be able to easily translate to a single-inheritance, single-dispatch
language.
C# is not available (yet), but it probably wouldn't be hard to produce,
based on the Java translator. The weakest part is actually the
documentation for the system.
Stella is an Open Source project, available under a choice of three
licensing options: GPL, LGPL or Mozilla.
--
Thomas A. Russ, USC/Information Sciences Institute
I've also been playing on some similar themes, and come to the
conclusion that to target Java/.net/Objective-C or C++ has too much of
an impedance match to use ANSI CL as source language.
Every library in the target languages (or for that matter python or
haskell) has a list model as a thing than has functions to access it's
members sequentially. Null and () are different. Even though you can add
wrappers and unwrappers to map cons'd lists onto functional lists,
automating them is a drag and the end code looks messy and runs slower
than pure Java; this gets very complicated if you are modifying a list
you have a reference to the head of elsewhere. It is also nice to use
the same iteration forms on functional lists, for example representing
the output of a streaming parser as a list of symbols- you don't want to
hold a 8GB AST in memory if you're only forward iterating over it's
contents, but you don't want to have to rewrite your code for the cases
where you do have the thing in memory.
The object systems in the target languages doesn't match CLOS. This
means if you want to use objects, you have to may from something other
than CLOS to the target language, or you can get lots of non-first arg
dispatch bugs. You can generate warnings for this quite easily at the
compile stage, if you also have a metaobject system in your generator.
As the computers on most people's desktops look like they are going to
be running a single dispatch object system as the core of their
operating system in a couple of years, downgrading from lisp's
capability is possibly the only pragmatic option. However, providing
auto-generation of the boilerplate for double dispatch is fairly
trivial, in the cases where the classes are in your control, so that can
be done transparently in most cases without a loss of conciseness.
Another thing I've played with is a lisp interface to a byte code
library, which had some success flattening OOT-OO Java and moving
branchs forward, detecting like clauses and other patterns as a
optimization process. That's implemented in Java, but with a lisp as the
control language, largely as I'd started it as a GUI based Java project,
but then decided that it would be easier to use with a command line and
scripting language, and if it's going to contain a scripting language
you may as well make it lisp. I'm planning to revisit that soon (I'm
writing an ASN.1 library for Java and want to be able to allow
flattening for the binary encoding cases but give a capability for
runtime configuration so you can read legacy file formats and arbitrary
XML), but is a bit too buggy for serious use right now, partly as I
tried to make its lisp too like common lisp so it ended up with lots of
impedance-management hacks, and more functionality than I could really
be bothered testing. I didn't want to reuse an extant cl implementation
as I hope to be able to reuse my little-lisp-on-java as a control
language for other Java tools.
Pete
On Fri, 26 Mar 2004 18:07:50 +0000, Pete Kirkham wrote:
> I've also been playing on some similar themes, and come to the
> conclusion that to target Java/.net/Objective-C or C++ has too much of
> an impedance match to use ANSI CL as source language.
That's specially true when you try to produce readable code in the
target language. But remember that Linj is not ANSI CL. Linj is a
Common Lisp-like language. My claim is that with some mild
restrictions in the source language, you can reduce the impedance
mismatch with the target language.
> Every library in the target languages (or for that matter python or
> haskell) has a list model as a thing than has functions to access it's
> members sequentially. Null and () are different.
Linj implements pairs just like in Common Lisp. They are not
translated into Java containers such as ArrayList or Vector, they are
translated just as a user-defined class. Here is (part of) the
definition of the cons type in Linj:
(package linj)
(defclass cons ()
((car :accessor car :initarg :car)
(cdr :accessor cdr :initarg :cdr)))
(defconstant +empty-list+ (make-instance 'cons))
(setf (car +empty-list+) +empty-list+
(cdr +empty-list+) +empty-list+)
(defmethod endp (this)
(eq this +empty-list+))
...
(defmethod find (elem (list cons) &key (test #'eql) (key #'identity))
(cond ((endp list)
null)
((funcall test elem (funcall key (first list)))
(first list))
(t
(find elem (rest list) :test test :key key))))
...
-----------------------------------------------------------------
This class definition is translated into:
-----------------------------------------------------------------
package linj;
public class Cons extends Object {
public Cons(Object car, Object cdr) {
this.car = car;
this.cdr = cdr;
}
public Object car() {
return car;
}
public Object setfCar(Object car) {
return this.car = car;
}
...
public boolean endp() {
return this == EMPTY_LIST;
}
...
public Object find(Object elem, Predicate2 test, Function key) {
if (this.endp()) {
return null;
} else if (test.funcall(elem, key.funcall(this.first()))) {
return this.first();
} else {
return this.rest().find(elem, test, key);
}
}
...
public static final Cons EMPTY_LIST = new Cons(null, null);
static {
EMPTY_LIST.setfCar(EMPTY_LIST);
EMPTY_LIST.setfCdr(EMPTY_LIST);
}
}
...
-----------------------------------------------------------------
Like you said, Linj also treats null and () as different entities and
I like it that way. However, _when these values are received in a
context where we are expecting booleans_ they are treated the same
and, just like in Common Lisp, () is also the false value. Consider
the following fragment/translation:
(defun foo (a b c)
(declare (boolean a) (cons b) (object c))
(not (and a b c)))
-----------------------------------------------------------------
public static boolean foo(boolean a, Cons b, Object c) {
return ! (a && (! b.endp()) && (c != null));
}
This allows you to write the traditional Common Lisp pattern of using
the empty list as false value. Consider the following case (I'll drop
the declare form and use a more compact equivalent):
(defun bar (elem1 elem2 list/cons)
(let ((tail (member elem1 list)))
(if (member elem2 tail)
elem1
elem2)))
-----------------------------------------------------------------
public static Object bar(Object elem1, Object elem2, Cons list) {
Cons tail = list.memberKey(elem1, null, null, 1);
if (! tail.memberKey(elem2, null, null, 1).endp()) {
return elem1;
} else {
return elem2;
}
}
Note the different uses of the member function. In the first, we are
expecting a list as result. In the second we are expecting a boolean.
My claim is that the boolean context is the only situation where you
want to treat 'null' and '()' (and 'nil') as the same entity.
Although Linj has a list data-type that is very similar to Common
Lisp's lists, nothing prevents you of using other Java containers.
They have different operations and a different use model, but that's
something you must know when you use them.
> Even though you can add wrappers and unwrappers to map cons'd lists
> onto functional lists, automating them is a drag and the end code
> looks messy and runs slower than pure Java; this gets very
> complicated if you are modifying a list you have a reference to the
> head of elsewhere.
As I said, they have different use models. If you use the 'cons' data
type, you get Lisp lists. If you use the 'ArrayList', 'Vector',
'LinkedList' or any other Java API data type, you must stick to that
data type operations. No impedance mismatch here.
> It is also nice to use the same iteration forms
> on functional lists, for example representing the output of a
> streaming parser as a list of symbols- you don't want to hold a 8GB
> AST in memory if you're only forward iterating over it's contents,
> but you don't want to have to rewrite your code for the cases where
> you do have the thing in memory.
They don't have the same iterations forms. In the 'cons' data type,
you have (restricted forms of) mapc, mapcar, dolist, etc. In the Java
API data types, you have the Java iterations forms such as Enumeration
and Iterator.
However, one of the things I like most about Linj is that you can
define macros that can use type information (something that Common
Lisp does not have). We can define a 'for-each' macro that looks at
the type of the object being iterated and expands into the correct
form. Here is an example, using the Vector' 'elements' method (it
returns an Enumeration):
(defun print-it (x/Vector)
(for-each (e (elements x))
(format t "~A~%" e)))
--------------------------------------------------------------
public static void printIt(Vector x) {
Enumeration iter = x.elements();
while (iter.hasMoreElements()) {
Object e = iter.nextElement();
System.out.println(e);
}
}
If you change the function to use the Iterator interface (using the
Vector' 'iterator' method), you get:
(defun print-it (x/Vector)
(for-each (e (iterator x))
(format t "~A~%" e)))
--------------------------------------------------------------
public static void printIt(Vector x) {
Iterator iter = x.iterator();
while (iter.hasNext()) {
Object e = iter.next();
System.out.println(e);
}
}
Now, we decide that, after all, the parameter 'x' is a list:
(defun print-it (x/cons)
(for-each (e x)
(format t "~A~%" e)))
--------------------------------------------------------------
public static void printIt(Cons x) {
for (Cons list = x; ! list.endp(); list = list.rest()) {
Object e = list.first();
System.out.println(e);
}
}
The macro is not difficult. Here's a quick attempt (don't bother yet
with potential name capture):
(def-linj-macro statement (for-each (?var ?form/expression) . ?body/statement-list)
(let ((form-type (get-type ?form)))
(cond ((cons-type-p form-type)
`(dolist (,?var ,?form) . ,?body))
((iterator-type-p form-type)
`(let ((iter ,?form))
(while (has-next iter)
(let ((,?var (next iter))) . ,?body))))
((enumeration-type-p form-type)
`(let ((iter ,?form))
(while (has-more-elements iter)
(let ((,?var (next-element iter))) . ,?body))))
(t
(error "Unknown type for iteration ~A" form-type)))))
Of course, if you prefer, you can make the macro more sophisticated by
looking at the implemented interfaces and searching for an iteration
protocol such as java.util.List. These macros, in fact, can have
access to the entire AST of the Linj program.
> The object systems in the target
> languages doesn't match CLOS. This means if you want to use objects,
> you have to may from something other than CLOS to the target
> language, or you can get lots of non-first arg dispatch bugs. You
> can generate warnings for this quite easily at the compile stage, if
> you also have a metaobject system in your generator. As the
> computers on most people's desktops look like they are going to be
> running a single dispatch object system as the core of their
> operating system in a couple of years, downgrading from lisp's
> capability is possibly the only pragmatic option. However, providing
> auto-generation of the boilerplate for double dispatch is fairly
> trivial, in the cases where the classes are in your control, so that
> can be done transparently in most cases without a loss of
> conciseness.
That's more or less what Linj does. Currently, we only have single
dispatch _but_ it doesn't need to be on the first argument. To
implement the usual Common Lisp functions you sometimes want to
'dispatch' on a different argument. For example, on the 'cons' class,
you would like to define methods such as 'member', 'mapcar', 'remove',
etc., where the 'receiver' is the second argument of the call. This
allows you to write '(member e l)' and to translate it to
'l.member(e)' in Java. Besides, Linj implements a simplified form of
method combination (including :before, :after and :around) so we
support a nice fragment of CLOS.
In general, single dispatch solves most of the problems but for
certain cases, we will really need multiple dispatch. I have plans to
add multiple dispatch but it'll have to wait until I have some more
time.
The truth is that Linj is used because of the huge Java libraries
(otherwise, I would have been using Common Lisp) or because the client
wants Java (someone told him it's good to have Java). These two
reasons reduce the usefulness of having full Common Lisp capabilities
in Linj: we would not be able to take advantage of them while dealing
with Java libraries and it would be harder to translate them into
readable Java code.
Thanks for your comments.
Antonio Leitao.
Antonio Menezes Leitao wrote:
> My claim is that the boolean context is the only situation where you
> want to treat 'null' and '()' (and 'nil') as the same entity.
I think I'd agree to that.
> However, one of the things I like most about Linj is that you can
> define macros that can use type information (something that Common
> Lisp does not have).
Macro expansion on expression type looks like it covers the iteration
(and a few other cases) nicely, though it means you still have two
libraries for (say) sorting a list.
> we support a nice fragment of CLOS.
I found your manual and would agree.
You use almost the same name mangling scheme as I do; though I hadn't
thought to put the types after the names of arguments with a slash.
As I'm interested in using the thing at runtime as a scripting language,
in my interpreter I've used the Java bean conventions to map accessors,
so that:
(when (not (visible frame)) (setf (visible frame) #t) (pack frame))
becomes (the equivalent of compiling and running):
if (!frame.isVisible()) {frame.setVisible(true); frame.pack(); }
rather than
if (!frame.visible()) {frame.setfVisible(true); frame.pack(); }
(assuming your translation that generates the accessor method name from
slot name is used in a symmetric manner).
It only tries getFoo() and isFoo() if foo() is undefined.
There's a similar mapping for named init-args and extra method args,
applied after for init and before for methods.
(defun foo () (make-instance foo "red" "apples" :bar 1 :baz 42))
->
static Foo foo () {
Foo foo = new Foo ("red", "apples");
foo.setBar(1);
foo.setBaz(42);
return foo;
}
The extra method args are applied before calling the method, so you can have
(draw graphics shape :color red
:stroke (once (make-instance basic-stroke 7)))
and it'll call setColor() and setStroke() before calling draw(shape),
and (when I get round to implementing it) use a static final field to
cache for the once value.
I also use a different mechanism to simulate conditions:
Conditions.raise(new Error("This is completely wrong"));
Where the implementation of Conditions.error passes the error to a list
of handlers stored in a thread local variable, so it's only thrown if
either the handler for the signalling thread is null or the no handler
in the list handles the error. This only deals with the exceptions in
the code you've written, not any from called functions, but that can't
be helped. It's mainly used by the interpreter for the debugger to hook
into, so it might not be a consideration with what you're doing.
For &rest I use the form proposed for Java varargs (pass an array of
objects & have a "Varargs" attribute to the method in the bytecode)
rather than passing a cons. If you're not looking at targeting 1.5 or
producing Java that ships without your runtime then that's not important.
Pete
On Sat, 27 Mar 2004 19:46:58 +0000, Pete Kirkham wrote:
> You use almost the same name mangling scheme as I do; though I hadn't
> thought to put the types after the names of arguments with a slash.
You can also use the traditional Common Lisp 'declare' form but given
the fact that you must use it more often than in Common Lisp I decided to
invent a nicer syntax. Some of my friends do not like it but
it allows you to write code a bit faster. Another reason is that I don't
like the aestetics of declare forms.
> As I'm interested in using the thing at runtime as a scripting language,
> in my interpreter I've used the Java bean conventions to map accessors,
> so that:
> (when (not (visible frame)) (setf (visible frame) #t) (pack frame))
>
> becomes (the equivalent of compiling and running):
> if (!frame.isVisible()) {frame.setVisible(true); frame.pack(); }
>
> rather than
> if (!frame.visible()) {frame.setfVisible(true); frame.pack(); }
>
> (assuming your translation that generates the accessor method name from
> slot name is used in a symmetric manner).
>
> It only tries getFoo() and isFoo() if foo() is undefined.
On an early phase of the Linj development I had a similar approach,
but I gave up because I found too much inconsistency in the Java APIs.
Currently, I reserve the (setf (accessor ...) ...) for my own methods.
For the Java APIs, I use the (set-visible frame t) instead of the
(setf (visible frame) t). Maybe I'll allow the user to specify which
of the approaches to use.
> There's a similar mapping for named init-args and extra method args,
> applied after for init and before for methods.
> (defun foo () (make-instance foo "red" "apples" :bar 1 :baz 42))
> ->
> static Foo foo () {
> Foo foo = new Foo ("red", "apples");
> foo.setBar(1);
> foo.setBaz(42);
> return foo;
> }
> The extra method args are applied before calling the method, so you can
> have
> (draw graphics shape :color red
> :stroke (once (make-instance basic-stroke 7)))
> and it'll call setColor() and setStroke() before calling draw(shape),
> and (when I get round to implementing it) use a static final field to
> cache for the once value.
That's something very interesting. And it doesn't look hard to
implement in Linj. However, Linj tries hard to implement Common Lisp
ordinary lambda lists and this will force the programmer to
declare all the keyword parameters. Moreover, some code fragments will
be hard to translate to readable Java. Here is one example:
(add panel (make-instance label "text" :alignment ...))
What will be the Java generated code in this case?
> I also use a different mechanism to simulate
conditions:
> Conditions.raise(new Error("This is completely wrong"));
>
> Where the implementation of Conditions.error passes the error to a list
> of handlers stored in a thread local variable, so it's only thrown if
> either the handler for the signalling thread is null or the no handler
> in the list handles the error. This only deals with the exceptions in
> the code you've written, not any from called functions, but that can't
> be helped. It's mainly used by the interpreter for the debugger to hook
> into, so it might not be a consideration with what you're doing.
Yes. Once again, my main goal was to let the client think that I wrote
tha Java code by hand. This decision forced me to use 'normal' Java code
almost exclusively. Regarding exceptions, I must confess that I
became completely fed up with Java checked exceptions and, as a result, I
decided to implement the propagation of Java checked exceptions to the
caller. Either you deal with the exception or you let it go up the
caller chain. If you never catch exceptions you end up with the top
level methods with a nice list of throws :-)
> For &rest I use the form
proposed for Java varargs (pass an array of
> objects & have a "Varargs" attribute to the method in the bytecode)
> rather than passing a cons. If you're not looking at targeting 1.5 or
> producing Java that ships without your runtime then that's not
> important.
>
To implement the Common Lisp &rest, I decided to go the Common Lisp way:
the caller passes a list (made of cons instances) and you can do whatever
you want with it. I might invent another lambda list keyword to implement
vargars but, in fact, my best client needed Java 1.1 so I could not depend
on the advanced features being proposed for java.
Thanks again for you comments. Lots of good ideas.
Antonio Leitao.
Antonio Menezes Leitao wrote:
> Here is one example:
>
> (add panel (make-instance label "text" :alignment ...))
>
> What will be the Java generated code in this case?
In bytecode you use dup (duplicate the last item on the operand stack),
an in the interpreter it's handled as part of the call to makeInstance.
It doesn't translate to Java-the-language without adding a temporary
variable.
Pete
Pete Kirkham wrote:
> > Where's Erik when we need him?
>
> Could you explain the reference?
Erik Naggum formerly posted a lot on c.l.l. He added a lot of value,
tended to have a polarizing influence on the newsgroup, and didn't
suffer fools gladly, if at all. I started reading the group during a
hiatus of his, and was compelled by the many references to him to seek
out and read many of his old posts. He knows his Lisp, and he's better
at natural language processing (i.e. reading comprehension) than most.
http://groups.google.com/groups?q=group%3Acomp.lang.lisp+%22erik+naggum%22
The above link unfortunately skews toward postings about Eric, rather
than by him, but you can keep scrolling.
--
Cameron MacKinnon
Toronto, Canada
Cameron MacKinnon <··········@clearspot.net> writes:
> http://groups.google.com/groups?q=group%3Acomp.lang.lisp+%22erik+naggum%22
>
> The above link unfortunately skews toward postings about Eric, rather than
> by him, but you can keep scrolling.
A search like:
http://groups.google.com/groups?as_ugroup=comp.lang.lisp&as_uauthors=erik%20naggum
specifies the author instead of posts containing the name, and should save you
some scrolling...
cheers,
Simon
Pete Kirkham <·················@cafemosaic.co.uk> wrote:
> find that once you are dealing with over a hundred lines of code you
> have to use an editor for lisp too, so the difference today is do I
> drag&drop (load "foo.lisp") into the rep loop or hit the
> recompile-and-run button.
Are quite sure you're not missing anything here?
Cheers,
-- Nikodemus
PS. I'm definitely not up to speed on Java IDEs, but is it really
commonplace to replace individual methods without recompiling significant
parts of the whole project -- and within milliseconds at that? I obviously
need to not only get out more, but get a new box. ;-)
·········@random-state.net wrote:
> PS. I'm definitely not up to speed on Java IDEs, but is it really
> commonplace to replace individual methods without recompiling significant
> parts of the whole project -- and within milliseconds at that? I obviously
> need to not only get out more, but get a new box. ;-)
In OO programming, the method implementation is independent of the
interface. In rapid development, each method is developed in isolation
against its unit tests. The unit test exercises the interface, so
doesn't need recompilation when the method change. Most of the
iterations for a single method require the only the recompilation of the
class that the method is in. To fire up Ant, recompile a single class
and run a dozen unit tests on it is typically 200-300ms on my G4 2x1.42.
That is the cycle time for most of my development; not significantly
longer compared to running the tests in the REPL loop. In either
environment, you spend >99% time editing the code, not building or
running it.
If I do the same in an IDE rather than an emacs clone, or am using an OS
which doesn't cache the JVM compiled bytecode image, it can take half a
minute, but that's IDEs/Windoze for you. I use an IDE as a glorified
debugger/object inspector if I get a bug which doesn't show in the unit
tests.
Once you get to interactions between classes, the cycle time goes up,
but if your assembling your application from proven components, and your
overall architecture is alright, then you don't spend too much time in
the unit assembly stages.
If changes to a method require a global rebuilt, you're not doing OO
right, and need to refactor. Refactoring takes longer, and isn't
supported by the language (which is one point where using lisp can be a
winner over pure Java; one of the things I'm playing with is a
refactoring tool that's scriptable in lisp) but that sort of
architectural change happens less frequently and requires you to spend
time thinking whatever your environment is, so isn't really part of the
smallest iterative loop.
Pete
"Erann Gat" <·········@jpl.nasa.gov> wrote in message
·······························@k-137-79-50-101.jpl.nasa.gov...
>
> I'm working on a simple object database built on top of a relational
> database. My current design is to have a "storable" mixin class which
> defines two methods, STORE and RETRIEVE, which do the obvious things.
>
> A problem with this simple design is that it places a burden on the user
> to keep track of the relative states of the objects in memory and in the
> DB, and to decide when to STORE and RETRIEVE them. It would be nice to
> make this transparent so that any reference to a slot of a STORABLE object
> would transparently synchronize to the DB. I had envisioned implementing
> this using a :before method on the slot-value and (setf slot-value)
> methods which would check to make sure everything was in sync.
>
> Is there a better way to do it? Is this a job for the MOP?
>
> My goal here is not only to make something functional, but also to
> highlight the power of CLOS with something that will really knock the
> socks off the Java people, so I really want to do this the Right Way.
>
> E.
There is a reason ACID relational databases have
begin-transaction/end-transaction constructs and databases without them are
weaker. Suppose I'm using a database of images and there is a failure when
the program is halfway through the modification of a large image. What is
the proper state of the database when the program is restarted? Your
persistence engine cannot know when the algorithm has completed the image
manipulation, nor can the engine know when there is a group of objects
modified separately but the changes for which must all hit the database or
none of them hit it. The programmer has to specify transaction end points to
delineate points of consistency between STORABLE objects in the application.
I suppose an argument could be made for implicit begin-transactions when a
STORABLE slot-value changes, although an argument could be made that
requiring the begin will help catch bugs where the programmer did not intend
to enter transaction state. Additionally, if you are flushing transactions
to the relational database as updates occur in the program, then you are
leaving the relational database with an open transaction state until the
end-transaction is called. That is not a nice way to play with the
database's other users and is especially bad if the programmer's bug caused
an implicit begin-transaction. Consider the image application again. What if
I want to apply a filter to see if I like it? If I don't, I undo. Is there a
reason to flush the image to the database before the user accepts the
change? I vote for the explicit begin-transaction.
You might explore a (flush) which does (with-transaction (db)
(store-all-modified-storable-objects)) but I'd also like to have
(with-transaction (db) (store-storable-objects <object>*)).
It's an interesting and practical application. Good luck.
"a" <···@def.gh> wrote in message
··························@sea-read.news.verio.net...
> There is a reason ACID relational databases have
> begin-transaction/end-transaction constructs and databases without them
are
> weaker. Suppose I'm using a database of images and there is a failure when
> the program is halfway through the modification of a large image. What is
> the proper state of the database when the program is restarted? Your
> persistence engine cannot know when the algorithm has completed the image
> manipulation, nor can the engine know when there is a group of objects
> modified separately but the changes for which must all hit the database or
> none of them hit it. The programmer has to specify transaction end points
to
> delineate points of consistency between STORABLE objects in the
application.
The biggest complaint I have is that simply updating/inserting a row with
multiple indexes must in itself be a transaction.
A single record update is nothing more than a block write, and is ALMOST
atomic. The risk of a write failure is quite low.
But throw in indexes, and a you are now talking several block writes, and
depending on how the blocks are flushed, can easily leave an index corrupted
if it fails at a particularly bad point.
So, just to guarantee that a single update is done properly, you need to
have the foundations of a transactional system in place to be able to, at a
database level, atomically update the record and its indexes.
There are few things more annoying than having to rebuild database indexes.
In Dark Days of Yore where I worked on ISAM systems, rebuilding indexes was
du jour for fixing problems. Step one: Rebuild indexes.
I understand that modern mySQL has transactions as an option, and the fact
that it's a server process will ideally help isolate application crashes
from corrupting the DB. But mySQL has other "problems" that make me avoid it
as well.
HOWEVER, as a bone head simple data store, i.e. a higher level Berkley DB,
I'm sure for Eranns needs it's acceptable.
Regards,
Will Hartung
(·····@msoft.com)
Erann Gat wrote:
> I'm working on a simple object database built on top of a relational
> database. My current design is to have a "storable" mixin class which
> defines two methods, STORE and RETRIEVE, which do the obvious things.
>
> A problem with this simple design is that it places a burden on the user
> to keep track of the relative states of the objects in memory and in the
> DB, and to decide when to STORE and RETRIEVE them. It would be nice to
> make this transparent so that any reference to a slot of a STORABLE object
> would transparently synchronize to the DB. I had envisioned implementing
> this using a :before method on the slot-value and (setf slot-value)
> methods which would check to make sure everything was in sync.
>
> Is there a better way to do it? Is this a job for the MOP?
>
> My goal here is not only to make something functional, but also to
> highlight the power of CLOS with something that will really knock the
> socks off the Java people, so I really want to do this the Right Way.
>
> E.
Hello,
I would not like to discourage you in your enterprise, but I think that
to "really knock the socks off the Java people", you will have to
implement something more difficult than that.
Current O/R mapping products (not only commercial ones) include more or
less the following functionality :
- ACID transactions. In multithreaded environments. Because it usually
matters;
- several mapping schemas for classes, because of the different
performace characteristcs for different access patterns to the
objects/tables, and as second because sometimes people like to access
legacy databases, whose datamodel have the structure of cobol
copy-members and as such is hardly even relational...) and for simple
types (because some legacy applications store a boolean as 1 and 0 in a
field of type VARCHAR(1)...) ;
- support different distribution models (C/S, multitier, in cluster or
not) whith all implications for distibuted transactions and caching;
- some method for making the mapping process easier (GUI, XML etc. -
this is not to say that it is a good solution, but better that nothing :) ;
- support for caching (and sometimes in a cluster);
- support some kind of may be optimizable (in terms of caching ans
translation to SQL) query language;
- the possibility to tune the access methods of the mapping library to
specific contexts (againg because of performance);
- support for all major databases (with the possibility to let you write
your own SQL, for the cases where the tool does this in a too dumb or
incompatible way);
- add some more less important functionality
As a user you have the possibility to choose between several (I know of
at least 15) products with licensing costs between $0 and several
thousends per seat and/or processor (application server not included :)
. With documentation between 0 and up to 800 pages. With support between
0 and somehow usable. With performance between 0 and good in the context
of having a Java O/R layer in your application. With more or less
portability between JVMs, application servers and databases.
Even if every single future is may be trivial, all together are by far
not. Some of the complexity comes from the design of the JVM, Java and
standards like JDBC, EJB and JDO. But the rest comes from questions not
related to Java directly (like distributed transactions and caching).
Not to mention the trade-offs of using O/R mapping in general and in
Java in particular. Actually some of the problems are in Java easier
than in CL, for example you haveto think more about possible mapping
schemas, because you can put in a slot much more things than in Java.
So, to impress a Java people, even if you don't manage to do it the
right way, you would have to at least implement enought functionality
just because in the Java world it is allready there. Not to say, that I
have a lot of positive feelings about the Java world - I just happen to
know somehow the market of O/R mapping tools and some of the
expectations of their users. (Commenting these expectations would be
actually a funny off-topic :)
How easy it is to implement a simple O/R mapping library is allready
demonstrated by UnCommonSQL and the like (I'm placing it before
CommonSQL not because of it's qualities, but being open-source, it
allows Java developers to get some concrete Lisp, if they choose to
study O/R-mapping tools for discovering the strenghts of CL).
Of course if you put enought functionality in your project, that would
serve not only Java developers, but also some CL users :)
...
...
One of the posters mentioned one of the Paepcke's papers. There are also
his papers about PCLOS - an O/R mapping framework. But for real code,
you will need to use current MOP documentation. There is also a recent
paper by Arthur Lee and Joseph Zachary called "Adding Support for
Persistence to CLOS via Its Metaobject Protocol". There is also an old
paper by Theodore Phelps, called "Persistent object storage in
collaborative systems".
Some posters mentioned cl-prevalence. Escaping the context of O/R
mapping tools, and entering the one of the Main Memory Databases
(prevalence is such, and besides of the marketing of the original
prevalence project, it is either the first nor best one in history, but
may be the simplest to implement in Java), I think that the concepts
found in projects like HiBase and Monet (there is even a similar and
really good commercial product calles KDB from K Systems, which seem to
predate Monet, but Monet is better documented) are more intersting in
terms of the "Right Way". But both would require a deep understanding of
your CL implementation and/or support from your vendor.
In the context of pure Lisp OODBs there is (besides PLOB) an old paper
from by and Swanson called "UCL+P - Defining and Implementing Persistent
Common Lisp".
I suppose, that enhancing cl-prevalence, implementing a better MMDB or
replacing the backend of PLOB with something accessible, would also be a
great contribution to both communities :)
With best regards
Plamen
--
Today is the first day of the rest of your life.
"Plamen Stamov" <··@stamov.ch> wrote in message
·············@news01.datazug.ch...
> Hello,
>
> I would not like to discourage you in your enterprise, but I think that
> to "really knock the socks off the Java people", you will have to
> implement something more difficult than that.
>
> Current O/R mapping products (not only commercial ones) include more or
> less the following functionality :
Sure, but you don't necessarily need that to "knock the socks off".
All of these tools are external to Java. Using either XML or augmenting
JavaDoc comments.
What Erann is doing is "simply" extending the object system in CL to support
a "crude persistence".
General purpose OO/Relational mapping packages are complicated, because the
task is complicated.
But within some contraints, simple class mapping isn't overly arduous. If
you can live with Class == Table, you can go quite a long way.
Implement it elegantly as a combination of macros, GFs and objects, on top
of what CL already provides, and it makes for a nice showcase of the system.
Many will poo-poo it because it doesn't do a, b, c, or d. And what they will
overlook is that when it's all done, you have basic persistence in CL.
When you add code to a CL image, you get CL. With Java, you get Java, Ant,
Hibernate, XML, XDoclet, etc. etc. etc. A bunch of disparate tools.
It is a wonder that they integrate as well as they do, but in the end, it's
a bunch of seperate entities, preprocessors, postprocessors, classloaders,
etc.
Whether in the end any socks will be knocked akimbo, is another matter.
Regards,
Will Hartung
(·····@msoft.com)
In article <···············@ID-197644.news.uni-berlin.de>, "Will Hartung"
<·····@msoft.com> wrote:
> Implement it elegantly as a combination of macros, GFs and objects, on top
> of what CL already provides, and it makes for a nice showcase of the system.
>
> Many will poo-poo it because it doesn't do a, b, c, or d. And what they will
> overlook is that when it's all done, you have basic persistence in CL.
With no need to read a book to learn how to use it. For a few hundred LOC
(hopefully).
That's the thing that most people don't seem to get: it's not about what
you can and can't do, it's about how much effort it takes to do it.
"Can you do X using Y?" is the wrong question. The right question is:
"How much work is it to do X using Y compared to doing X using Z? (And
part of the tally of work required has to unclude the time it takes to
read the manual.)
> Whether in the end any socks will be knocked akimbo, is another matter.
I regret ever having used that phrase.
E.
me :
>How easy it is to implement a simple O/R mapping library is allready
>demonstrated by UnCommonSQL and the like (I'm placing it before
>CommonSQL not because of it's qualities, but being open-source, it
>allows Java developers to get some concrete Lisp, if they choose to
>study O/R-mapping tools for discovering the strenghts of CL).
...
>Of course if you put enought functionality in your project, that
would
>serve not only Java developers, but also some CL users :)
> In article <···············@ID-197644.news.uni-berlin.de>, "Will Hartung"
> <·····@msoft.com> wrote:
>
> > Implement it elegantly as...
Yes.
> With no need to read a book to learn how to use it. For a few hundred LOC
> (hopefully).
>
> That's the thing that most people don't seem to get: it's not about what
> you can and can't do, it's about how much effort it takes to do it.
>
> "Can you do X using Y?" is the wrong question. The right question is:
> "How much work is it to do X using Y compared to doing X using Z? (And
> part of the tally of work required has to unclude the time it takes to
> read the manual.)
>
Yes. But again, it is allready done by projects like UnCommonSQL (In
the context of O/R mappers). They miss may be some PowerPoint slides
explaining what actually happens in the (for non-lisper)
non-documented source code (which consists of actually only four
bigger files!), because education isn't their main motive (and from
your post I got that you want at first place to USE your O/R-mapper).
So I could not see any added value to the existing materials, from
which a non-lisper may learn. How to do macros, GFs etc. is explained
more or less in a lot of books, and examples exist in much more open
source projects. That CL may cut the time to market is also known. And
that this is only one of several factors one may take in account for
evaluation of a technology for a concrete project is also known. And
to help CL providing a "success story" would be nice, but a very, very
simple O/R mapper would (in my view) not suffice.
Because I missed the added value in that respect, I completly ignored
it in my post and supposed that you want to add value in terms of
functionality and in the same time are not informed about the current
state of affaires in Java. So I wanted to help you better predict the
time you need to invest. Further I supposed that an O/R mapper which
is simpler than UnCommonSQL would be of less use...
I see I was wrong.
My point was that it would be usefull for both communities to add
value to something. My second point was that it would be more usefull
for you if you use something more usefull than to build from scratch
something less usefull :)
I'm sorry that all that got smashed throught the virtuosity of my
english :)
Plamen
--
When CL is your hammer, does everybody look like a thumb?
Erann Gat wrote:
> My goal here is not only to make something functional, but also to
> highlight the power of CLOS with something that will really knock the
> socks off the Java people, so I really want to do this the Right Way.
Well, we've heard from the Java camp about the state of the art there,
so here's some thoughts:
By default, storable objects should map one class to a table, with all
slots stored, and the class and slot names corresponding to the table
and column names. Overriding this default mapping would be a "nice to have".
Instantiating an object should automatically retrieve it from the DB, if
it exists.
Updating any slot ought to add that object to a queue of objects to be
written. Once the queue reaches x objects, or every y seconds, or upon
shutdown, the queue gets flushed to the database. This is a high
performance, non-ACID strategy. Setting x=1 approximates ACID.
Alternatively, you could designate one slot as special, and setting that
slot would cause the whole object to be queued for write. In this
scheme, the programmer is responsible for updating slots in a partial
order, and for always touching the special slot whenever any other slot
is updated.
Done right, the number of keystrokes required for the user of storable
ought to be very minimal.
Now, what happens if the class is redefined to add slots? The storable
code notices that the class definition is out of sync with the database,
and either
a) updates the database schema by adding or dropping a column (table
create & copy may be required)
b) creates another table, and remembers that there are now n+1 versions
of the table. From now on, object instantiations must check old versions
of the table if the newest version fails to turn up a match. When a
match is found in an older table, it is moved to the newer table
(optionally).
Supporting this for the add/drop of a slot in one class is trivial.
Support for moving a slot from one class to another if a 1:1 mapping
between them exists ought not to be too difficult. Automatically
supporting more advanced schema redesigns left as an exercise...
Being able to update the object schema and/or the class<->database
mapping seamlessly while the application is running is a "knock your
socks off" feature. People who insist that their development environment
has moved beyond the waterfall model won't be able to follow you through
THOSE rapids.
And finally, because every project needs a stupid acronym to gain
mindshare, may I suggest SWIM -- "Store What I Mean"?
--
Cameron MacKinnon
Toronto, Canada
In article <······················@golden.net>, Cameron MacKinnon
<··········@clearspot.net> wrote:
> Erann Gat wrote:
> > My goal here is not only to make something functional, but also to
> > highlight the power of CLOS with something that will really knock the
> > socks off the Java people, so I really want to do this the Right Way.
>
> Well, we've heard from the Java camp about the state of the art there,
> so here's some thoughts:
>
> By default, storable objects should map one class to a table, with all
> slots stored, and the class and slot names corresponding to the table
> and column names. Overriding this default mapping would be a "nice to have".
>
> Instantiating an object should automatically retrieve it from the DB, if
> it exists.
>
> Updating any slot ought to add that object to a queue of objects to be
> written. Once the queue reaches x objects, or every y seconds, or upon
> shutdown, the queue gets flushed to the database. This is a high
> performance, non-ACID strategy. Setting x=1 approximates ACID.
>
> Alternatively, you could designate one slot as special, and setting that
> slot would cause the whole object to be queued for write. In this
> scheme, the programmer is responsible for updating slots in a partial
> order, and for always touching the special slot whenever any other slot
> is updated.
>
> Done right, the number of keystrokes required for the user of storable
> ought to be very minimal.
Yep, that's pretty much what I'm up to.
> Now, what happens if the class is redefined to add slots?
Yes, a most interesting question :-)
> The storable
> code notices that the class definition is out of sync with the database,
Or you can shadow defclass so it doesn't have to "notice".
> and either
> a) updates the database schema by adding or dropping a column (table
> create & copy may be required)
MySQL has a handy-dandy "alter table" command :-)
> Supporting this for the add/drop of a slot in one class is trivial.
> Support for moving a slot from one class to another if a 1:1 mapping
> between them exists ought not to be too difficult.
I'm not sure what you mean by "moving a slot from one class to another."
> Being able to update the object schema and/or the class<->database
> mapping seamlessly while the application is running is a "knock your
> socks off" feature. People who insist that their development environment
> has moved beyond the waterfall model won't be able to follow you through
> THOSE rapids.
OK, you've got me fired up now :-)
> And finally, because every project needs a stupid acronym to gain
> mindshare, may I suggest SWIM -- "Store What I Mean"?
An interesting suggestion. I'll take it under advisement.
Thanks for the cogent and constructive feedback.
E.
Erann Gat wrote:
>>Supporting this for the add/drop of a slot in one class is trivial.
>>Support for moving a slot from one class to another if a 1:1 mapping
>>between them exists ought not to be too difficult.
>
>
> I'm not sure what you mean by "moving a slot from one class to another."
Say you've got two tables in the database, each with one row per
employee. Classical database theory says this should all be in one
table, but if one table is for employee ID, login and home directory,
used all the time, and the other table has numbers only used every two
weeks for running payroll, it can be a big win to split them, so the
payroll stuff isn't taking up RAM all the time as a consequence of the
other info being constantly used.
Now someone decides that maybe a certain field should be moved from one
table to the other. The DB administrator creates or alters the
destination table, runs a select to move the field over, creates the new
source table (now missing a column), runs a select to move the data
over, drops the old tables, and builds indexes on and renames the new
ones. Also, someone's got to go through the source of all the
applications that use those tables, changing the SQL on all the insert,
select and update statements that are affected. Huge pain in the ass,
which is why database stuff is generally done waterfall model.
I think I came across the following articles last year sometime, and
they just blew me away. A high pressure development environment, a
database constantly in flux, and lots of legacy applications which
continue to function, talking to what appears to be version (a few
months and many changes ago) of the database. Lisp being the magic
bullet, of course. It was never like this where I worked!
Tracking Assets in the Production of 'Final Fantasy : The Spirits Within'
http://www.lava.net/~shiro/Private/essay/gdc2002.html
Shooting A Moving Target--- An Experience In Developing A Production
Tracking Database
http://www.shiro.dreamhost.com/scheme/docs/jlugm2000.html
--
Cameron MacKinnon
Toronto, Canada
Cameron MacKinnon <··········@clearspot.net> writes:
> I think I came across the following articles last year sometime, and
> they just blew me away. A high pressure development environment, a
> database constantly in flux, and lots of legacy applications which
> continue to function, talking to what appears to be version (a few
> months and many changes ago) of the database. Lisp being the magic
> bullet, of course. It was never like this where I worked!
>
> Tracking Assets in the Production of 'Final Fantasy : The Spirits Within'
> http://www.lava.net/~shiro/Private/essay/gdc2002.html
>
> Shooting A Moving Target--- An Experience In Developing A Production
> Tracking Database
> http://www.shiro.dreamhost.com/scheme/docs/jlugm2000.html
I read the Final Fantasy one from a link on the alu.cliki.net site.
Both those articles are reachable from there (same author). Very
impressive stuff.
I worked in an environment using Perl + Perl::DBI + Oracle where a
tech lead managed to get the DB people (there was some serious
compartmentalization at the company) to do a 1:1 object to DB table
mapping. There was a bit of a problem though with the approach.
Objects often contained references to other objects so that
instantiating them from the database ended up grabbing megabytes of
data. It is possible to saturate gigabit ethernet.
That problem was fixed (?) by using a lazy loading algorithm so that
slots were loaded on demand and then cached.
The big problem I had with that particular architecture was that the
DB schema was fairly quickly cast into stone. That wouldn't have
been a problem if it wasn't for the simple fact that the database was
driving a website. The cost of displaying 1:1 mapped objects on web
pages was high.
I found myself creating a number of what I called pseudoclasses. They
were so named because they did not exist in the database as such.
They were constructed from SELECT statements that were far more
efficient than aggregating a bunch of 'real' classes because only a
single query was required. I was able to get away with them as they
were read only so synchronization was not an issue. When I say 'get
away with them' I really mean that in a political sense rather than a
technical sense.
I'm sure that the defects of the particular implementation play a
large role in my opinion here, but I got the impression that while a
1:1 class mapping to tables in an RDBMS seems good on paper, it is
not really always the way to go.
I really do like that idea about having the database schema auto
updated when you change a class definition. If it could track moving
a slot from one class to another, that would blow off more than just
socks I think.
--
Those who do not remember the history of Lisp are doomed to repeat it,
badly.
> (dwim x)
NIL