Discussion:
WITH-ACTIVE-LAYERS does not eval its args. Alternatives?
Kilian Sprotte
2013-12-01 09:58:45 UTC
Permalink
Hi,

I have run into a situation, where I would want WITH-ACTIVE-LAYERS to
evaluate its layer arguments or to have an alternative construct which
does so.

My questions are:

1. Is there a reason for WITH-ACTIVE-LAYERS to be designed this way?

2. Is there an alternative that allows me to achieve the desired
effect?

ENSURE-ACTIVE-LAYER and ENSURE-INACTIVE-LAYER being functions of
course evaluate their arguments, but using them together with an
UNWIND-PROTECT does not work, because ENSURE-ACTIVE-LAYER works
globally and also affects other threads.

The background is that I am using layers to represent different
backends in a database API. I would need WITH-ACTIVE-LAYERS to
evaluate its argument in the following example: A user wants to
connect to the database, so he specifies some connection settings,
including the backend to be used:

(defun open-db (spec)
(destructuring-bind (backend &rest args) spec
(with-active-layers (backend)
(apply #'%open-db args))))

Suggestions or Remarks?

Thanks
Kilian
Pascal Costanza
2013-12-01 12:56:22 UTC
Permalink
Hi Kilian,
Post by Kilian Sprotte
Hi,
I have run into a situation, where I would want WITH-ACTIVE-LAYERS to
evaluate its layer arguments or to have an alternative construct which
does so.
1. Is there a reason for WITH-ACTIVE-LAYERS to be designed this way?
Yes, the macro version can be implemented more efficiently, because you already know at macro-expansion time what layers to activate.
Post by Kilian Sprotte
2. Is there an alternative that allows me to achieve the desired
effect?
Yes. adjoin-layer and remove-layer allow you to programmatically create new combinations of layers. You need to pass one layer and a “layer context”, which is an already composed set of layers. You need to call adjoin-layer and remove-layer several times if you want to compose several layers, because composition ordering matters. current-layer-context gives you a context of the currently active layers. So adjoin-layer, remove-layer, and current-layer-context in combination are the functions to compute new sets of layers.

Once you have a new layer context, you can use funcall-with-layer-context or apply-with-layer-context to execute a block of code within that context.

Such contexts are first-class, so you can store them and pass them around.
Post by Kilian Sprotte
The background is that I am using layers to represent different
backends in a database API. I would need WITH-ACTIVE-LAYERS to
evaluate its argument in the following example: A user wants to
connect to the database, so he specifies some connection settings,
(defun open-db (spec)
(destructuring-bind (backend &rest args) spec
(with-active-layers (backend)
(apply #'%open-db args))))
Suggestions or Remarks?
This is exactly the kind of case for which the first-class contexts are designed, and I have used them in similar situations for some demos. Please let me know if you run into any problems with this.

I hope this helps,
Pascal

--
Pascal Costanza
Pascal Costanza
2013-12-01 17:28:38 UTC
Permalink
Post by Pascal Costanza
Hi Kilian,
Post by Kilian Sprotte
Hi,
I have run into a situation, where I would want WITH-ACTIVE-LAYERS to
evaluate its layer arguments or to have an alternative construct which
does so.
1. Is there a reason for WITH-ACTIVE-LAYERS to be designed this way?
Yes, the macro version can be implemented more efficiently, because you already know at macro-expansion time what layers to activate.
Post by Kilian Sprotte
2. Is there an alternative that allows me to achieve the desired
effect?
Yes. adjoin-layer and remove-layer allow you to programmatically create new combinations of layers. You need to pass one layer and a “layer context”, which is an already composed set of layers. You need to call adjoin-layer and remove-layer several times if you want to compose several layers, because composition ordering matters. current-layer-context gives you a context of the currently active layers. So adjoin-layer, remove-layer, and current-layer-context in combination are the functions to compute new sets of layers.
Once you have a new layer context, you can use funcall-with-layer-context or apply-with-layer-context to execute a block of code within that context.
Such contexts are first-class, so you can store them and pass them around.
A bit more detail: For example, you can also do something like this.

(defvar *some-context*
(with-active-layers (l1 l2 l3)
(current-layer-context)))

…and then later…

(funcall-with-layer-context *some-context* (lambda () (do-this) (do-that)))


Pascal

--
Pascal Costanza

Loading...