On Wed, 8 Dec 2004 01:06:53 +0900, Austin Ziegler <halostatue / gmail.com> wrote:
> 
> > I think immutability is usually a good thing from a design point
> > of view, so I can see a general need to have more freezing
> > options.
> 
> Why? Convince me, because as someone who has done my share of design
> work, I don't see immmutability as a positive on most things. On
> primary key references, yes (e.g., a customer's identification
> number should never change or be changed in any way), immutability
> is good. But as a general rule? Immutability isn't necessarily what
> you want.
> 
> (As a point of note, in my Ruby, I don't think that I've *ever* used
> #freeze. In the billing design work that I did, none of it depended
> upon immutability of the objects -- the only immutable things in my
> code and design were *constants*.)
> 
> The "need" for immutability is very, erm, mutable and can be worked
> around with various design decisions.
> 

I agree- good interface design effectively creates immutable classes
if desired. But that can be an issue because the object may only
desire immutability later in its lifecycle, or too certain clients.

Also, good design is an ideal case. In a large project, things can
become ugly and to be able to enforce a high-degree of control via
immutable objects can be beneficial, IMHO. Good design requires good
contracts to enforce it.

There are three things that make up an immutable object so far in our
discussion:
1. frozen objects
        -this could also be done with good interface design for
certain cases. other alternatives to freeze would be useful to know-
wrap/delegate, undeffing, etc.
2. frozen attributes (assignmentFreeze)
       -mostly appplies to inheritence, so private instance variables
would help here.
3. frozen value objects (referred to objects of a certain type)
       -not really discussed yet, but seems important as without it,
freeze is only slushy.

And of course the question, why do you need it.

It's hard to describe the benefits of immutables in a short example,
but I think it's similar in nature to global variables- with 
mutables, edits can be done in many places, leading to bugs as well as
maintenance issues.

That's why I mentioned singletons. They often raise the  desire for
immutables since they facilitate uncontrolled access and program flow-
if you can't get to something, you can't change it. With singletons,
there is often too much coupling, which makes the issue of mutability
more pronounced. It's too easy for isolated pieces of code to break
with appropriate responsibility and assume too much knowledge of the
domain object.

For instance, in an asset manager:
asset=AssetManager.getAsset("id=1")
asset.value="10"
Being embedded in gui code.

With the singleton, this kind of code can be sprinkled throughout the
code. Bad design, sure. But this is often an issue where there is
gui-app logic bindings in different dialogs, tables, etc.

Of course, a better domain class can help.

class Asset
  def initialize name, location,  value
    @name=name
    @location=location
    @value=value
  end
  attr_reader :name, :location, :value
end

This is essentially what I do with Java- creating interfaces that
present an immutable interface to the domain object. But that doesn't
work with duck typing. So in Ruby, it needs to be part of the class
interface, and the problem with that is that I may need to have more
write access to an object early in it's lifecycle. I suppose wrappers
or delegates are an option here, and that's not too hard to implement
in a nice way given Ruby's nature. Or even undefing methods. Any
suggestions welcome.

As a side note though, even with freezing,  the above business object
is not immutable...
a=Asset.new "laptop", "home", 500
a.location << " is where the heart is"   # regardless of a being frozen or not.

If an object should protect it's invariant nature, then it would be
nice for ruby to provide some capability for immutable value objects-
maybe something like:

value_reader :name, :location, :value    # returns defensive copies

Some on the concept of value objects vs. reference objects:
http://c2.com/cgi/wiki?ValueObject
http://c2.com/cgi/wiki?ValueObjectsShouldBeImmutable

> 
> You assert the value of immutability, but you haven't actually
> demonstrated the value. I'm really not trying to be difficult here,
> but what sort of immutability to you mean in Java -- and why do you
> then need it in Ruby? I find that most of the time when people say
> that they want particular features in Ruby they do so because
> they're not *thinking* in Ruby, but in other languages that they
> have to deal with on a daily basis to pay the bills. I know I do it,
> from time to time, with C++ now that I'm making my money from that.
> 

I'm still learning to "think" in Ruby for sure, but the essence of the
need comes from non-trivial systems without a perfect design.

Removing singletons and making certain domain objects immutable is
somewhat like putting aspects of the "Law of Demeter" into place.
http://c2.com/cgi/wiki?LawOfDemeter

Another way of thinking about is in terms of object lifecycle- if it
becomes immutable at some point, there is less to worry about.  In a
large system, I would freeze and object just prior to releasing into
the "wild"- e.g. outside the boundaries of my subsystem.

> > And is there anyway to may attributes private so that subclasses
> > can't see them? IIRC, this is possible feature of Rite. This would
> > remove some of the need, since immutability is enforced by
> > invisibility.
> 
> Um. What do you mean by "attributes"? If you mean that which is
> generated by attr_accessor, then:
> 
>    class A
>      attr_accessor :foo
>      private :foo, :foo=
>    end                     # => A
>    class B < A; end        # => nil
>    B.new.foo = :bar
>       # => NoMethodError: private method `foo=' called for
>       # #<B:0x2b52c38>
>

I meant the case where the instance variable @foo is accessed in side
the class B.
 
> If you mean instance variables, no, and that wouldn't work anyway --
> it would simply be a source of errors.
> 
>    class A
>       attr_accessor  :foo
>       private_var    :@foo
>    end
> 

private_var is a concept?

>
>    class B < A
>       def foo=(x)
>          @foo = x
>       end
>    end
> 
> Logically, this shouldn't cause a problem, because even if @foo in A
> is private, then B should still be able to have it's own @foo.
> 
> Maybe there's a way around this -- and this could be potentially
> very useful -- with something like:
> 
>    module A
>       def foo=(x)
>          @<foo> = x
>       end
>    end
> 
> If Ruby mangles @<foo> to be something like @__A_foo__, then you can
> refer to @<foo> safely in a module without worrying about bumping
> into instance variables belonging to the class.
> 
> > BTW- Is freezing bindings simply a delayed constant? Or is there
> > more to it?
> 
> I don't know what itsme really wants, because I think that the idea
> of making variable bindings -- instance or otherwise -- frozen is a
> bad idea. Freezing an object freezes that object's state -- which
> includes assignment to instance variables, certainly, but also
> inclues adding singleton methods, modifying the singleton object,
> extending the object, etc.
> 

I think private variables would alleviate much of the need. That would
allow a better contract for inheritence,  and that was largely the
interest in my first post.

Other issues requiring freezing probably point to an interface that
exposes too many internals - e.g. use of attr_accessor or a class that
has too much responsibility or is simply too big. There is still the
issue of value objects. Any thoughts on that?

http://c2.com/cgi/wiki?ValueObject
http://c2.com/cgi/wiki?ValueObjectsShouldBeImmutable

> > Also, is there anything such as a deep freeze of an object graph?
> > I'm guessing not, since that would something hard to generalize
> > easily (I think of all the Java serializatoin performance problems
> > where it turned out that the data was pulling the application
> > code along with it).
> 
> No, there isn't such built into Ruby. You could write one, but I
> don't think it's a good idea.
>

Agreed- unless needed for a particular case.
 
Regards,
Nick