From: Krzysztof Drewniak on
I am writing a game and would like a hook implementation. I have most
of the groundwork but cannot figure out how to actually insert the
hook code into a function. Right now, i have a function
(hooks object string), which returns the hooks as a form that looks
like ((print x) (+ 2 2)) [the actual form is different BTW]. Now, I
would like a function (or macro, whatever) (call-hooks object string)
so that given the return value of ((print x) (+ 2 2)) from hooks,
would make
(let ((x 3))
(call-hooks object string))
become
(let ((x 3))
(progn (print x) (+ 2 2)))
I am really lost on how to implement this. Any help?

Krzysztof Drewniak


--
X-Real-Email-With-Antispam: krzysdrewniak at gmail dot com
pgp key on keyserver.ubuntu.com and maybe some other place too
From: Tamas K Papp on
On Mon, 03 May 2010 12:43:47 +0000, Krzysztof Drewniak wrote:

> I am writing a game and would like a hook implementation. I have most of
> the groundwork but cannot figure out how to actually insert the hook
> code into a function. Right now, i have a function (hooks object
> string), which returns the hooks as a form that looks like ((print x) (+
> 2 2)) [the actual form is different BTW]. Now, I would like a function
> (or macro, whatever) (call-hooks object string) so that given the return
> value of ((print x) (+ 2 2)) from hooks, would make
> (let ((x 3))
> (call-hooks object string))
> become
> (let ((x 3))
> (progn (print x) (+ 2 2)))
> I am really lost on how to implement this. Any help?
>
> Krzysztof Drewniak

Would generic functions work for you? Eg

(defgeneric call-hooks (object id))

