Freezing Variable Assignment

Hi,

Is there a feature to freeze variable assignment?

e.g.
a="hello"
a.assignfreeze
a=“goodbye” # ===> generates exception

Or, in a related vein, a type freeze, so that only similar objects can be added.

I realize that constants offer assignment freezing to a certain degree.

The reason I ask, is that I stepped over-top of some framework
variables today, and it was hard to find out what was going on. Where
as ruby protects keywords, it would be nice if frameworks or custom
domain specific languages could do the same.

Thanks,
Nick

"Nicholas Van Weerdenburg" <vanweerd@gmail.com> wrote in message

Is there a feature to freeze variable assignment?

e.g.
a="hello"
a.assignfreeze
a="goodbye" # ===> generates exception

I like this, specially if it also covers instance variables. I think Ruby's
current freeze is a special case, in which all instance variables of the
given object are frozen.

It's in the same vein that I think Observable should target a instance
variable (an attribute, more generally) of an object, rather than an entire
object.

Hi --

Hi,

Is there a feature to freeze variable assignment?

e.g.
a="hello"
a.assignfreeze
a="goodbye" # ===> generates exception

I tend to think this would be a pretty radical reconceptualization of
how variable identifiers relate to objects, rather than just a
feature. At the very least it would probably have to be something
like:

   freeze_identifier(:a)

as opposed to sending a message to the object referenced by 'a'. (In
your example you'd be sending the message 'assignfreeze' to the string
"hello".)

Or, in a related vein, a type freeze, so that only similar objects can be added.

You'd have to define 'type' and/or 'similar object', which isn't
always easy :slight_smile:

David

···

On Mon, 6 Dec 2004, Nicholas Van Weerdenburg wrote:

--
David A. Black
dblack@wobblini.net

Is there a feature to freeze variable assignment?

e.g.
a="hello"
a.assignfreeze
a="goodbye" # ===> generates exception

No, but you might be able to do something with WeakRef (weakref.rb)
or some other proxy class and freezing that.

  class Var
    def initialize(value)
      @value = value
      @const = false
    end

    def const!
      @const = true
    end

    def const?
      @const
    end

    attr_accessor :value
    def value=(value)
      raise "Var #{@value.inspect} is constant." if const?
      @value = value
    end

    def class
      @value.class
    end

    def id
      @value.__id__
    end

    def method_missing(sym, *args)
      @value.send(sym, *args)
    end

    def inspect
      @value.inspect
    end
  end

  a = Var.new("hello")
  a.value = "goodbye"
  a.class
  a.const!
  a.value = "yo!"

Or, in a related vein, a type freeze, so that only similar objects
can be added.

Define "similar objects". What if I want a variable to be only an
IO? Should I restrict it to items which inherit from IO? If so, I
lose the ability to transparently use StringIO or ZLib::GzipWriter
(or GzipReader) objects. This gets to the heart of why static typing
is generally a bad idea -- it makes classes less extensible, and
when you have static typing enforced by the language, there's almost
always ways to escape out of it with no protection from the compiler
involved (e.g., pointers).

I realize that constants offer assignment freezing to a certain
degree.

The reason I ask, is that I stepped over-top of some framework
variables today, and it was hard to find out what was going on.
Where as ruby protects keywords, it would be nice if frameworks or
custom domain specific languages could do the same.

What do you mean, specifically? Did you reopen the classes in the
framework, or something? If the framework put variables in such a
way as to make it easy for you to overwrite something that shouldn't
have been, then I think that it's a bug in the framework, not in
your code. In some ways, Transaction::Simple is a framework, and I
have deliberately made it "hard" to step on Transaction::Simple
variables.

"Nicholas Van Weerdenburg" <vanweerd@gmail.com> wrote in message

Is there a feature to freeze variable assignment?

e.g.
a="hello"
a.assignfreeze
a="goodbye" # ===> generates exception

I like this, specially if it also covers instance variables. I
think Ruby's current freeze is a special case, in which all
instance variables of the given object are frozen.

Um. Not really. Only the direct replacement of those objects is
frozen.

  a = Struct.new("Effable", :a, :b).new
  a.a = "abcdef"
  a.b = %w(a b c d e f)
  a.freeze
  a.a = "ghijkl" # raises error
  a.a.gsub!(/a/, 'z') # no error
  a.b[0] = 'z' # no error

