architecture.hooks icon indicating copy to clipboard operation
architecture.hooks copied to clipboard

Is it possible to compose hook handlers?

Open Ambrevar opened this issue 6 years ago • 9 comments

Say I have a hook of handlers with 1 argument returning 1 argument. If would like to run the successive handlers of the result of the previous one. Looking at the code of run-hook it does not seems possible directly.

I came up with the following function:

(defun run-composed-hook (hook &rest args)
  (apply (apply #'alexandria:compose hook) args))

It works, but my main concern is that this is not part of the description of the hook, the behaviour of indeed decided at the call site, not by the hook itself.

The only way to fix it, as I understand it, is to implement this "composability" feature in cl-hooks itself. Thoughts?

Ambrevar avatar Aug 30 '19 12:08 Ambrevar

To implement this, I suggest a "handler-combination" option. Default would be #'mapcar for instance, my above suggestion would be #'alexandria:compose, but we could imagine others, like #'or (run hooks until one returns non-nil) and #'and (run hooks until one returns nil).

Ambrevar avatar Aug 30 '19 13:08 Ambrevar

You are right that execution order and composition of hooks is not customizable at the moment. The current combination implementation only applies to results after all hooks have been run independently. It is like method combination without call-next-method. Having hooks execute independently was a conscious design decision at the time.

That said, I don't see why additional customization shouldn't be possible. I will have to think about how to generalize the current mechanism. Your suggestion sounds like one possibility, but the interaction with the current combination option is unclear since with compose execution there are no multiple results to combine.

scymtym avatar Aug 31 '19 11:08 scymtym

Thanks for considering it.

Regarding the interaction with the combination, well we could raise a warning when the user choose both #'compose for handlers #'list for result combination. We could handle the few edge cases, but ultimately if we let the user pass their own function, then they must be responsible for their compatibility. It's a fair design in my opinion :)

Ambrevar avatar Aug 31 '19 12:08 Ambrevar

Sorry for taking a long time here. I haven't forgotten about this. Despite not having a lot of time to think about this, I can say right away that I don't like the warning idea too much. I hope to combine composition and combination into a single mechanism so that the problem doesn't arise.

scymtym avatar Sep 07 '19 12:09 scymtym

I'd be happy to work on this :) Would you accept a patch?

Ambrevar avatar Sep 07 '19 12:09 Ambrevar

Sorry for not getting back to you earlier. I can't making any promises about accepting changes. I suggest you fork the repository and work in your fork for now.

scymtym avatar Sep 13 '19 07:09 scymtym

Intuitiveness question: cl-hooks are first-in-last-out queues. Which means if 2 handlers modify the same variable, the last one added will be overridden by the first. I think the most expected behaviour is the reverse. Thoughts?

Within the context of the current issue, it would be as simple as using #'reverse as composition function. @vindarel?

Ambrevar avatar Sep 26 '19 10:09 Ambrevar

@Ambrevar Btw, are you aware of https://gitlab.com/Gnuxie/method-hooks? From a brief look, it seems to have some of the features you are proposing.

scymtym avatar Sep 26 '19 11:09 scymtym

I am, but if I'm not mistaken they only work over methods (and thus objects). I need stand-alone hooks.

Ambrevar avatar Sep 26 '19 11:09 Ambrevar