Break OO privacy: Difference between revisions

→‎{{header|Common Lisp}}: Less text, more code.
(→‎{{header|Common Lisp}}: Break off and extend paragraph on inheritance.)
(→‎{{header|Common Lisp}}: Less text, more code.)
Line 188:
The concept of privacy in Lisp is located in the package system. An external symbol S in package P can be accessed using P:S. If S is internal then P:S results in error at read time. This is completely independent of context and orthogonal to the use of symbols to denote class slots (instance variables), methods, functions, class names themselves, global variables, et cetera. Even if the private symbol appears in a literal piece of data like a quoted list, it is an error to refer to it: <code>(1 2 3 P:S)</code>. The internal symbol S in package P can be accessed using <code>P::S</code>. This is easy to do because programmers are assumed to be responsible grownups who can be trusted to know what they are donig. Also, note that this privacy is a property of the relationship between a symbol and a package. A symbol can be present in more than one package, such that it can be internal in some of them, and external in others.
 
<lang lisp>(defpackage :funky
There is another concept of symbolic privacy in Lisp, and that is the use of symbols which do not belong to any package, the so-called uninterned symbols. It is not possible to resolve an identifier token in the source code to some uninterned symbol, because lexical resolution of symbols in textual code or data goes through the package system. With the help of some macros, a module of code can do things like create classes which have slots that are named using uninterned symbols, making them difficult to access for outsiders, if not impossible.
;; only these symbols are public
(:export :widget :get-wobbliness)
;; for convenience, bring common lisp symbols into funky
(:use :cl))
 
;; switch reader to funky package: all symbols that are
;; not from the CL package are interned in FUNKY.
 
(in-package :funky)
 
(defclass widget ()
;; :initarg -> slot "wobbliness" is initialized using :wobbliness keyword
;; :initform -> if initarg is missing, slot defaults to 42
;; :reader -> a "getter" method called get-wobbliness is generated
((wobbliness :initarg :wobbliness :initform 42 :reader get-wobbliness)))
 
;; simulate being in another source file with its own package:
;; cool package gets external symbols from funky, and cl:
(defpackage :cool
(:use :funky :cl))
 
(in-package :cool)
 
;; we can use the symbol funky:widget without any package prefix:
(defvar *w* (make-instance 'widget :wobbliness 36))
 
;; ditto with funky:get-wobbliness
(format t "wobbliness: ~a~%" (get-wobbliness *w*))
 
;; direct access to the slot requires fully qualified private symbol
;; and double colon:
(format t "wobbliness: ~a~%" (slot-value *w* 'funky::wobbliness))
 
;; if we use unqualified wobbliness, it's a different symbol:
;; it is cool::wobbliness interned in our local package.
;; we do not have funky:wobbliness because it's not exported by funky.
(unless (ignore-errors
(format t "wobbliness: ~a~%" (slot-value *w* 'wobbliness)))
(write-line "didn't work"))
 
;; single colon results in error at read time! The expression is not
;; even read and evaluated. The symbol is internal and so cannot be used.
(format t "wobbliness: ~a~%" (slot-value *w* 'funky:wobbliness))
</lang>
 
Output using CLISP:
 
<pre>wobbliness: 36
wobbliness: 36
didn't work
*** - READ from #<INPUT BUFFERED FILE-STREAM CHARACTER #P"funky.lisp" @44>:
#<PACKAGE FUNKY> has no external symbol with name "WOBBLINESS"</pre>
 
=={{header|E}}==
Anonymous user