簡體   English   中英

在宏中獲取類型信息

[英]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-INFORMATIONVARIABLE-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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM