Mengganti operator khusus menggunakan MACROLET?

Saya mencoba mengganti IF secara lokal (tujuannya adalah untuk menyematkan DSL).

Perilakunya benar-benar membuatku bingung. Di SBCL:

(macrolet ((if (x) x)) 
  (if 'x)) ; => X

(macrolet ((if (x) x)) 
  (let ((x 2)) 
    (if 'x))) ; => Execution of a form compiled with errors.

(let ((x 2)) 
  (macrolet ((if (x) x)) 
    (if 'x))) ; => Execution of a form compiled with errors.

Adakah yang tahu mengapa ia berperilaku seperti itu?

Saya juga mencoba menggunakan simbol yang diinternir xxx::if tetapi tidak berhasil, SBCL masih menganggapnya sebagai operator khusus apa pun paketnya.

Apakah ada cara untuk mengganti operator khusus secara lokal dengan benar? Atau setidaknya secara harfiah menimpa, yang berarti seseorang dapat mengetikkan if dan itu akan diperluas (menggunakan sihir pembaca mungkin membantu tetapi saya tidak begitu paham dengan itu).

Pembaruan (Terpecahkan): Pesan kesalahan persis untuk tanggal 2 yang saya terima

Execution of a form compiled with errors.
Form:
  (MACROLET ((IF (X)
             X))
  (IF 'X))
Compile-time error:
  Lock on package COMMON-LISP violated when binding IF as a local macro while in
package COMMON-LISP-USER.
See also:
  The SBCL Manual, Node "Package Locks"
  The ANSI Standard, Section 11.1.2.1.2
   [Condition of type SB-INT:COMPILED-PROGRAM-ERROR]

Yang ke 3 serupa.

Ternyata menggunakan xxx::if membuatnya berfungsi. Saya hanya tidak membayangi simbol dengan benar pada percobaan sebelumnya.

Cara sederhana untuk membuatnya berfungsi:

(defpackage p)
(macrolet ((p::if (x) x)) 
  (let ((x 2)) 
    (p::if 'x))) ; => X

Jika Anda ingin mewarisi operator CL biasa lainnya di P maka lakukan saja (defpackage p (:use :cl) (:shadow if)).


person BlueFlo0d    schedule 29.02.2020    source sumber
comment
Saya mendapatkan kesalahan dari yang pertama, Kunci pada paket COMMON-LISP dilanggar saat mengikat IF sebagai makro lokal   -  person Barmar    schedule 29.02.2020
comment
Jika saya menggunakan p::if saya tidak mendapatkan kesalahan dari versi ke-2 dan ke-3, hanya peringatan tentang variabel X yang tidak digunakan. Hal ini dapat dimengerti karena Anda mengutip x di badan, bukan menggunakan variabel.   -  person Barmar    schedule 29.02.2020
comment
Keduanya mengembalikan 2 tanpa peringatan jika saya menghapus kutipan sebelum x.   -  person Barmar    schedule 29.02.2020
comment
Ok itu menarik, sebelumnya saya melakukan sb-ext:unlock-package :cl. Mungkin ada gangguan pada penguncian paket? Izinkan saya mencoba dengan REPL baru.   -  person BlueFlo0d    schedule 29.02.2020
comment
Saya masih mendapatkan kesalahan yang sama ketika saya menjalankan (let ((x 2)) (macrolet ((cl-user::if (x) x)) (cl-user::if 'x))) di repl baru. Saya menggunakan SBCL 1.5.1. Apakah implementasi ini bergantung?   -  person BlueFlo0d    schedule 29.02.2020
comment
Jangan gunakan cl-user::if, buat paket Anda sendiri dan gunakan mypackage::if   -  person Barmar    schedule 29.02.2020
comment
Oke (defpackage p (:use :cl) (:shadow if)) (macrolet ((p::if (x) x)) (p::if 'x)) berfungsi untuk saya sekarang. Terimakasih banyak!   -  person BlueFlo0d    schedule 29.02.2020
comment
Saya baru saja melakukan (make-package 'p) dan kemudian menggunakan p::if. Tidak perlu mewarisi atau membayangi apapun.   -  person Barmar    schedule 29.02.2020
comment
Harap edit pertanyaan dan tampilkan pesan kesalahan persis seperti yang Anda dapatkan.   -  person Barmar    schedule 29.02.2020
comment
Tentu, saya juga ingin menggunakan beberapa hal lain yang disediakan CL di DSL (itulah sebabnya saya ingin menyematkannya). Oke, saya memperbarui pertanyaannya sekarang.   -  person BlueFlo0d    schedule 29.02.2020
comment
Perhatikan bahwa (defpackage p) dan (make-package 'p) tidak ditentukan untuk Common Lisp portabel. Tidak diketahui paket mana yang mereka gunakan. SBCL terkenal tidak menggunakan apa pun.   -  person Rainer Joswig    schedule 29.02.2020


Jawaban (1)


Mengikat simbol if (yang ada di paket Common Lisp, common-lisp:if) adalah perilaku tidak terdefinisi .

Jika Anda ingin membuat bahasa makro yang menggunakan beberapa nama simbol yang sama dengan Common Lisp standar, sebaiknya gunakan paket berbeda, sehingga yang terlihat seperti if sebenarnya adalah mypackage:if.

Lebih detail: bagian 11.1.2.1.2 menyatakan bahwa semua pengikatan simbol paket Common Lisp adalah perilaku tidak terdefinisi kecuali jika diizinkan secara eksplisit.

Mengikat simbol Common Lisp dengan macrolet secara eksplisit diperbolehkan dalam subklausul 11.1.2.1.2.1:

"Jika simbol eksternal paket COMMON-LISP tidak didefinisikan sebagai fungsi standar, makro, atau operator khusus, simbol tersebut diperbolehkan untuk mengikatnya secara leksikal sebagai makro (misalnya, dengan makrolet).

Jadi, misalnya, karena konstanta pi (yaitu cl:pi) bukan merupakan fungsi, makro, atau operator khusus, (macrolet ((pi ...)) ...) dapat dievaluasi dengan program yang sesuai.

person Kaz    schedule 02.03.2020