# \[\]\<\<

**URL:** <https://rubytalk.org/t/topic/395>\
**Category:** ruby-talk\
**Created:** [26 June 2002 11:13 UTC](https://rubytalk.org/t/topic/395 "2002-06-26T11:13:05Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![Tom\_Sawyer](https://avatars.discourse-cdn.com/v4/letter/t/a587f6/32.png) [@Tom\_Sawyer](https://rubytalk.org/u/Tom_Sawyer)\
**Post date:** [26 June 2002 11:13 UTC](https://rubytalk.org/t/topic/395/1 "2002-06-26T11:13:05Z")

</div>

here’s something i thought was interesting:  
(note that the comments are intended)

class Tt

#attr\_accessor :h # no access to h!

def initialize  
@h = {}  
@h[‘test’] = '1…'  
end

def # only want to allow reading  
@h[xp]  
end

#def []=(xp, ap) # no assignment allowed!

# @h[xp] = [ap]

#end

end

tt = Tt.new  
tt[‘test’] \<\< ‘…2’ # how come you can do me?  
tt[‘test’] = ‘1…2’ # if you can’t do me!

it seems to me that the main point of accessors and the []= method is to  
control accessiblity to a class’ instance variables. right? but as this  
example shows, there are ways in which “back-doors” exist.

shouldn’t there be better control of such thing? i came across this when  
i was actually trying to create a []\<\< method, and realized that i have  
never seen such a beast before! shouldn’t such a thing exist?

~transami

---

<div class="post-metadata">

**Author:** ![Jim\_Freeze2](https://avatars.discourse-cdn.com/v4/letter/j/e480ec/32.png) [@Jim\_Freeze2](https://rubytalk.org/u/Jim_Freeze2)\
**Post date:** [26 June 2002 11:17 UTC](https://rubytalk.org/t/topic/395/2 "2002-06-26T11:17:29Z")

</div>

> here’s something i thought was interesting:  
> (note that the comments are intended)
> 
> class Tt
> 
> #attr\_accessor :h # no access to h!
> 
> def initialize  
> @h = {}  
> @h[‘test’] = ‘1…’  
> end
> 
> def # only want to allow reading  
> @h[xp]  
> end
> 
> #def =(xp, ap) # no assignment allowed!
> 
> # @h[xp] = [ap]
> 
> #end
> 
> end
> 
> tt = Tt.new  
> tt[‘test’] \<\< ‘…2’ # how come you can do me?  
> tt[‘test’] = ‘1…2’ # if you can’t do me!

Well, you are returning a pointer to the object.  
How about

def   
@h[xp].dup  
# or, just to be sure  
#Marshal.load(Marshal.dump(@h[xp]))  
end

Jim

> **···**
>
> On Wed, Jun 26, 2002 at 08:13:05PM +0900, Tom Sawyer wrote:
> 
> > it seems to me that the main point of accessors and the = method is to  
> > control accessiblity to a class’ instance variables. right? but as this  
> > example shows, there are ways in which “back-doors” exist.
> > 
> > shouldn’t there be better control of such thing? i came across this when  
> > i was actually trying to create a \<\< method, and realized that i have  
> > never seen such a beast before! shouldn’t such a thing exist?
> > 
> > ~transami
> 
> –  
> Jim Freeze  
> If only I had something clever to say for my comment…  
> ~

---

<div class="post-metadata">

**Author:** ![Kent\_Dahl2](https://avatars.discourse-cdn.com/v4/letter/k/a87d85/32.png) [@Kent\_Dahl2](https://rubytalk.org/u/Kent_Dahl2)\
**Post date:** [26 June 2002 12:13 UTC](https://rubytalk.org/t/topic/395/3 "2002-06-26T12:13:58Z")

</div>

Tom Sawyer wrote:

> tt[‘test’] \<\< ‘…2’ # how come you can do me?

Keep in mind that the first part just returns an object. Once you have a  
reference to an object, you can call any method you like on it, that  
isn’t private or protected. This includes \<\<.

> it seems to me that the main point of accessors and the = method is to  
> control accessiblity to a class’ instance variables. right? but as this  
> example shows, there are ways in which “back-doors” exist.

If you let your objects out to the surrounding world unprotected (or  
unfrozen) these things will happen. Take a look at the dup suggestion  
from Jim, or consider freezing your objects before returning them. (This  
will mean that you have to create a new object if you want to change it  
on the inside, and deny any access on the outside. Sounds like a matter  
of preference and optimization consideration.)

Or you could be nasty and do like this before you return:  
$ irb  
irb(main):001:0\> a = [1,2,3]  
[1, 2, 3]  
irb(main):002:0\> class \<\<a; private :\<\<; end  
Array  
irb(main):003:0\> a \<\< 5  
NameError: private method `\<\<’ called for [1, 2, 3]:Array  
from (irb):3

> shouldn’t there be better control of such thing? i came across this when  
> i was actually trying to create a \<\< method, and realized that i have  
> never seen such a beast before! shouldn’t such a thing exist?

No. You would then need (insert any mutator method of the returned  
object here) methods, which would drive you bonkers in the end, since  
you can’t possibly be sure about them all. Freezing or dup-ing sounds  
alot more sensible.

> **···**
>
> –  
> ([Kent Dahl]/)\_ _~_**[[http://www.stud.ntnu.no/~kentda/](http://www.stud.ntnu.no/~kentda/) ]**_/~  
> ))\_student_/(( \__d L b_\_/ NTNU - graduate engineering - 4. year )  
> ( \__\_õ|õ_/**/ ) _)Industrial economics and technological management(  
> \__**/_ö_\_\_\_\_/ (\_**_engineering.discipline_=_Computer::Technology_**)

---

<div class="post-metadata">

**Author:** ![Yohanes\_Santoso](https://avatars.discourse-cdn.com/v4/letter/y/3e96dc/32.png) [@Yohanes\_Santoso](https://rubytalk.org/u/Yohanes_Santoso)\
**Post date:** [27 June 2002 09:57 UTC](https://rubytalk.org/t/topic/395/4 "2002-06-27T09:57:23Z")

</div>

Tom Sawyer [transami@transami.net](mailto:transami@transami.net) writes:

> class Tt  
> #attr\_accessor :h # no access to h!  
> def initialize  
> @h = {}  
> @h[‘test’] = ‘1…’  
> end  
> def # only want to allow reading  
> @h[xp]  
> end  
> #def =(xp, ap) # no assignment allowed!
> 
> # @h[xp] = [ap]
> 
> #end  
> end  
> tt = Tt.new

> tt[‘test’] \<\< ‘…2’ # how come you can do me?  
> Of course it is valid, I would be surprised if it is not valid. In the  
> code above for class Tt, you didn’t define any = method. This means,  
> objects of class Tt does not respond to =. But tt[‘test’] is not of  
> type Tt. It is of type String. So of course tt[‘test’]\<\<‘…2’ is a  
> valid statement.

> tt[‘test’] = ‘1…2’ # if you can’t do me!  
> Since Tt does not respond to =, then this one is correctly invalid.

To make objects contained within @h readonly, you don’t make the  
container (Tt or @h) readonly. Instead you have to write a wrapper for  
the objects within @h that limits access, or write an object that only  
allows changes from the direct owner of the container (which is Tt).

Or if you want the easy way, use the dup/freeze as Kent suggested.

def   
@h[xp].dup.freeze  
end

> prob w/ freezing is as you said, it freezes up the instance on the  
> inside too.

Which is why you dup it first.

> prob w/ dup is the loss of connection to the orig --later changes from  
> within the class won’t be reflected and changes on the outside will  
> seemingly work enven though there is no actual reflection within.

If you want to maintain the connection, then you got to write a  
wrapper for the objects within @h.

So,

> it seems to me that the main point of accessors and the = method is to  
> control accessiblity to a class’ instance variables. right?

right

> but as this example shows, there are ways in which “back-doors”  
> exist.

there is no backdoor. by using the accessor and the = method, you  
are controlling the class’ instance variable. but once you give the  
object that the instance variable refers to, all is game.

Perhaps, what you want is deep-freeze (similar to deep-copy). Then the  
solution is similar to the deep-copy problem, you got to traverse each  
object you control and wrap/freeze them.

YS.

---

<div class="post-metadata">

**Author:** ![Tom\_Sawyer](https://avatars.discourse-cdn.com/v4/letter/t/a587f6/32.png) [@Tom\_Sawyer](https://rubytalk.org/u/Tom_Sawyer)\
**Post date:** [27 June 2002 07:55 UTC](https://rubytalk.org/t/topic/395/5 "2002-06-27T07:55:32Z")

</div>

> No. You would then need (insert any mutator method of the returned  
> object here) methods, which would drive you bonkers in the end, since  
> you can’t possibly be sure about them all…

then i have to ask: why do we have =? taking a consistant analogous  
approach would suggest that all we should have is an = method, but we  
don’t have that either! this is interesting as it means = is purely a  
statement and exists outside the scope of object-orientation. having  
heard so often that Ruby is 100% oo, my first guess would have been that  
the Object class would have some sort = method. but perhaps that’s an  
impossibility?

so anyhow, having heard a few solutions, i guess all i’m really getting  
at is that i was very suprised that the “shortcut” module method  
attr\_reader dosen’t already do something like that. for when i say  
attr\_reader :myvar, that’s exactly what i mean, R…E…A…D…E…R… know  
what i’m saying?

prob w/ freezing is as you said, it freezes up the instance on the  
inside too.

prob w/ dup is the loss of connection to the orig --later changes from  
within the class won’t be reflected and changes on the outside will  
seemingly work enven though there is no actual reflection within.

you approach with an object class works best but again you have to  
identify each and ever possible write method --no better off then \<\<.

a real solution calls for a means to truly designate an object  
read-only. does such a means exist? if not, would it be worth enhancing  
ruby to support it?

~transami

> **···**
>
> On Wed, 2002-06-26 at 06:13, Kent Dahl wrote:
> 
> > Tom Sawyer wrote:
> > 
> > > tt[‘test’] \<\< ‘…2’ # how come you can do me?
> > 
> > Keep in mind that the first part just returns an object. Once you have a  
> > reference to an object, you can call any method you like on it, that  
> > isn’t private or protected. This includes \<\<.
> > 
> > > it seems to me that the main point of accessors and the = method is to  
> > > control accessiblity to a class’ instance variables. right? but as this  
> > > example shows, there are ways in which “back-doors” exist.
> > 
> > If you let your objects out to the surrounding world unprotected (or  
> > unfrozen) these things will happen. Take a look at the dup suggestion  
> > from Jim, or consider freezing your objects before returning them. (This  
> > will mean that you have to create a new object if you want to change it  
> > on the inside, and deny any access on the outside. Sounds like a matter  
> > of preference and optimization consideration.)
> > 
> > Or you could be nasty and do like this before you return:  
> > $ irb  
> > irb(main):001:0\> a = [1,2,3]  
> > [1, 2, 3]  
> > irb(main):002:0\> class \<\<a; private :\<\<; end  
> > Array  
> > irb(main):003:0\> a \<\< 5  
> > NameError: private method `\<\<’ called for [1, 2, 3]:Array  
> > from (irb):3
> > 
> > > shouldn’t there be better control of such thing? i came across this when  
> > > i was actually trying to create a \<\< method, and realized that i have  
> > > never seen such a beast before! shouldn’t such a thing exist?
> > 
> > No. You would then need (insert any mutator method of the returned  
> > object here) methods, which would drive you bonkers in the end, since  
> > you can’t possibly be sure about them all. Freezing or dup-ing sounds  
> > alot more sensible.
> > 
> > –  
> > ([Kent Dahl]/)\_ _~_**[[http://www.stud.ntnu.no/~kentda/](http://www.stud.ntnu.no/~kentda/) ]**_/~  
> > ))\_student_/(( \__d L b_\_/ NTNU - graduate engineering - 4. year )  
> > ( \__\_õ|õ_/**/ ) _)Industrial economics and technological management(  
> > \__**/_ö_\_\_\_\_/ (\_**_engineering.discipline_=_Computer::Technology_**)

---

<div class="post-metadata">

**Author:** ![ts1](https://avatars.discourse-cdn.com/v4/letter/t/a8b319/32.png) [@ts1](https://rubytalk.org/u/ts1)\
**Post date:** [27 June 2002 11:21 UTC](https://rubytalk.org/t/topic/395/6 "2002-06-27T11:21:41Z")

</div>

> then i have to ask: why do we have =?

Some objects are special because they act \*like\* a container where you can  
store other objects.

For these strange objects (like Array) you can define a method to store an  
object ("set") and a method to retrieve it ("get"), then you can work with  
these objects

&nbsp;&nbsp;&nbsp;a = Array.new  
&nbsp;&nbsp;&nbsp;a.set(0, 12)  
&nbsp;&nbsp;&nbsp;a.get(0)

Now that you have these 2 methods you can try to find nicer name like,  
for example, "" for "get", and "=" for "set" this give

&nbsp;&nbsp;&nbsp;a = Array.new  
&nbsp;&nbsp;&nbsp;a.=(0, 12)  
&nbsp;&nbsp;&nbsp;a.(0)

and to make it more nicer, you just make this equivalent to

&nbsp;&nbsp;&nbsp;a = Array.new  
&nbsp;&nbsp;&nbsp;a[0] = 12  
&nbsp;&nbsp;&nbsp;a[0]

Guy Decoux

---

<div class="post-metadata">

**Author:** ![Kent\_Dahl2](https://avatars.discourse-cdn.com/v4/letter/k/a87d85/32.png) [@Kent\_Dahl2](https://rubytalk.org/u/Kent_Dahl2)\
**Post date:** [27 June 2002 19:26 UTC](https://rubytalk.org/t/topic/395/7 "2002-06-27T19:26:06Z")

</div>

Tom Sawyer wrote:

> then i have to ask: why do we have =? taking a consistant analogous  
> approach would suggest that all we should have is an = method, but we  
> don’t have that either! this is interesting as it means = is purely a  
> statement and exists outside the scope of object-orientation. having  
> heard so often that Ruby is 100% oo, my first guess would have been that  
> the Object class would have some sort = method. but perhaps that’s an  
> impossibility?

This is because variables aren’t objects and thus a plain = is a  
statement.

The reason we have = is because of the same reason we have  
obj.attribute=(val) as an attribute writer to mirror the obj.attribute()  
attribute reader. Similar level of indirection (esp. when you start  
looking at Hash)

> so anyhow, having heard a few solutions, i guess all i’m really getting  
> at is that i was very suprised that the “shortcut” module method  
> attr\_reader dosen’t already do something like that. for when i say  
> attr\_reader :myvar, that’s exactly what i mean, R…E…A…D…E…R… know  
> what i’m saying?

Yeah, but then again, this wouldn’t be extremely useful. For numbers, it  
already works like you intend, since they are immutable.

> prob w/ freezing is as you said, it freezes up the instance on the  
> inside too.

That should be no problem, since on the inside, you can assign to the  
variable, no? It makes changes possibly more slow and memory consuming  
(need to make new instance with changes, cant use chomp! etc), but  
shouldn’t really be much.

> prob w/ dup is the loss of connection to the orig --later changes from  
> within the class won’t be reflected and changes on the outside will  
> seemingly work enven though there is no actual reflection within.

Hmm. The first would be a problem with freeze too, even if you do  
reassignment internally.

> you approach with an object class works best but again you have to  
> identify each and ever possible write method --no better off then \<\<.

Sounds like you want an intelligent proxy. Return a delegator that stops  
all access that might change it.

A naive approach: Use method\_missing in a wrapper object, and filter on  
= and ! signs in the methods called.

Another naive approach: use a wrapper object that both holds reference  
to the internal, dynamic data, and a frozen, dup’ed version that you  
forward to when used. If you want it to be synced to the internal,  
dynamic data all the time, I guess you’d have to dup and freeze it every  
time it is accessed thru the wrapper.

But I must say, it sounds like there is a Observer pattern trying to get  
out from inside the problems you present.

> **···**
>
> –  
> ([Kent Dahl]/)\_ _~_**[[http://www.stud.ntnu.no/~kentda/](http://www.stud.ntnu.no/~kentda/) ]**_/~  
> ))\_student_/(( \__d L b_\_/ NTNU - graduate engineering - 4. year )  
> ( \__\_õ|õ_/**/ ) _)Industrial economics and technological management(  
> \__**/_ö_\_\_\_\_/ (\_**_engineering.discipline_=_Computer::Technology_**)
