[]<<


(Tom Sawyer) #1

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


(Jim Freeze) #2

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…
~


(Kent Dahl) #3

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/ ]/~
))_student
/(( _d L b_/ NTNU - graduate engineering - 4. year )
( __õ|õ// ) )Industrial economics and technological management(
_
/ö____/ (_engineering.discipline=Computer::Technology)


(Yohanes Santoso) #4

Tom Sawyer 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.


(Tom Sawyer) #5

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/ ]/~
))_student
/(( _d L b_/ NTNU - graduate engineering - 4. year )
( __õ|õ// ) )Industrial economics and technological management(
_
/ö____/ (_engineering.discipline=Computer::Technology)


(ts) #6

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

   a = Array.new
   a.set(0, 12)
   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

   a = Array.new
   a.[]=(0, 12)
   a.[](0)

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

   a = Array.new
   a[0] = 12
   a[0]

Guy Decoux


(Kent Dahl) #7

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/ ]/~
))_student
/(( _d L b_/ NTNU - graduate engineering - 4. year )
( __õ|õ// ) )Industrial economics and technological management(
_
/ö____/ (_engineering.discipline=Computer::Technology)