-
-
Notifications
You must be signed in to change notification settings - Fork 407
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Functional config & hooks #419
Comments
@vindarel's suggestion: Use |
yeah +1 as discussed I find it a small step but a grand usability and code stability improvement. |
Maybe a (much?) better idea: If we look at the big picture, what we are trying to do here is specialize a class (in the general sense). So what if we did the following: In Next source, define (declaim (type (remote-interface) remote-interface-class))
(defparameter remote-interface-class (find-class 'remote-interface))
; ...
; Then replace every `make-instance 'remote-interface` with
(make-instance (class-name remote-interface-class) ...) Now from the user init.lisp: (defclass my-interface (remote-interface)
((search-engines :initarg my-newvalue-here)
(other-slot :initarg my-other-default-here)))
(setf remote-interface-class (find-class 'my-interface)) Note that This would be illegal (and caught at compile-time): (setf remote-interface-class (find-class 'buffer)) So this allows third-party packages to offer defaults to the user. A key feature here is that CLOS supports multiple inheritance: this effectively allows us to compose defaults. Say, some contributors develop Doom-Next and some others Space-Next. (defclass my-interface (doom-next:remote-interface space-next:remote)
())
(setf remote-interface-class (find-class 'my-interface)) Or, the other way around, I would change the order of the superclasses: (defclass my-interface (space-next:remote doom-next:remote-interface)
())
(setf remote-interface-class (find-class 'my-interface)) We would always have access to all default values of all classes. The type of Note that it does not remove the need for hooks as mentioned above. Thoughts? |
The above example was for |
Forgot one nicety: the (let ((buffer-class (find-class 'my-other-class)))
(make-buffer...))
; Back to normal here. |
A gotcha: slot names could be mistyped. For instance if the user writes (defclass my-interface (remote-interface)
((seach-engines :initarg my-newvalue-here)
(other-slot :initarg my-other-default-here)))
(setf remote-interface-class (find-class 'my-interface)) they might be puzzled as for why the search-engines settings have not been applied. (Note the missing One way around this: use (defclass my-interface (remote-interface)
())
(defmethod initialize-instance :after ((interface my-interface))
(setf (search-engines interface) my-new-value-here)) |
See #484 for an implementation. |
Closing since #484 has been merged. |
A discussion on the general idea of configurability: This is an iteration on
the question of hooks (#383) and
the controversial topic of functional configuration
(#397).
I just had an epiphany when working with the set-url hook:
the handlers take the URL as argument, but not the buffer. So how do the
handler know which buffer they apply to. Calling
(current-buffer)
is wrong:for instance, when we call
reload-buffer
over multiple buffers,(current-buffer)
always returns the same buffer, not the buffer beingreloaded.
Possible fixes:
Add a
buffer
parameter. But it fits badly withrun-composed-hook
whichcalls the next handler over the return value of the previous. Even if we pass
the buffer along, we've got no strong guarantee that no handler is returning a
different buffer, thus breaking the integrity of the hook.
Beside, it really feels like we are passing along an information that should
already be there, because the hook belongs to the buffer.
Replace the named handler by a closure over the buffer. This seems right, but
there is a problem: a closure is anonymous, and hooks don't like lambdas as
the Emacs experience tells us:
Lambdas are not comparable and thus duplicates can't be removed and will
stack up.
They are black boxes to the user, we can't know what they do.
It's impractical to remove a lambda interactively since it has no name.
So what if we created a
handler
class with the following slots:handler that sets the search engine to '("foo" "bar")
.place
: (Optional) If the function is a setter, describe the place being set,e.g.
'(remote-interface buffer-make-hook load-hook)
.value
: (Optional) If the function is a setter, describe the value,e.g.
'(1 2 3)
.We would modify
add-to-hook
to either accept functions orhandler
s but notanonymous functions.
Then
run-hooks
should be able to run both regular functions andthe above
handler
s in a hook.Handlers should be comparable so that the hooks can remove duplicates.
I can think of various criteria for comparison:
EQUAL
. This is only useful forsetters.
In #397, we discussed new
"functional" (stateless) ways to configure Next.
I was suggesting
Obviously this is not the easiest.
We could use helper macros but they would necessarily add unnamed handlers to
hooks... Which is not a problem anymore if we use the above
handler
types!Then we can write macros to set default values (dummy code):
This would allow us to set values this way:
We could also support
to avoid flooding
*after-init-hook*
.Thoughts?
The text was updated successfully, but these errors were encountered: