[英]Get the type information in macros
(defmacro test (&key list &environment env)
(typecase (get-type list env)
(list `(do-something (list ,@list)))
(integer `(do-something (list ,list ,list ,list)))))
(test :list '(1 2 3)) ; => (do-something (list 1 2 3))
(test :list (* 1 2)) ; => (do-something (list (* 1 2) (* 1 2) (* 1 2)))
是否可以在宏擴展時獲取list
類型? (我稱之為get-type
)
它不適用於評估,因為此時變量可能不存在,並且可能會更改某些其他值,因此不應更改。
還是應該讓我的宏擴展成為一種也稱為Typecase的形式?
大多數Lisp方言可以看作是動態鍵入的。 Common Lisp也不例外。 添加了類型和類型聲明以允許在運行時處理類型:用於運行時類型檢查和運行時類型分派。 它還允許Common Lisp實現在編譯時使用類型信息以生成更快的代碼。 使用類型信息進行編譯時類型檢查或在用戶級別使用宏不是目標。 盡管至少在早期,CMUCL(后來的SBCL和Scieneer CL)使用類型聲明和類型推斷來進行一些編譯時類型檢查。 大多數其他實現不進行編譯時類型檢查,而少數實現則進行類型推斷。
請注意,您可以在運行時添加或重新定義類型。 同樣,類型層次結構可以在運行時更改(例如,類層次結構)。
如果您有一個實際的對象,則可以確定Lisp系統在編譯時認為的類型。
但是函數返回值的類型是什么? 該信息可能來自類型聲明或類型推斷。 后者在Common Lisp中是不可移植的-可能存在特定於實現的方法,可以從類型推斷中獲取信息或從庫中使用類型推斷器。
有關函數的類型聲明的信息,Common Lisp的某些實現提供了函數FUNCTION-INFORMATION
(最初是針對ANSI CL提出的,但並未納入實際標准中)。
在LispWorks 6.1中:
CL-USER 42 > (compile (defun foo () (the fixnum 3)))
FOO
NIL
NIL
CL-USER 43 > (declaim (ftype (function () fixnum) foo))
T
CL-USER 44 > (function-information 'foo)
:FUNCTION
NIL
((FTYPE FUNCTION NIL FIXNUM))
另請參見VARIABLE-INFORMATION
。 FUNCTION-INFORMATION
和VARIABLE-INFORMATION
也記錄在CLtL2中,但沒有記錄在Common Lisp Hyperspec中(因為它們不在標准中)。
通常在宏擴展時使用此類類型信息進行操作會打開一整蠕蟲。 對於基本用途,我建議您考慮運行時類型,並創建用於分配運行時類型的代碼-除非您確實在編譯時確實需要類型信息。
除了Rainer的出色回答外 ,請注意Common Lisp包括采用形式和環境的函數constantp 。 這不會涵蓋所有情況,因為有人可以使用變量而不是常量形式來調用宏,但是如果您確實獲得了常量形式,則可以在編譯時檢查其類型:
(defmacro foo (arg &environment environment)
(if (constantp arg environment)
(typecase arg
(string 'string-expansion)
(vector 'vector-expansion)
(number 'number-expansion)
(t 'constant-but-other-type-expansion))
'nonconstant-expansion))
CL-USER> (macroexpand '(foo "hello"))
STRING-EXPANSION
T
CL-USER> (macroexpand '(foo #(1 2 3)))
VECTOR-EXPANSION
T
CL-USER> (macroexpand '(foo 42))
NUMBER-EXPANSION
T
CL-USER> (macroexpand '(foo 'something-else))
CONSTANT-BUT-OTHER-TYPE-EXPANSION
T
CL-USER> (macroexpand '(foo bar))
NONCONSTANT-EXPANSION
T
現在,這似乎有些round回,但是在某些地方它可能很有用。
還是應該讓我的宏擴展成為一種也稱為Typecase的形式?
如果您查看序列操縱函數的開源實現,您可能會發現一些非常有趣的代碼示例。 許多Common Lisp函數(例如reduce )采用序列,這些序列可以是列表或向量。 不幸的是,要在這兩種類型上有效實現降低的代碼是不同的。 例如,看看SBCL的序列函數 。 您將開始了解他們如何處理此問題。 您可能還想看看編譯器宏。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.