In my design I have a front-end and a back-end. The front-end acts
like a command processor, but in CL, instead of a specially written
interpreted language. The front-end controls the back-end. The front-
end is always the same, but the back-end can change. So I am trying to
connect the back-end to the front-end through a common interface.
The interface functions are created in the back-end and always refer
to lexical variables, so I need to return closures. However, there are
several strategies possible.
a) Return a multiple value list of closures and bind them in the front-
end to call them via funcall
b) Return a structure containing these closures and also call them via
funcall
c) export them via the package definition and create them as named
closures in the following fashion
(defun create-closures ()
(let ((a 0))
(values
(defun closure-up ()
(incf a))
(defun closure-down ()
(decf a)))))
Maybe I should even write some glue ? In the current case the front-
end loads the back-end, but maybe I should have a third program which
initialises the back-end, and then initialise the front-end using the
return value from the back-end ?
Regards,
Jurgen
jurgen_defurne <··············@pandora.be> writes:
> In my design I have a front-end and a back-end. The front-end acts
> like a command processor, but in CL, instead of a specially written
> interpreted language. The front-end controls the back-end. The front-
> end is always the same, but the back-end can change. So I am trying to
> connect the back-end to the front-end through a common interface.
>
> The interface functions are created in the back-end and always refer
> to lexical variables, so I need to return closures. However, there are
> several strategies possible.
>
> a) Return a multiple value list of closures and bind them in the front-
> end to call them via funcall
> b) Return a structure containing these closures and also call them via
> funcall
> c) export them via the package definition and create them as named
> closures in the following fashion
>
> (defun create-closures ()
> (let ((a 0))
> (values
> (defun closure-up ()
> (incf a))
>
> (defun closure-down ()
> (decf a)))))
>
> Maybe I should even write some glue ? In the current case the front-
> end loads the back-end, but maybe I should have a third program which
> initialises the back-end, and then initialise the front-end using the
> return value from the back-end ?
Closures are equivalent to objects.
Why don't you use normal CLOS objects, with classes and subclasses?
(defclass counter ()
((a :initform 0)))
(defmethod counter-up ((self counter))
(incf (slot-value self 'a)))
(defmethod counter-down ((self counter))
(decf (slot-value self 'a)))
(defun create-counter ()
(make-instance 'counter))
(let ((c (create-counter)))
(counter-up c)
(counter-down c))
Then you can easily define "back-end" counters:
(defclass remote-counter (counter)
((host :initform "localhost" :initarg :host :reader host)
(login :initform "counter" :initarg :login :reader login)
(file :initform "~/counter" :initarg :file :reader file)))
(defmethod gen-command ((self remote-counter) operator)
(format nil "ssh ····@~A\" bash -c 'counter=$(cat ~S);counter=$(( $counter ~A 1 ));echo $counter>~2:*~S'"
(login self) (host self) (file self) operator))
(defmethod counter-up ((self remote-counter))
(ext:shell (gen-command self "+")))
(defmethod counter-down ((self remote-counter))
(ext:shell (gen-command self "-")))
and write create-counter to select the right backend:
(defun create-counter ()
(ecase *backend*
((:remote)
(make-instance 'remote-counter :host *remote-host* :login *remote-login* :file *remote-file*))
((:local)
(make-instance 'counter))))
For a more complete example, have a look at the sources of swank.
--
__Pascal Bourguignon__