起因是purcell的一个commit:
然后我去看了一下各大配置,发现确实defadvice不那么受待见……
这是为什么?
谢谢各位
起因是purcell的一个commit:
然后我去看了一下各大配置,发现确实defadvice不那么受待见……
这是为什么?
谢谢各位
新的㝍法直观易懂易用
我现在是两种都用……毕竟advice-add “形式上”感觉有点多此一举……(必须为此专门写个函数,而不是写在一个sexp里面)
我用advice-add仅仅是在advice函数不止用于一处时……
(defin foo (bar quux)
...)
要求对其 around “修饰”一下。
(defun foo@around (fn bar quux)
(funcall fn bar quux))
(advice-add 'foo :around 'foo@around)
(defadvice foo (around foo@around (bar quux) activate)
ad-do-it)
虽然少写了一行,但是,是不是觉得 ad-do-it
有点可疑?怎么又多了一个 activate
?其实它就相当于 advice-add
,所以真正对等的写法是:
(defadvice foo (around foo@around (bar quux))
ad-do-it)
(ad-active 'foo)
这不就也是两步走了吗?而且还有个让我觉得比较错乱的是 (defadvice foo (around foo@around
这句声明到底谁在 around 谁?也许你说写多了就习惯了,好吧。
接下来再加点需求:处理原函数的参数和返回值
(defun foo@around (fn bar quux)
(let* ((new-bar ...)
(new-quux ...)
(result (funcall fn new-bar new-quux)))
(... process the `result' ...)))
很直观,就是一般函数的写法。
需要用到一堆可疑的 ad-
开头的宏,根本不想写。
另外再补充一点,新的写法其实也可以一步到位:
(define-advice ...)
楼上的内容其实总结起来就是:
新的写法,就是一个普通函数的写法,你所需资源都声明好了,没有隐藏秘密。旧的写法,你需要记住很多额外的东西。
看看 `nadvice.el’ 文档是怎么说的:
划重点:with much fewer bells and whistles
感谢!
话说doom-emacs几乎全是advice-add……
以及define-advice
的参数lambda-list
是什么鬼?
……还有为什么那个commit里几乎所有的advice函数里都有一个&rest _
参数?
因为advice函数会收到被advice函数的所有参数,如果你不关心这些参数就用&rest _
,大部分语言都用_
表示不关心的参数,而且Emacs byte compiler会无视_
,不会因为你没用参数报unused variable
警告。
(defun myfunc (a b c)
(list a b c c))
(defun myadvice@myfunc (&rest _)
(print "side effect"))
(advice-add 'myfunc :before 'myadvice@myfunc)
nil
(myfunc 1 2 3)
"side effect"
(1 2 3 3)
好吧……知道了。
然而我发现define-advice用得很少……大部分人还是defun...advice-add
的形式……
我能想到的原因就是define-advice对advice function的命名……还有对老Emacs版本的兼容问题……
哈哈哈抱歉,看错了
这个有点奇怪,平时大家都prefer lambda to defun
,现在这里又prefer defun
了
主要还是它对advice function的命名……如果我有一个很多字符的函数名,要给它加一个advice,那么advice function的名字就可能变成这样:
cua--deactivate-rectangle@suspend-whole-line-or-region-local-mode
而且它的语法也令人费解(比如参数LAMBDA-LIST是什么鬼?)。
最重要的一点可能是它只兼容25以后的Emacs版本。而从defadvice到advice-add的转换很多人都是在24的时代就做了。
所以就是……只有一点好处(代码比较简洁)但有很多不便的事情我为什么要做?
顺便一提它的展开式是
(prog1
(defun ...)
(advice-add ...))
可能还会拖慢那么一丁点点点的性能(毕竟等于多套了个prog1)。
=========================
但是我这种强迫症患者当然是会全部都用define-advice的,除了那个advice function本身还被别的地方引用,或者我要添加的advice function是个lambda函数……
请教个问题,原来用 defadvice
的时候,可以在 before 的时候 (ad-set-arg i v)
来改变变量的值,那在 (advice-add)
的时候,又该怎么做呢?
比如原来可以这样改变值
(defadvice xx (before xx-a (a b) activate)
(ad-set-arg 0 1))
那现在用不了 ad-set-arg 的时候,该怎么做呢?
before 的时候改参数是为了传递给谁?根本没有接收者嘛。
around 的时候才有改参数的必要。新的写法无需改参数,直接传递新的值作为参数就可以可:
;; old style
(defadvice xx (around xx-a (a b) activate)
(ad-set-arg 0 1)
...)
;; new style
(defun xx-a (orig-fn a b)
(let ((new-a 1))
(funcall orig-fn new-a b)))
原来的那个 before 的是来自于 spacemacs 的 Chinese layer 里的!不过,现在我明白怎么弄了!感谢!
所以啊,老的写法早该废弃,新的写法清晰易懂。
我画个时序图来说明 before 和 around 的区别,以及为什么 before(或 after)无必要修改参数:
+-------+ +------+
| Emacs | | User |
+-------+ +------+
| |
| |
(funcall ad@before a b) |
| --------------> |
| (defun ad@before (a b)
| ...)
| <-------------- |
(funcall orig-fn a b) |
| |
| |
v v
+-------+ +------+
| Emacs | | User |
+-------+ +------+
| |
| |
(funcall ad@around orign-fn a b) |
| --------------> |
| (defun ad@around (orig-fn a b)
| (funcall orig-fn a b))
| <-------------- |
| |
| |
v v
确实清晰了很多,解决了我不少疑惑。