From: ··········@gmail.com
Subject: Information hiding in CLOS?
Date: 
Message-ID: <1161252662.432710.312570@k70g2000cwa.googlegroups.com>
So I come from a {..} background, and I've been reading up details of
CLOS. (must say it is awesome!).

One thing I could not find: In C++ Class definitions, we have public,
protected and private members.  Is there anything similar in lisp?  If
not, is there a workaround, or is it not needed?

sanket.

From: Ken Tilton
Subject: Re: Information hiding in CLOS?
Date: 
Message-ID: <vKKZg.5$zi7.1@newsfe09.lga>
··········@gmail.com wrote:
> So I come from a {..} background, and I've been reading up details of
> CLOS. (must say it is awesome!).
> 
> One thing I could not find: In C++ Class definitions, we have public,
> protected and private members.  Is there anything similar in lisp?  If
> not, is there a workaround, or is it not needed?

Depends on what you use hiding for. Self-documentation? To enforce an 
API? Probably the closest would be exporting/not exporting symbols from 
a package in which the class would be defined. One can also leave off 
defining readers and/or writers and then use slot-value internally.

More fundamentally, as a rule Lisp does not try to get correct code out 
of people by hog-tying them.

kt

-- 
Cells: http://common-lisp.net/project/cells/

"I'll say I'm losing my grip, and it feels terrific."
    -- Smiling husband to scowling wife, New Yorker cartoon
From: Kaz Kylheku
Subject: Re: Information hiding in CLOS?
Date: 
Message-ID: <1161278503.530093.67210@f16g2000cwb.googlegroups.com>
··········@gmail.com wrote:
> So I come from a {..} background, and I've been reading up details of
> CLOS. (must say it is awesome!).
>
> One thing I could not find: In C++ Class definitions, we have public,
> protected and private members.

This is an artifact of C++ classes having a double role. C++ classes
not only define the structure of run-time objects, but they also serve
as namespaces.

If you declare some x in a class C, then that x is a member of the C
namespace, i.e. it is the symbol C::x. This is a separate relationship
from x being a member of the class C. The latter semantic relationship
can exist independently of namespace containment.

For instance, why couldn't some A::b be a member of class C?

  // fictitious example
  class C {
     typedef int A::b;
     double A::z;
  };

C++ has namespaces, but it hadn't always. If C++ had had them from the
early beginnings, perhaps classes would work differently today.

If classes didn't serve as namespaces, then it would have to be
namespaces which control access. And namespaces would no longer be
declaration regions, either. You would need a way of declaring that
some name X exists in this namspace, under such and such protection,
without actually declaring that name to have any semantics in the
programming language, because you would actually want to use that name
elsewhere for naming something. A ``symbol'' keyword might do the
trick:

  namespace A {
  private:
    symbol X; // X exists in namespace and is private
  public:
    symbol Y;
    symbol C;
  }

Now you can continue withour imaginary flavor of C++ and use the
symbols:

  int A::X = 3; // error, we are not in A namespace, and X is private

  namespace A {
     int A::X; // ok, we are in A.
     int X; // unqualified X means A::X
  }

  class A::C {   // C is public, so this is okay
    int A::X;  // error, we are not in A
    void A::Y(); // Good, Y is public in A.
  };

  namespace A {
    class C { // really A::C
      int X;  // Okay: this is A::X and we are in A
    };
  }

  A::C c; // OK, we are not in A, but C is public.

  c.X = 3; // error, no symbol X known in this scope.

  c.A::X = 3; // Error, A::X is private

  c.A::Y(); // Okay, since Y is public.

  using A::Y; // import it

  c.Y(); // use unqualified

This gives a kind of gist of how Lisp packages work, or how a very
similar concept might work in C++ syntax. Symbols are entered into
packages, and packages control symbol visibility (external or
internal).  When the reader reads Lisp expressions, it always has a
current package context. Unqualified names are resolved with respect to
that current package. Qualified names are used to refer out of the
package.

You can use symbols from any package to name an entity and its
components. A class can be X::Y, with a slot being Z::W. Some method
M::N can be specialized to take a parameter of that class.

In Common Lisp, you can import names that live in a package into
another package.