Freeze isn't necessarily recursive.

It's in the same vein that I think Observable should target a
instance variable (an attribute, more generally) of an object,
rather than an entire object.

Why?

-austin

···

On Mon, 6 Dec 2004 11:51:55 +0900, Nicholas Van Weerdenburg <vanweerd@gmail.com> wrote:
On Mon, 6 Dec 2004 12:22:35 +0900, itsme213 <itsme213@hotmail.com> wrote:
--
Austin Ziegler * halostatue@gmail.com
               * Alternate: austin@halostatue.ca

Nicholas Van Weerdenburg wrote:

Is there a feature to freeze variable assignment?

e.g.
a="hello"
a.assignfreeze
a="goodbye" # ===> generates exception

Or, in a related vein, a type freeze, so that only similar objects can be added.

The proposed Syntax confuses the object a variable is referring to with the variable itself. It would likely be assign_freeze :var instead. But I don't think this is general enough to include it directly in Ruby.

Let me suggest an idea for a revised request instead: Request a way of specifying a callback for local variable assignment. trace_var already can be used for doing that with global variables. It would be a generalization to also allow this with local ones. The interface would be this:

trace_var(:string) do |arg|
   raise(TypeError, "Can't assign #{arg.inspect} to variable 'string'," +
         " because it is not a String.") unless arg.is_a?(String)
end

Using that you could implement assign_freeze() by yourself. But please make sure to think about performance issues. It might be a good idea to implement it and benchmark it and send the patch with statistics to ruby-core. Note that slowdown that is only caused by *using* the new feature (e. g. does not make old code slower) is not much of an issue.

Sorry if this sounds like a lot of work, but I think that if you do it like that it would be a very nice feature that would be generally useful to the community without having too much of a downside. It would allow for very powerful meta programming.

Austin Ziegler wrote:
...

It's in the same vein that I think Observable should target a
instance variable (an attribute, more generally) of an object,
rather than an entire object.

Why?

It's useful in GUI code, where each control, field, etc. is wired up to one or more attrs in the window instance, and other related windows can be wired up to those attrs too, to keep their state synchronized. See, for instance, foxtails on raa, which uses FXRuby and observable (also on raa). That observable lib (which should have been called observable-attr) is just what Nicholas described: it makes attrs observable.

···

On Mon, 6 Dec 2004 11:51:55 +0900, Nicholas Van Weerdenburg > <vanweerd@gmail.com> wrote:

"Austin Ziegler" <halostatue@gmail.com> wrote in message

  a = Struct.new("Effable", :a, :b).new
  a.a = "abcdef"
  a.b = %w(a b c d e f)
  a.freeze
  a.a = "ghijkl" # raises error
  a.a.gsub!(/a/, 'z') # no error
  a.b[0] = 'z' # no error

Freeze isn't necessarily recursive.

Correct. All instance variables of the object are frozen, not the objects
they refer to.

One of the nice things about a pure oo language like Ruby or Smalltalk is
the clean model of state as an object graph: a set of objects and their
links via instance variables. The only possible changes of state are (a)
changes to links via deleting links or creating links (b) creation of new
objects. Freezing then is about prohibiting certain link changes.

Nicholas Van Weerdenburg wrote:

> Is there a feature to freeze variable assignment?
>
> e.g.
> a="hello"
> a.assignfreeze
> a="goodbye" # ===> generates exception
>
> Or, in a related vein, a type freeze, so that only similar objects can be added.

The proposed Syntax confuses the object a variable is referring to with
the variable itself. It would likely be assign_freeze :var instead.

Yes. My bad.

But I don't think this is general enough to include it directly in Ruby.

Let me suggest an idea for a revised request instead: Request a way of
specifying a callback for local variable assignment. trace_var already
can be used for doing that with global variables. It would be a
generalization to also allow this with local ones. The interface would
be this:

trace_var(:string) do |arg|
   raise(TypeError, "Can't assign #{arg.inspect} to variable 'string'," +
         " because it is not a String.") unless arg.is_a?(String)
end

Using that you could implement assign_freeze() by yourself. But please
make sure to think about performance issues. It might be a good idea to
implement it and benchmark it and send the patch with statistics to
ruby-core. Note that slowdown that is only caused by *using* the new
feature (e. g. does not make old code slower) is not much of an issue.

