อันตรายที่สำคัญคือความหมายผูกพันที่ไม่ได้กำหนดตัวแปรตัวแปรคือไม่กำหนดด้วยdefvar
และเพื่อน ๆ เปลี่ยนกับlexical-binding
: โดยไม่ได้let
ผูกทุกอย่างแบบไดนามิก แต่มีlexical-binding
ตัวแปรที่ไม่ได้กำหนดเปิดใช้งานจะผูกพันlexicallyและ elided แม้สมบูรณ์หากไม่ได้ใช้ในขอบเขตของคำศัพท์ในปัจจุบัน .
รหัสเก่าบางครั้งอาศัยสิ่งนี้ เพื่อหลีกเลี่ยงการขึ้นต่อกันอย่างหนักสำหรับฟีเจอร์เสริมมันจะผูกตัวแปรแบบไดนามิกโดยไม่ต้องมีไลบรารี่ที่เกี่ยวข้องหรือประกาศตัวแปรเอง:
(let ((cook-eggs-enabled t))
(cook-my-meal))
หากคุณลักษณะการทำอาหารเป็นตัวเลือกเราไม่ต้องการบังคับให้มีการพึ่งพาที่ไม่จำเป็นกับผู้ใช้ดังนั้นเราจึงไม่ใช้(require 'cook)
และพึ่งพาcook-my-meal
ฟังก์ชั่นการ โหลดอัตโนมัติ
เป็นที่ชัดเจนสำหรับผู้อ่านของมนุษย์ที่cook-eggs-enabled
ไม่ได้เป็นตัวแปรในตัวเครื่อง แต่ยังคงอ้างถึงตัวแปรแบบไดนามิกทั่วโลกจากcook
ไลบรารีที่นี่ หากไม่มีlexical-binding
รหัสนี้จะทำงานตามที่ต้องการ: cook-eggs-enabled
ถูกผูกไว้แบบไดนามิกไม่ว่าจะถูกกำหนดไว้หรือไม่ก็ตาม
lexical-binding
อย่างไรก็ตามด้วยการแบ่งมัน: cook-eggs-enabled
ถูกผูกไว้กับlexically (และปรับให้เหมาะสมเนื่องจากไม่ได้ใช้) ดังนั้นตัวแปรแบบไดนามิกทั่วโลกcook-eggs-enabled
จึงไม่เคยสัมผัสเลยและยัง ถูกเรียกnil
ตามเวลาcook-my-meal
ดังนั้นเราแปลกใจที่ไม่มีไข่ ในมื้ออาหารของเรา
โชคดีที่ปัญหาเหล่านี้เป็นเรื่องง่ายที่จะสังเกตเห็น : คอมไพเลอร์ไบต์ตามธรรมชาติเตือนเกี่ยวกับคำศัพท์ที่ไม่ได้ใช้ที่นี่
การแก้ไขง่ายๆคือ: ทั้งเพิ่ม(require 'cook)
(สำหรับคุณสมบัติที่ไม่ได้อยู่แล้วไม่จำเป็นจริงๆ) หรือเพื่อหลีกเลี่ยงยากที่อ้างอิง-ประกาศตัวแปรเป็นตัวแปรแบบไดนามิกในรหัสของคุณเอง มีdefvar
รูปแบบพิเศษสำหรับสิ่งนี้:
(defvar cook-eggs-enabled)
สิ่งนี้นิยามcook-eggs-enabled
ว่าเป็นตัวแปรแบบไดนามิก แต่ไม่มีผลกับ docstring, load-history
(และfind-variable
และเพื่อน ๆ ) หรือสิ่งอื่นใดยกเว้นลักษณะการผูกของตัวแปร
cook-eggs-enabled
จะไม่ถูกดึงออกเมื่อทำlet
เสร็จหรือไม่ ฉันค่อนข้างแน่ใจว่าฉันพบข้อผิดพลาดเช่นนี้มาก่อน defvar เกิดขึ้นภายในlet
และlet
หลังจากนั้นคืนค่าตัวแปรให้เป็นสถานะเริ่มต้น (โมฆะ)