(defmethod call-hooks ((object number) (id (eql 'foo)))
(print object)
(+ 2 2))

(let ((x 3))
(call-hooks x 'foo)) ; => 4, also prints 3

Note that I use symbols (give faster lookups, bit more lispy) because
EQL is not for comparing strings. You can even define a fallback
callback (that rolls off the tongue nicely, doesn't it? :-) as

(defmethod call-hooks ((object number) id)
(print "What should I do with this number?"))

(defmethod call-hooks (object id)
(print "The most general callback, ever."))

Hope this helps,

Tamas
From: Pascal J. Bourguignon on
Krzysztof Drewniak <krzysdrewniakNOSPAM(a)gmail.com> writes:

> I am writing a game and would like a hook implementation. I have most
> of the groundwork but cannot figure out how to actually insert the
> hook code into a function. Right now, i have a function
> (hooks object string), which returns the hooks as a form that looks
> like ((print x) (+ 2 2)) [the actual form is different BTW]. Now, I
> would like a function (or macro, whatever) (call-hooks object string)
> so that given the return value of ((print x) (+ 2 2)) from hooks,
> would make
> (let ((x 3))
> (call-hooks object string))
> become
> (let ((x 3))
> (progn (print x) (+ 2 2)))
> I am really lost on how to implement this. Any help?

CLOS already has hooks implemented.
They're called :before and :after methods.

So if you define a generic function:

(defmethod f ((object <some-class>))
...)

you can add a hook:

(defmethod f :after ((object <some-class>))
(do-something-after))

(or :before for before).


But these would be "programmer-level" hooks.



If you want to provide user-level hooks, such as in emacs, then you
have to define for each function whether it will have hooks and where
(before, in the middle, after, etc).

Then you can write:

(define-hook *f-before-hook*)
(define-hook *f-middle-hook*)
(define-hook *f-after-hook*)

(defun f (...)
(call-hook *f-before-hook* ...)
(do-something)
(call-hook *f-middle-hook* ...)
(do-something-else)
(call-hook *f-after-hooks* ...))


(add-hook *f-before-hook* (lambda (...) (print (list before-f ...))))
(add-hook *f-before-hook* (lambda (...) (print "Let's start! Ho!")))
(add-hook *f-after-hook* (lambda (...) (print (list after-f ...))))
(add-hook *f-after-middle* (function my-middle-hook))

(f ...)



(defmacro define-hook (name &optional documentation)
`(defvar ,name nil ,(or documentation "A hook.")))

(defmacro call-hook (hook &rest arguments)
(dolist (meat hook)
(apply meat arguments)))

(defmacro add-hook (hook function)
`(push ,function ,hook))


The point is that the meat you put on the hook, won't be a mere sexp
or list of sexps, but a full fleshed function. Remember, lisp is a
functional programming language, with first class functions, so make
good use of them!



[Besides, in general (print x) alone doesn't mean anything, because x
is not defined; you need a closure to have a variable x to give to
print: (lambda (x) (print x)).]

--
__Pascal Bourguignon__
http://www.informatimago.com
From: His kennyness on
Krzysztof Drewniak wrote:
> I am writing a game and would like a hook implementation. I have most
> of the groundwork but cannot figure out how to actually insert the
> hook code into a function. Right now, i have a function
> (hooks object string),

<cough> Care to tell us what the string parameter would have been called
had you given it a name describing its semantics as opposed to type?

ie, is that the selector?

> which returns the hooks as a form that looks
> like ((print x) (+ 2 2)) [the actual form is different BTW].

Ugh, you mean symbolic form, right? That you want to eval? (But which
won't work to capture 'x). Let's have these looks as anonymous
first-class functions by runtime, m'kay?

> Now, I
> would like a function (or macro, whatever) (call-hooks object string)
> so that given the return value of ((print x) (+ 2 2)) from hooks,
> would make
> (let ((x 3))
> (call-hooks object string))

> become
> (let ((x 3))
> (progn (print x) (+ 2 2)))
> I am really lost on how to implement this. Any help?

Ain't happenin. You need your hooks to take parameters and feed the
binding of x to the hook. In something more complex such as a game, you
will likely pass one parameter that is pretty much the entire universe
(so the hooks can do what they like) and a second parameter that will be
some object in the universe, such as a player instance.

if that sounds too limiting, hooks can take &rest and then as long as
you otherwise organize things so you know which kinds of hooks get
passed what the caller can do the right thing for each hook.

kt
From: Thomas A. Russ on
Krzysztof Drewniak <krzysdrewniakNOSPAM(a)gmail.com> writes:

> I am writing a game and would like a hook implementation. I have most
> of the groundwork but cannot figure out how to actually insert the
> hook code into a function. Right now, i have a function
> (hooks object string), which returns the hooks as a form that looks
> like ((print x) (+ 2 2)) [the actual form is different BTW]. Now, I
> would like a function (or macro, whatever) (call-hooks object string)
> so that given the return value of ((print x) (+ 2 2)) from hooks,
> would make
> (let ((x 3))
> (call-hooks object string))
> become
> (let ((x 3))
> (progn (print x) (+ 2 2)))
> I am really lost on how to implement this. Any help?

Well, the real problem you have here is that unless you can do this at
macro-expansion time, you won't be able to capture the lexical scope of
the variable X. By the very nature and design of lexical arguments, you
can't get access to them from something you pass in. The code has to be
in the lexical scope of the variable at the time it is compiled.

Partly for that reason, one of the standard ways of defining hooks in
lisp languages involves making them be functions that you can apply to
arguments. That means that you need to know the argument application
conventions. But if you are trying to pass the value "by name" as you
seem to be doing, you already need to know that X is the name of the
variable.

So, I would suggest that you follow the standard lisp convention design
model of using functions for your hooks. So then your code will look
like

Hook:

(lambda (x) (print x) (+ 2 2))

Invocation:

(let ((x 3))
(call-hooks object string x))

Implementation. I assume there can be more than one hook since you
named the function CALL-HOOKS instead of CALL-HOOK


(defun call-hooks (object string argument)
(dolist (f (get-hooks object string))
(funcall f argument)))

If there can only be a single one and you want to be able to return a
value, then I would do

(defun call-hook (object string argument)
(let ((f (get-hook object string)))
(when f
(funcall f argument))))



--
Thomas A. Russ, USC/Information Sciences Institute