Sorry if this sounds like a lot of work, but I think that if you do it
like that it would be a very nice feature that would be generally useful
to the community without having too much of a downside. It would allow
for very powerful meta programming.

Those are good ideas. Maybe over Christmas I can take my ruby up an
notch and start working with c files.

Thanks,
Nick

···

On Wed, 8 Dec 2004 04:57:32 +0900, Florian Gross <flgr@ccan.de> wrote:

>>It's in the same vein that I think Observable should target a
>>instance variable (an attribute, more generally) of an object,
>>rather than an entire object.
>
>
> Why?

It's useful in GUI code, where each control, field, etc. is wired up to
one or more attrs in the window instance, and other related windows can
be wired up to those attrs too, to keep their state synchronized. See,
for instance, foxtails on raa, which uses FXRuby and observable (also on
raa). That observable lib (which should have been called
observable-attr) is just what Nicholas described: it makes attrs

observable.

GUIs are a good example, including wiring up attributes of controls (e.g.
theTemperatureIndicator.color) to attributes of domain objects (e.g.
theFurnace.temperature).

I'd go further and say that almost any place one registers interest in an
object, what is really intended is an interest in some attribute (or,
equivalently, some set of attributes) of that object.

Which is why I think the freeze proposal is a Good Thing.

No, the instance variables are not frozen, the instance 'a' of Effable is.

#a= modifies a, which is disallowed because a is frozen.

You cannot freeze variables, just objects.

a = "foo"
a.freeze
a = "bar"

···

On 06 Dec 2004, at 07:32, itsme213 wrote:

"Austin Ziegler" <halostatue@gmail.com> wrote in message

  a = Struct.new("Effable", :a, :b).new
  a.a = "abcdef"
  a.b = %w(a b c d e f)
  a.freeze
  a.a = "ghijkl" # raises error
  a.a.gsub!(/a/, 'z') # no error
  a.b[0] = 'z' # no error

Freeze isn't necessarily recursive.

Correct. All instance variables of the object are frozen, not the objects
they refer to.

That makes sense, and it seems that the observable-attr is the right
thing for what he needs there.

-austin

···

On Mon, 6 Dec 2004 14:32:12 +0900, Joel VanderWerf <vjoel@path.berkeley.edu> wrote:

Austin Ziegler wrote:
> On Mon, 6 Dec 2004 11:51:55 +0900, Nicholas Van Weerdenburg > > <vanweerd@gmail.com> wrote:
>>It's in the same vein that I think Observable should target a
>>instance variable (an attribute, more generally) of an object,
>>rather than an entire object.
> Why?
It's useful in GUI code, where each control, field, etc. is wired up to
one or more attrs in the window instance, and other related windows can
be wired up to those attrs too, to keep their state synchronized. See,
for instance, foxtails on raa, which uses FXRuby and observable (also on
raa). That observable lib (which should have been called
observable-attr) is just what Nicholas described: it makes attrs observable.

--
Austin Ziegler * halostatue@gmail.com
               * Alternate: austin@halostatue.ca

I respecfully but heartily disagree. Ruby freezes objects by freezing their
instance variables. The latter is the fundamental operation.

a = "foo" # makes the variable a refer to the object "foo"

a.freeze # makes the instance variables of the object "foo" frozen.

Try this:

@a = "foo"
self.freeze
@a = "bar"

Ruby just happens to treat local variables differently. There is no
fundamental reason to do so.

···

"Eric Hodel" <drbrain@segment7.net> wrote

> Correct. All instance variables of the object are frozen, not the
> objects
> they refer to.

No, the instance variables are not frozen, the instance 'a' of Effable
is.

#a= modifies a, which is disallowed because a is frozen.

You cannot freeze variables, just objects.

a = "foo"
a.freeze
a = "bar"

But 'a' is not an instance of anything. 'a' is a _variable_, it _refers_ to
an instance of Effable. The variable is not the object.

···

"Eric Hodel" <drbrain@segment7.net> wrote

> "Austin Ziegler" <halostatue@gmail.com> wrote in message
>> a = Struct.new("Effable", :a, :b).new
>> a.a = "abcdef"
>> a.b = %w(a b c d e f)
>> a.freeze
No, the instance variables are not frozen, the instance 'a' of Effable
is.

Hi --

