Re: Program compression



Richard Heathfield <rjh@xxxxxxxxxxxxxxx> writes:

In any (even relatively sensible) language where it takes /more/ than one
line of code, it only takes more than one line the /first/ time - because
the programmer will wrap it into a function (or procedure, or subroutine,
or whatever) and, in future, call the function (or whatever). So this
conciseness argument is a mere canard, as all program tasks eventually
boil down to:

call existing_solution

which is a one-liner in pretty well any language.

Not in languages that don't provide syntactic abstraction.

Let's take a simple example, the Memento pattern:
http://en.wikipedia.org/wiki/Memento_pattern

Everytime you want to apply this pattern to a new class, you have to
add the following methods and class, take care not to miss a member to
save/restore/keep in the memento:

public Object saveToMemento() {
System.out.println("Originator: Saving to Memento.");
return new Memento(state);
}
public void restoreFromMemento(Object m) {
if (m instanceof Memento) {
Memento memento = (Memento)m;
state = memento.getSavedState();
System.out.println("Originator: State after restoring from Memento: "+state);
}
}

private static class Memento {
private String state;

public Memento(String stateToSave) { state = stateToSave; }
public String getSavedState() { return state; }


In lisp, we write the same, but only ONCE:

(defgeneric save-to-memento (<base-class>)
(:documentation
"Returns a new <memento-class> holding all the state of <base-class>"))

(defmacro define-memento (<memento-class> <base-class> (&rest <base-class-slots>))
`(progn
(defclass ,<memento-class> ()
,(loop
:for slot :in <base-class-slots>
:collect `(,slot
:initarg ,(intern (symbol-name slot)
(load-time-value (find-package "KEYWORD"))))))
(defmethod save-to-memento ((self ,<base-class>))
(format *trace-output* "~&~S saving to memento.~%" self)
(make-instance ',<memento-class>
,@(loop
:for slot :in <base-class-slots>
:collect (intern (symbol-name slot) (load-time-value (find-package "KEYWORD")))
:collect `(slot-value self ',slot))))
(defmethod restore-from-memento ((self ,<base-class>) (memento ,<memento-class>))
(format *trace-output* "~&~S restoring from memento.~%" self)
,@(loop
:for slot :in <base-class-slots>
:collect `(setf (slot-value self ',slot) (slot-value memento ',slot)))
self)
',<memento-class>))


And then, everytime we have a new class:

(defclass originator ()
((state :initarg :state :accessor state)))
(defmethod (setf state) :before (new-value (self originator))
(format *trace-output* "~&~S setting new state ~S~%" self new-value))


we only have to write the oneliner call:

(define-memento memento originator (state))


to add a memento to the new class:

(defun example ()
(let ((caretaker '())
(originator (make-instance 'originator :state "Initial")))
(setf (state originator) "State1")
(setf (state originator) "State2")
(push (save-to-memento originator) caretaker)
(setf (state originator) "State3")
(push (save-to-memento originator) caretaker)
(setf (state originator) "State4")
(restore-from-memento originator (elt caretaker 0))
(format *trace-output* "~&~S state is ~S~%" originator (state originator))
(values)))

C/USER[82]> (example)
#<ORIGINATOR #x208D4AB6> setting new state "State1"
#<ORIGINATOR #x208D4AB6> setting new state "State2"
#<ORIGINATOR #x208D4AB6> saving to memento.
#<ORIGINATOR #x208D4AB6> setting new state "State3"
#<ORIGINATOR #x208D4AB6> saving to memento.
#<ORIGINATOR #x208D4AB6> setting new state "State4"
#<ORIGINATOR #x208D4AB6> restoring from memento.
#<ORIGINATOR #x208D4AB6> state is "State3"

(defclass person ()
((name :initarg :name :accessor name)
(address :initarg :address :accessor address)
(birthdate :initarg :birthdate :reader birthdate)))


(define-memento person-state person (name address birthdate)) ; ONE LINE! YAY!


C/USER[87]> (let* ((p (make-instance 'person
:name "Tintin"
:address "Chateau de Moulinsart"
:birthdate '1929/01/10))
(m (save-to-memento p)))
(print (list (name p) (address p) (birthdate p)))
(setf (name p) "Tintinovitch"
(address p) "Kremlin")
(print (list (name p) (address p) (birthdate p)))
(restore-from-memento p m)
(print (list (name p) (address p) (birthdate p)))
(values))
#<PERSON #x207D0636> saving to memento.

("Tintin" "Chateau de Moulinsart" |1929/01/10|)
("Tintinovitch" "Kremlin" |1929/01/10|)
#<PERSON #x207D0636> restoring from memento.

("Tintin" "Chateau de Moulinsart" |1929/01/10|)


This is possible because lisp, in addition to supporting data
abstraction and procedural abstraction, which any other programming
language followed suit, also supports syntactic abstraction and
meta-linguistic abstraction.


--
__Pascal Bourguignon__ http://www.informatimago.com/

PLEASE NOTE: Some quantum physics theories suggest that when the
consumer is not directly observing this product, it may cease to
exist or will exist only in a vague and undetermined state.
.



Relevant Pages