(funcall #'foo) vs (funcall 'foo) — #:g1

Posted 2011-02-28 10:51:00 GMT

Twitterでもぶつぶつ言っていたのですが、(FUNCALL 'FOO)と、(FUNCALL #'FOO)では色々と意味が違ってきます。
基本的に、(FUNCTION FOO)を使った場合は、関数オブジェクトを扱っているような感じかなと思いますし、(QUOTE FOO)の場合は、SYMBOL-FUNCTIONを実行時に呼ぶという感じかなと思います。
他にもなんやかんやとありますが、インライン展開でも違いがあることがあるというのをみつけたので記念にメモ。
元はといえば、SBCLのFUNCTIONのところのコメントに書いてあったことなのですが、インライン展開にも違ってくるようです。
どういうことかというと、

(declaim (inline bar))
(defun bar ()
  "こんにちは")
と定義しておいて、それを呼び出す側で、 (quote)と (fuction)のものを比較してみます。

quoteを使う

(defun foo ()
  (funcall 'bar))
(foo)
; disassembly for FOO
; 079DBA04:       BA18000000       MOV EDX, 24                ; no-arg-parsing entry point
;       09:       488B05A0FFFFFF   MOV RAX, [RIP-96]          ; #<FDEFINITION object for BAR>
;       10:       B908000000       MOV ECX, 8
;       15:       FF7508           PUSH QWORD PTR [RBP+8]
;       18:       FF6009           JMP QWORD PTR [RAX+9]
;       1B:       CC0A             BREAK 10                   ; error trap
;       1D:       02               BYTE #X02
;       1E:       18               BYTE #X18                  ; INVALID-ARG-COUNT-ERROR
;       1F:       54               BYTE #X54                  ; RCX
当然かもしれませんが、インライン展開されません。

functionを使う

(defun foo2 ()
  (funcall #'bar))
(foo2)
; disassembly for FOO2
; 083A0BC4:       488B15A5FFFFFF   MOV RDX, [RIP-91]          ; "こんにちは"
                                                              ; no-arg-parsing entry point
;       CB:       488BE5           MOV RSP, RBP
;       CE:       F8               CLC
;       CF:       5D               POP RBP
;       D0:       C3               RET
;       D1:       CC0A             BREAK 10                   ; error trap
;       D3:       02               BYTE #X02
;       D4:       18               BYTE #X18                  ; INVALID-ARG-COUNT-ERROR
;       D5:       54               BYTE #X54                  ; RCX
展開してみると埋め込まれていることが分かります。

大抵の場合においてFUNCTIONを使っておけば良いかなというところで、あえてFUNCTIONを使わないのだという場合には、むしろ(FUNCALL (SYMBOL-FUNCTION 'FOO))と書いた方がわかりやすいかもしれません。まあ、(FUNCALL 'FOO)の方が(FUNCALL #'FOO)より見た目が短かくて簡潔なのですが…。

comments powered by Disqus