> > Correct. All instance variables of the object are frozen, not the
> > objects
> > they refer to.
>
> No, the instance variables are not frozen, the instance 'a' of Effable
> is.
>
> #a= modifies a, which is disallowed because a is frozen.
>
> You cannot freeze variables, just objects.
>
> a = "foo"
> a.freeze
> a = "bar"

I respecfully but heartily disagree. Ruby freezes objects by freezing their
instance variables. The latter is the fundamental operation.

Ruby freezes objects by... freezing objects. It doesn't freeze
variables. Here are some examples, where instance variables are
nowhere to be found but freezing still happens:

  irb(main):001:0> a =
  =>
  irb(main):002:0> a.freeze
  =>
  irb(main):003:0> a[1] = 1
  TypeError: can't modify frozen array
  irb(main):004:0> s = "abc"
  => "abc"
  irb(main):005:0> s.freeze
  => "abc"
  irb(main):006:0> s << "def"
  TypeError: can't modify frozen string

a = "foo" # makes the variable a refer to the object "foo"

a.freeze # makes the instance variables of the object "foo" frozen.

Not exactly; it freezes the object to which 'a' is a reference. One
of the consequences of that (though not the only consequence) is that
a's instance variables bindings are frozen. That's why you can't do
this:

Try this:

@a = "foo"
self.freeze
@a = "bar"

i.e., because you're trying to change what object @a refers to, which
changes the state of 'self' (part of self's state being the bindings
of its instance variables). However, notice that you *can* do this:

  irb(main):001:0> @a =
  =>
  irb(main):002:0> self.freeze
  => #<Object:0xbf50da10 @a=>
  irb(main):003:0> @a << 1
  => [1]

because the object to which @a refers is not frozen.

Ruby just happens to treat local variables differently. There is no
fundamental reason to do so.

Actually there is: instance variables belong to an object and maintain
its state, while local variables do not. So "freezing local
variables" (i.e., making some local variable identifiers un-reusable)
really should be discussed completely separately from Kernel#freeze,
perhaps given a different name even for purposes of discussion, since
it's a basically different concept.

David

···

On Tue, 7 Dec 2004, itsme213 wrote:

"Eric Hodel" <drbrain@segment7.net> wrote

--
David A. Black
dblack@wobblini.net

Correct. All instance variables of the object are frozen, not
the objects they refer to.

No, the instance variables are not frozen, the instance 'a' of
Effable is.

#a= modifies a, which is disallowed because a is frozen.

You cannot freeze variables, just objects.

a = "foo"
a.freeze
a = "bar"

I respecfully but heartily disagree. Ruby freezes objects by
freezing their instance variables. The latter is the fundamental
operation.

You may disagree, but you'd be incorrect.

irb(main):008:0> class << a
irb(main):009:1> def matz
irb(main):010:2> "matz"
irb(main):011:2> end
irb(main):012:1> end
TypeError: can't modify frozen object
        from (irb):9

(Assuming the same Effable that has been discussed to this point.)

The object referred to by 'a' is frozen, not the instance variables
of said object.

The fundamental operation is *freezing the object*.

@a = "foo"
self.freeze
@a = "bar"

Ruby just happens to treat local variables differently. There is
no fundamental reason to do so.

Sure there is, and it's precisely because variables are simply
labels, but instance variables are part of the state of the object.

-austin

···

On Tue, 7 Dec 2004 07:12:37 +0900, itsme213 <itsme213@hotmail.com> wrote:

"Eric Hodel" <drbrain@segment7.net> wrote

--
Austin Ziegler * halostatue@gmail.com
               * Alternate: austin@halostatue.ca

You cannot freeze variables, just objects.

a = "foo"
a.freeze
a = "bar"

I respecfully but heartily disagree. Ruby freezes objects by freezing their
instance variables. The latter is the fundamental operation.

variable.c disagrees with you:

