From: jurgen_defurne
Subject: Coupling two packages via an interface ?
Date: 
Message-ID: <2b1064e2-6bae-43d4-9fc9-510cba7544e2@a12g2000yqm.googlegroups.com>
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
From: Pascal J. Bourguignon
Subject: Re: Coupling two packages via an interface ?
Date: 
Message-ID: <7cfxhtqrsf.fsf@pbourguignon.anevia.com>
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__