The internal symbols can always be accessed with a double colon. If S
is internal to package P, then P::S can always refer to it, but P:S is
erroneous. If S is an external symbol in package P, then either P:S or
P::S can refer to it.

Use internal symbols to name things that are intended to be private,
and don't document those things for others to use. Of course, people
can still discover those things and use the double colon to use your
symbols anyway.
From: Lars Rune Nøstdal
Subject: Re: Information hiding in CLOS?
Date: 
Message-ID: <pan.2006.10.19.10.56.17.376955@gmail.com>
On Thu, 19 Oct 2006 03:11:02 -0700, sankymoron wrote:

> not needed?

It's not needed.


(defpackage :Encapsulation
  (:use :cl))
(in-package :Encapsulation)


(defclass SomeClass ()
  ((public-member :accessor public-member-of :initarg :public-member)
   (must-be-a-sane-value :reader must-be-a-sane-value-of :initform ".")))
(export '(SomeClass public-member-of))


(defmethod (setf must-be-a-sane-value-of) ((new-value string) (some-class SomeClass))
  "Ensures stored value is a string ending with a #\."
  (unless (char= (char new-value (- (length new-value) 1)) #\.)
    (error "`must-be-a-sane-value' must end with a #\."))
  (setf (slot-value some-class 'must-be-a-sane-value) new-value))
(export 'must-be-a-sane-value-of)


#||||||#


(defpackage :Test
  (:use :cl :Encapsulation))
(in-package :Test)


(defun test ()
  (let ((some-instance (make-instance 'SomeClass)))
    (setf (public-member-of some-instance) "anything")
    (setf (must-be-a-sane-value-of some-instance) "hello.")
    
    (handler-case
        (setf (must-be-a-sane-value-of some-instance) "non sane")
      (t ()
        (write-line "..maintaining sane state..")))

    (values
     (public-member-of some-instance)
     (must-be-a-sane-value-of some-instance))))
(export 'test)


#||||#


Test> (test)
..maintaining sane state..
"anything"
"hello."

-- 
Lars Rune Nøstdal
http://lars.nostdal.org/
From: Pascal Bourguignon
Subject: Re: Information hiding in CLOS?
Date: 
Message-ID: <87hcy034e6.fsf@thalassa.informatimago.com>
··········@gmail.com writes:

> So I come from a {..} background, and I've been reading up details of
> CLOS. (must say it is awesome!).
>
> One thing I could not find: In C++ Class definitions, we have public,
> protected and private members.  Is there anything similar in lisp?  If
> not, is there a workaround, or is it not needed?

At the class level, as indicated by Lars, you have the possibility to
define various reader, writer or full accessors, or to define your own
methods to access directly the slots with slot-value.

You can also use the packages, and not export some accessors if you
consider them private.

If you have some methods of slots that are really very private, you
can always name them with some convention such as using % or $ in
their name.


And finally, if you want more control on the part of the language, you
can define your own metaclass or your own defclass macro to implement
any kind of access control you fancy.

But actually, it's not needed, in general.


-- 
__Pascal Bourguignon__                     http://www.informatimago.com/
The rule for today:
Touch my tail, I shred your hand.
New rule tomorrow.
From: Pascal Bourguignon
Subject: Re: Information hiding in CLOS?
Date: 
Message-ID: <87zmbs1mjy.fsf@thalassa.informatimago.com>
··········@gmail.com writes:

> So I come from a {..} background, and I've been reading up details of
> CLOS. (must say it is awesome!).
>
> One thing I could not find: In C++ Class definitions, we have public,
> protected and private members.  Is there anything similar in lisp?  If
> not, is there a workaround, or is it not needed?

By the way, if you really want to hide a variable, you can do it with
closures (as long as nobody use implementation specific stuff to
access the closure, like system:closure-environment or things like
that..., and as long as nobody breaks in the debugger while in the
closure).



(defun make-protected-data (password)
  (let ((hidden 'data)
        (key     password))
   (lambda (message password &optional value)
      (if (equal password key)
         (ecase message
           ((set) (setf hidden value))
           ((get) hidden))
         (error "Bad password.")))))

(defparameter *p* (make-protected-data "key"))


[684]> (funcall *p* 'get "key")
DATA
[685]> (funcall *p* 'set "key" 42)
42
[686]> (funcall *p* 'get "key")
42
[687]> (funcall *p* 'get "wrong")

*** - Bad password.
The following restarts are available:
ABORT          :R1      ABORT
Break 1 [688]> :bt
<1> #<SYSTEM-FUNCTION EXT:SHOW-STACK> 3
<2> #<COMPILED-FUNCTION SYSTEM::PRINT-BACKTRACE>
<3> #<COMPILED-FUNCTION SYSTEM::DEBUG-BACKTRACE>
<4> #<SYSTEM-FUNCTION SYSTEM::READ-EVAL-PRINT> 2
<5> #<COMPILED-FUNCTION SYSTEM::BREAK-LOOP-2-2>
<6> #<SYSTEM-FUNCTION SYSTEM::SAME-ENV-AS> 2
<7> #<COMPILED-FUNCTION SYSTEM::BREAK-LOOP-2>
<8> #<SYSTEM-FUNCTION SYSTEM::DRIVER>
<9> #<COMPILED-FUNCTION SYSTEM::BREAK-LOOP>
<10> #<SYSTEM-FUNCTION INVOKE-DEBUGGER> 1
<11> #<SYSTEM-FUNCTION ERROR> 1
<12> #<SPECIAL-OPERATOR IF>
EVAL frame for form 
(IF (EQUAL PASSWORD KEY)
 (LET ((#1=#:G8637 MESSAGE))
  (CASE #1# ((SET) (SETQ HIDDEN VALUE)) ((GET) HIDDEN)
   (OTHERWISE
    (SYSTEM::ETYPECASE-FAILED #1#
     (SYSTEM::CASE-ERROR-STRING 'MESSAGE '(SET GET)) '(MEMBER SET GET)))))
 (ERROR "Bad password."))
APPLY frame for call (:LAMBDA 'GET '"wrong")
<13> 
#<FUNCTION :LAMBDA (MESSAGE PASSWORD &OPTIONAL VALUE)
  (IF (EQUAL PASSWORD KEY)
   (ECASE MESSAGE ((SET) (SETF HIDDEN VALUE)) ((GET) HIDDEN))
   (ERROR "Bad password."))> 2
<14> #<SYSTEM-FUNCTION FUNCALL> 3
EVAL frame for form (FUNCALL *P* 'GET "wrong")
Printed 14 frames
Break 1 [688]> :u
<1> #<SPECIAL-OPERATOR IF>
EVAL frame for form 
(IF (EQUAL PASSWORD KEY)
 (LET ((#1=#:G8637 MESSAGE))
  (CASE #1# ((SET) (SETQ HIDDEN VALUE)) ((GET) HIDDEN)
   (OTHERWISE
    (SYSTEM::ETYPECASE-FAILED #1#
     (SYSTEM::CASE-ERROR-STRING 'MESSAGE '(SET GET)) '(MEMBER SET GET)))))
 (ERROR "Bad password."))
Break 1 [688]> key
"key"                ; OOOOPPSS!!
Break 1 [688]> 


Happily, with clisp, compiling provides one more level of protection:


(defun make-protected-data (password)
  (let ((hidden 'data)
        (key     password))
   (compile nil (lambda (message password &optional value)
                  (if (equal password key)
                     (ecase message
                       ((set) (setf hidden value))
                       ((get) hidden))
                     (error "Bad password."))))))

(defparameter *p* (make-protected-data "key"))


[691]> (funcall *p* 'get "wrong")

*** - Bad password.
The following restarts are available:
ABORT          :R1      ABORT
Break 1 [692]> :bt
Printed 0 frames
Break 1 [692]> :u
EVAL frame for form (FUNCALL *P* 'GET "wrong")
Break 1 [692]> key

*** - EVAL: variable KEY has no value
The following restarts are available:
USE-VALUE      :R1      You may input a value to be used instead of KEY.
STORE-VALUE    :R2      You may input a new value for KEY.
ABORT          :R3      ABORT
ABORT          :R4      ABORT
Break 2 [693]> 


But not really :-) :

[697]> (disassemble *p*)

Disassembly of function NIL
(CONST 0) = #(KEY #1="key" HIDDEN DATA #(PASSWORD #1# NIL))
(CONST 1) = 1
(CONST 2) = SET
(CONST 3) = 3
(CONST 4) = GET
(CONST 5) = MESSAGE
(CONST 6) = (SET GET)
(CONST 7) = SYSTEM::CASE-ERROR-STRING
(CONST 8) = (MEMBER SET GET)
(CONST 9) = SYSTEM::ETYPECASE-FAILED
(CONST 10) = "Bad password."
2 required arguments
1 optional argument
No rest parameter
No keyword parameters
32 byte-code instructions:
0     (UNBOUND->NIL 1)
2     (LOAD&PUSH 2)
3     (CONST&PUSH 0)                      ; #(KEY #1="key" HIDDEN DATA ...)
4     (CONST 1)                           ; 1
5     (SVREF)
6     (PUSH)
7     (CALLS2&JMPIF 4 L25)                ; EQUAL
10    (CONST&PUSH 10)                     ; "Bad password."
11    (CALLSR 0 29)                       ; ERROR
14    L14
14    (LOAD&PUSH 1)
15    (CONST&PUSH 0)                      ; #(KEY #1="key" HIDDEN DATA ...)
16    (CONST 3)                           ; 3
17    (SVSET)
18    (SKIP&RET 4)
20    L20
20    (CONST&PUSH 0)                      ; #(KEY #1="key" HIDDEN DATA ...)
21    (CONST 3)                           ; 3
22    (SVREF)
23    (SKIP&RET 4)
25    L25
25    (LOAD&PUSH 3)
26    (JMPIFEQTO 2 L14)                   ; SET
29    (LOAD&PUSH 3)
30    (JMPIFEQTO 4 L20)                   ; GET
33    (LOAD&PUSH 3)
34    (CONST&PUSH 5)                      ; MESSAGE
35    (CONST&PUSH 6)                      ; (SET GET)
36    (CALL2&PUSH 7)                      ; SYSTEM::CASE-ERROR-STRING
38    (CONST&PUSH 8)                      ; (MEMBER SET GET)
39    (CALL 3 9)                          ; SYSTEM::ETYPECASE-FAILED
42    (SKIP&RET 4)
NIL
[698]> 

And so on...  
On a Von Neumann computer, you can always do some peek or poke.

You'd need a real capability based system to really have protected and
private data.  Like: http://www.capros.org/ (ex http://www.eros-os.org/).
Or perhaps in a few months: http://dept-info.labri.fr/~strandh/gracle.ps



-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

ATTENTION: Despite any other listing of product contents found
herein, the consumer is advised that, in actuality, this product
consists of 99.9999999999% empty space.
From: Pascal Costanza
Subject: Re: Information hiding in CLOS?
Date: 
Message-ID: <4ppeffFj5e47U1@individual.net>
··········@gmail.com wrote:
> So I come from a {..} background, and I've been reading up details of
> CLOS. (must say it is awesome!).
> 
> One thing I could not find: In C++ Class definitions, we have public,
> protected and private members.  Is there anything similar in lisp?  If
> not, is there a workaround, or is it not needed?

There is a distinction between information hiding and encapsulation. 
Encapsulation means "you should never ever be able to access that piece 
of information even if you really need it", and information hiding means 
"you shouldn't need to access this bit of information, and it might 
actually change its implementation, but if you know what you're doing, 
here is a back door to get to it."

Common Lisp supports information hiding at the package level: You can 
define packages that export some symbols and keep other symbols to 
themselves. However, the package system is defined in a way that you can 
also access internal symbols if you really need to. The package system 
is a generic mechanism for controlling access to concepts, it is in fact 
just a name space management system. It doesn't matter what you use the 
names / symbols for, be it function names, variable names, class names, 
slot names, etc. pp.

So the neat thing is that CLOS doesn't need to define any access control 
because this is handled orthogonally by the package system. It's also 
good to know that when you add your own (domain-specific) language 
constructs, you don't have to worry too much about these things either 
because you can reuse the package system for your own purposes as well.

There are very few constructs in Common Lisp that support encapsulation. 
In general, the assumption in Common Lisp is that programmers are 
competent enough to judge the trade offs of respecting or breaking 
information hiding.


Pascal

-- 
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/