VALUE
rb_ivar_set(obj, id, val)
     VALUE obj;
     ID id;
     VALUE val;
{
     if (!OBJ_TAINTED(obj) && rb_safe_level() >= 4)
     rb_raise(rb_eSecurityError, "Insecure: can't modify instance variable");
     if (OBJ_FROZEN(obj)) rb_error_frozen("object");

a = "foo" # makes the variable a refer to the object "foo"

a.freeze # makes the instance variables of the object "foo" frozen.

Try this:

@a = "foo"
self.freeze
@a = "bar"

No, self is frozen, not @a. Your example is no different than the one using Effable.

@a = "foo" # self.instance_variable_set "@a", "foo"
self.freeze
@b = "bar" # self.instance_variable_set "@b", "bar", raises because self is frozen.

···

On 06 Dec 2004, at 14:12, itsme213 wrote:

You guys are talking past each other ...

Itsme213: "Ruby freezes objects by freezing their instance variables"

Weirich Translation: When a Ruby object is frozen, the binding of its
instances variable names to values are made unchangeable (i.e. frozen).

Eric points to variable.c, which implements the policy annunciated by itsme.
And David correctly points out that the simple view of only looking at the
instance variables does not paint the whole picture (e.g. freezing arrays).

But as far as classes implemented in Ruby (as opposed to classes implemented
in C), the viewpoint is pretty right on.

···

On 06 Dec 2004, at 14:12, itsme213 wrote:
> I respecfully but heartily disagree. Ruby freezes objects by freezing
> their instance variables. The latter is the fundamental operation.

On Monday 06 December 2004 07:47 pm, Eric Hodel wrote:

variable.c disagrees with you:

--
-- Jim Weirich jim@weirichhouse.org http://onestepback.org
-----------------------------------------------------------------
"Beware of bugs in the above code; I have only proved it correct,
not tried it." -- Donald Knuth (in a memo to Peter van Emde Boas)

Hi --

> > I respecfully but heartily disagree. Ruby freezes objects by freezing
> > their instance variables. The latter is the fundamental operation.

> variable.c disagrees with you:

You guys are talking past each other ...

Itsme213: "Ruby freezes objects by freezing their instance variables"

Weirich Translation: When a Ruby object is frozen, the binding of its
instances variable names to values are made unchangeable (i.e. frozen).

David's previous statement on this: "One of the consequences of
[calling a.freeze] (though not the only consequence) is that a's
instance variables bindings are frozen." :slight_smile:

Eric points to variable.c, which implements the policy annunciated by itsme.
And David correctly points out that the simple view of only looking at the
instance variables does not paint the whole picture (e.g. freezing arrays).

But as far as classes implemented in Ruby (as opposed to classes implemented
in C), the viewpoint is pretty right on.

But that, in turn, is because Kernel#freeze freezes an object's state,
and state for non-builtins is generally (always?) a matter of instance
variables. That's why I disagree with itsme213 that instance
variables are a good model for how to treat local variables. (Not
that that matters, really; if it's a good idea to be able to freeze
local variable bindings [I'm not convinced it is], it doesn't have to
depend on analogy with Kernel#freeze.)

One could perhaps reason that a Binding "has" local variables, in
somewhat the same way that an object "has" instance variables, and
that there should be some way to freeze those bindings too (i.e., the
bindings in a Binding). The cases are not exactly parallel, though,
since a Binding can also have instance variables.... But it might be
an alternative way to approach it.

David

···

On Tue, 7 Dec 2004, Jim Weirich wrote:

> On 06 Dec 2004, at 14:12, itsme213 wrote:
On Monday 06 December 2004 07:47 pm, Eric Hodel wrote:

--
David A. Black
dblack@wobblini.net

"Jim Weirich" <jim@weirichhouse.org> wrote in message

You guys are talking past each other ...

I think so.

Eric points to variable.c, which implements the policy annunciated by

itsme.

And David correctly points out that the simple view of only looking at the
instance variables does not paint the whole picture (e.g. freezing

arrays).

I was talking about the pure object model part of Ruby. In such a pure
object model, a[1] and a[2] are instance variables (some Smalltalk
descriptions calls these 'indexed instance variables' as opposed to 'named
instance variables'). How something is optimized in C is a different matter.

In any case, consider my postings as a point of view; it is a clean and
potentially useful one. The part about local variables was not central to
me; I was just looking for a bit more uniformity in treatment of all
variable bindings, even temporary (local) ones. It makes things like dynamic
binding more uniform to implement.

my 3 c

"David A. Black" <dblack@wobblini.net> wrote in message

That's why I disagree with itsme213 that instance
variables are a good model for how to treat local variables.

My main interest in freezing is around instance variables. As I said ...
  > I like this, specially if it also covers instance variables.

Cheers