關於第一個問題 - 有在計劃全局沒有單一的慣例。有些人使用*foo*
(因爲與CL中的命名約定不同,正如我在上面的評論中所說的),有些人使用CAPITALS(在大小寫敏感的Schemes中),但都不夠流行以至於不被視爲慣例。
第二個問題更爲微妙。是的,有一個單一的全局命名空間的缺陷。這些錯誤與Javascript 和中的錯誤相同,如Common Lisp中所述:加載隨機代碼可能會改變其他代碼的含義 - 因此,如果我要加載兩個庫,並且都定義了一些foo
全局函數,那麼這些庫中的一個會打破另一個。這不是一個新問題,並且有各種方式來處理它。
最明顯的做法是避免使用不屬於公共接口的全局變量。因此,例如,而不是
(define helper ...)
(define foo ...)
你寫
(define foo
(let()
(define helper ...)
(define foo ...)
foo))
,如果你在你的界面具有多種功能,你做同樣的事情(例如,用球拍的define-values
或使用功能的列表)。請注意,這具有雙重保護:helper
之類的內部事物不會污染全局命名空間,因此它不會破壞其他代碼 - 而定義helper
的其他代碼不會破壞此代碼。這也包括foo
,以防遞歸調用。
當你想要快速編寫這樣的代碼時,一個相關的黑客就是編寫這樣的代碼:在第一個版本中,Scheme編譯器無法將foo
編譯成快速循環,因爲綁定稍後可能會發生變化,但使用第二個版本優化是可能的。
當您沒有模塊系統時,另一種防禦性編程形式是獲取對您的代碼非常重要的值。例如,你可以這樣做:
(define foo
(let ([+ +] [- -] [* *] [/ /] ... more ...)
(define helper ...)
(define foo ...)
foo))
現在的代碼有自己的不可變綁定的算術運算,它保護它免受變化在未來這些操作,並允許算術的安全優化,在此代碼。
所有這些都是一種窮人模塊系統的約定,這與Javascript中常見的(function() { ... })()
類似。但是,當然,一個合適的模塊系統使得這更方便,更好地運行。較新的Javascript版本和較新的Scheme版本都面臨着某種模塊系統的這個問題,而在Scheme中,趨勢是僅使用模塊並避免load
爲不可靠的黑客攻擊。因爲你可能會覆蓋全局變量或函數從其他文件覆蓋。解決方法是與CL的包系統,它可以採取行動作爲一種弱模塊系統,它的弱點在於它給你提供了單獨命名空間的便利,但是命名空間仍然是全局的,這意味着優化仍然非常困難。換句話說,這些命名空間不會給你傳統模塊系統具有「封閉的世界假設」,這意味着編譯器仍然不能假定綁定不會改變。)
@ [Will Ness](http://stackoverflow.com/users/849891/willness )哈哈忘記了那些無言者?你有什麼經驗?我真的不想從一開始就弄錯我的代碼。 – usernvk 2013-04-29 18:28:07
@WillNess:'* foo *'約定不適用於全局變量,它適用於特殊變量,因爲如果名稱用於某個地方的本地詞法作用域中,那麼特殊聲明對代碼可能是災難性的。一些Schemers也將它用於具有特定狀態的全局變量,如自定義全局變量。 – 2013-04-30 06:04:31
@EliBarzilay感謝您的澄清。 :) – 2013-04-30 08:12:34