Re: CLOS and databases
From: Antonio Menezes Leitao (menezesleitao_at_netcabo.pt)
Date: 03/25/04
- Next message: Duane Rettig: "Re: Code Feedback Wanted (Generating more garbage)"
- Previous message: Erann Gat: "Re: What would you do with 10 Man Years?"
- In reply to: Pete Kirkham: "Re: CLOS and databases"
- Next in thread: André Thieme: "Re: CLOS and databases"
- Reply: André Thieme: "Re: CLOS and databases"
- Reply: Pete Kirkham: "Re: CLOS and databases"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Date: Thu, 25 Mar 2004 22:31:02 +0000
Pete Kirkham <pete-kirkham-2004@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.]
- Next message: Duane Rettig: "Re: Code Feedback Wanted (Generating more garbage)"
- Previous message: Erann Gat: "Re: What would you do with 10 Man Years?"
- In reply to: Pete Kirkham: "Re: CLOS and databases"
- Next in thread: André Thieme: "Re: CLOS and databases"
- Reply: André Thieme: "Re: CLOS and databases"
- Reply: Pete Kirkham: "Re: CLOS and databases"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Relevant Pages
|