help-gnu-emacs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Improve this snippet!


From: Pascal J. Bourguignon
Subject: Re: Improve this snippet!
Date: Sat, 22 Jun 2013 11:54:46 +0200
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/24.2 (gnu/linux)


Miguel Guedes <miguel.a.guedes@gmail.com> writes:

> Hello list,
>
> I have the need to dynamically load a list of functions whose purpose it 
> is to provide specific functionality depending on context.
>
> Here is what I hope is an easy to understand example: 
>
> ;; Setup default function bindings
> (setq fn-foo 'default-foo)
>       fn-bar 'default-bar);
>
> (let ((mode-descriptor
>        '("c++-mode"
>          ((foo . specific-c++-mode-implementation-foo)))))
>   (dolist (i (cadr mode-descriptor))
>     (let ((name (car i)))
>        (cond ((eq 'foo name)
>                 (setq fn-foo (cdr i)))
>              ((eq 'bar name)
>                 (setq fn-bar (cdr i)))))))
>
>
> mode-descriptor above has the format:
>  ( mode-name:string
>   ( 
>     (defun-name:symbol . specific-defun-name:symbol)
>     ...
>   )
>  )
>
> Essentially the goal is to initialise fn-foo and fn-bar (and others) with 
> defuns that don't do anything but can be given defun names that carry out 
> specific tasks depending on context.  The specific context is emacs' 
> currently active major mode.
>
> The above logic works fine but I'm looking for the right way of doing 
> this.  Somehow what I came up with seems like a hack that works but isn't 
> quite right.  I'm a beginner to LISP and lack the experience or knowledge 
> a seasoned/pro LISPer surely has.
>
> How would you improve the data structure of `mode-descriptor' and does 
> LISP provide (a) better way(s) to lookup/load the specific defun symbols? 
> Any other comments?

You could use:

             (ecase name
               ((foo) (setf fn-foo (cdr i)))
               ((bar) (setf fn-foo (cdr i))))

instead of cond. (ecase having the advantage over case to signal an
error if the value is not amongst the cases.

But in your case, see below.



Otherwise, it looks good, in the context of emacs configuration.



One thing you may want to add, is a functional wrapper over fn-foo, etc.

The point is to wrap the binding of foo with a fbinding of foo
funcalling or applying it.

       (defun foo (&rest args)
          (apply foo args))


Let's write a macro to do it nicely:

(require 'cl)

(defvar *hooked-functions* '() "List of hooked functions.")

(defmacro define-hooked-function (name default-function-name)
   `(progn
       (defvar ,name nil ,(format "The hook of function %s" name))
       (setq ,name ,default-function-name)
       (defun ,name (&rest args)
          (apply ,name args))
       (pushnew ',name *hooked-functions*)
       ',name))

(defun remove-hooked-function (name)
   (setf *hooked-functions* (remove name *hooked-functions*))
   name)


(defun hooked-function-p (name)
   (not (not (member name *hooked-functions*))))

(defun set-function-hook (hooked-function-name meat-function-name)
   (if (hooked-function-p hooked-function-name)
      (set hooked-function-name meat-function-name)
      (error "%s is not a hooked function." hooked-function-name)))

(defun call-hooked-function (name &rest arguments)
   (apply name arguments))



(defun default-foo (x) (message "default foo %s" x))
(define-hooked-function foo 'default-foo)

(foo 42)                       --> "default foo 42"
(hooked-function-p 'foo)       --> t

(set-function-hook 'foo (lambda (z) (format "anonymous meat %s" z)))

(foo 42)                       --> "anonymous meat 42"
(call-hooked-function 'foo 42) --> "anonymous meat 42"



So your code becomes:

(define-hooked-function foo 'default-foo)
(define-hooked-function bar 'default-bar)

(let ((mode-descriptor
       '("c++-mode"
         ((foo . specific-c++-mode-implementation-foo)))))
  (loop for (name . meat) in (second mode-descriptor)
        do (set-function-hook name meat)))






Once you'd have fun with doing it yourself like that, you can realize
that you have a little problem: if we later use defun on foo, it will
still be on the *hooked-functions* list, so we could still  use
set-function-hook on it, but without further effect (unless the defun
calls the function bound to the foo variable).





The solution would be to ignore all this, and just use the defalias
function:



(defalias 'foo 'default-foo)
(defalias 'bar 'default-bar)

(foo 42) --> "default foo 42"

(defun specific-c++-mode-implementation-foo (z) 
   (message "specific-c++-mode-implementation-foo %s" z))

(let ((mode-descriptor
       '("c++-mode"
         ((foo . specific-c++-mode-implementation-foo)))))
  (loop for (name . meat) in (second mode-descriptor)
        do (defalias name meat)))

(foo 42) --> "specific-c++-mode-implementation-foo 42"



-- 
__Pascal Bourguignon__                     http://www.informatimago.com/
A bad day in () is better than a good day in {}.  
You know you've been lisping too long when you see a recent picture of George 
Lucas and think "Wait, I thought John McCarthy was dead!" -- Dalek_Baldwin


reply via email to

[Prev in Thread] Current Thread [Next in Thread]