Feature #2509

Recursive freezing?

Added by Marc-Andre Lafortune over 4 years ago. Updated 8 months ago.

[ruby-core:27256]
Status:Rejected
Priority:Normal
Assignee:Yukihiro Matsumoto
Category:core
Target version:next minor

Description

=begin
I like freezing my constants, config files I read, etc... I believe it is the typical use case for #freeze.

In all theses cases, what I really want to do is freeze everything. There is often no easy way to do this (e.g. for config files), or else one must explicitly call freeze a bunch of times, like:

DEFAULTSENDFILE_OPTIONS = {
:type => 'application/octet-stream'.freeze,
:disposition => 'attachment'.freeze,
}.freeze

It would be very nice if there was an easy way to freeze recursively arrays, hashes, etc...

A solution would be for #freeze to accept a level argument (similar to flatten, but the default being 1), or alternatively a boolean one (recursive = false).

Should I write a patch for this feature request?

Thanks,

Marc-André
=end

deep_freeze.pdf (90.8 KB) Marc-Andre Lafortune, 08/31/2013 08:59 AM

History

#1 Updated by Shyouhei Urabe over 4 years ago

=begin

It would be very nice if there was an easy way to freeze recursively arrays, hashes, etc...

The difficult part is those "etc". Imagine how can you freeze a lambda and its "everything"?

irb(main):001:0> a = 0
=> 0
irb(main):002:0> p = lambda { p a }.freeze
=> #Proc:0x00007fc681b2e310@(irb):2
irb(main):003:0> p.frozen?
=> true
irb(main):004:0> a = 1
=> 1
irb(main):005:0> p.call
1
=> nil
irb(main):006:0>

=end

#2 Updated by Rick DeNatale over 4 years ago

=begin
On Sun, Dec 20, 2009 at 11:02 PM, Shyouhei Urabe redmine@ruby-lang.org wrote:

Issue #2509 has been updated by Shyouhei Urabe.

It would be very nice if there was an easy way to freeze recursively arrays, hashes, etc...

The difficult part is those "etc".  Imagine how can you freeze a lambda and its "everything"?

 irb(main):001:0> a = 0
 => 0
 irb(main):002:0> p = lambda { p a }.freeze
 => #Proc:0x00007fc681b2e310@(irb):2
 irb(main):003:0> p.frozen?
 => true
 irb(main):004:0> a = 1
 => 1
 irb(main):005:0> p.call
 1
 => nil
 irb(main):006:0>

The other danger to consider is where does 'everything' end. This
made me think of Ice Nine from Kurt Vonnegut's Cat's Cradle

http://en.wikipedia.org/wiki/Ice-nine

--
Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Twitter: http://twitter.com/RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale

=end

#3 Updated by Marc-Andre Lafortune over 4 years ago

=begin
I'm sorry for not having been more precise.

My proposal would affect directly Array, Hash, Range & Struct. Traversal would be guarded against infinite recursion, like #inspect and al. Enumerator simply forwards the recursive call to the arguments used when contructing them, as if these were stored as instance variables.

In std libraries:
OpenStruct: nothing special to do (since sub objects are stored as instance variables)
Delegate: To be more intuitive, they probably should not decrement the level to freeze, so that freezing a given object by 2 levels would have a similar effect than freezing its delegate by 2 levels.
Set: Uses a hash, so should also forward the call without decrementing the level. Freezing a set would thus have similar effect to freezing an array.
StringIO: Forwards the call to the underlying string.

I feel the the lambda example is not an issue. The local variable "a" doesn't belong to the lambda, and lambdas have no mutating methods anyways, so anylambda.freeze, anylambda.freeze(-1) have the same (trivial) effect (unless they somehow get instance variables, in which case these get frozen like any other object).

=end

#4 Updated by Kazuhiro NISHIYAMA about 4 years ago

  • Target version changed from 1.9.2 to 2.0.0

=begin

=end

#5 Updated by Shyouhei Urabe over 3 years ago

  • Status changed from Open to Assigned

=begin

=end

#6 Updated by Yutaka HARA over 1 year ago

  • Description updated (diff)
  • Target version changed from 2.0.0 to next minor

#7 Updated by Boris Stitnicky 12 months ago

I would beg to make the method name #deep_freeze, if possible.

#8 Updated by Marc-Andre Lafortune 8 months ago

Slide added for deep_freeze

#9 Updated by Yukihiro Matsumoto 8 months ago

  • Status changed from Assigned to Rejected

Object#deep_freeze means you can freeze everything recursively, by definition.
But what if the target object accidentally refers objects that are not supposed to be frozen (e.g. classes)?
That would be disaster.

So, I'd rather prefer limited deep_freeze e.g.

module DeepFreezable
def deepfreeze
self.each
reference do |o|
o.deep_freeze
end
end
end

So I reject this Object#deep_freeze idea.

Matz.

#10 Updated by Marc-Andre Lafortune 8 months ago

Thanks for considering it.

I am a bit surprised about the reason for your rejection.

I mean, there are many ways in Ruby to shoot yourself in the foot and that is not a problem in my opinion.

I'm also sceptical as to how many objects that you would want to freeze contain a reference to a class.

In any case, if the problem really is of classes freezing, it would be easy to make Class#deep_freeze private to prevent that.

Also available in: Atom PDF