Project

General

Profile

Actions

Feature #2509

closed

Recursive freezing?

Added by marcandre (Marc-Andre Lafortune) almost 15 years ago. Updated over 10 years ago.

Status:
Rejected
Target version:
[ruby-core:27256]

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:

DEFAULT_SEND_FILE_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


Files

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

Related issues 1 (0 open1 closed)

Related to Ruby master - Feature #17145: Ractor-aware `Object#deep_freeze`RejectedActions
Actions #1

Updated by shyouhei (Shyouhei Urabe) almost 15 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

Actions #2

Updated by RickDeNatale (Rick DeNatale) almost 15 years ago

=begin
On Sun, Dec 20, 2009 at 11:02 PM, Shyouhei Urabe 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

Actions #3

Updated by marcandre (Marc-Andre Lafortune) almost 15 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 any_lambda.freeze, any_lambda.freeze(-1) have the same (trivial) effect (unless they somehow get instance variables, in which case these get frozen like any other object).

=end

Actions #4

Updated by znz (Kazuhiro NISHIYAMA) over 14 years ago

  • Target version changed from 1.9.2 to 2.0.0

=begin

=end

Actions #5

Updated by shyouhei (Shyouhei Urabe) about 14 years ago

  • Status changed from Open to Assigned

=begin

=end

Updated by yhara (Yutaka HARA) about 12 years ago

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

Updated by Anonymous over 11 years ago

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

Actions #9

Updated by matz (Yukihiro Matsumoto) about 11 years 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 deep_freeze
    self.each_reference do |o|
      o.deep_freeze
    end
  end
end

So I reject this Object#deep_freeze idea.

Matz.

Updated by marcandre (Marc-Andre Lafortune) about 11 years 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.

Updated by alexeymuranov (Alexey Muranov) over 10 years ago

This looks to me like a strange reason to reject too. If a programmer recursively freezes an object that will freeze things that the programmer did not want to freeze, i think the program will probably crash immediately (with error messages mentioning frozen objects), and the programmer will either discover a bug, or will learn better how the program works.

P.S. Maybe the concern is that because of Duck Typing, programmers are not expected to know what their objects are...

P.P.S.(2014-06-16) It is not even necessary to have any unfrozen object: programming with everything immutable should work well too.

Updated by alexeymuranov (Alexey Muranov) over 10 years ago

I have thought of the following solution: add simultaneously Object#deep_freeze and Object#insulated?. If obj.insulated? is false, obj.deep_freeze (or deep_freeze! by the way?) should raise an error.

Updated by shyouhei (Shyouhei Urabe) over 10 years ago

I agree. Even if we allow a programmer to shoot themselves, they must be able to know what's going on when they do so. If a collection cannot be frozen at once, that should be notified or detected somehow.

Updated by marcandre (Marc-Andre Lafortune) over 10 years ago

I think we can get the effect of having an error for edge cases (deep freezing a class, for example) by simply removing Module#deep_freeze. I feel there's no need to add a new method like insulated?, simply ask instead respond_to?(:deep_freeze).

Actions #15

Updated by marcandre (Marc-Andre Lafortune) about 4 years ago

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0