Feature #2509

Recursive freezing?

Added by marcandre (Marc-Andre Lafortune) over 2 years ago. Updated about 1 year ago.

[ruby-core:27256]
Status:Assigned Start date:12/21/2009
Priority:Normal Due date:
Assignee:matz (Yukihiro Matsumoto) % Done:

0%

Category:core
Target version:2.0.0

Description

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é

History

Updated by shyouhei (Shyouhei Urabe) over 2 years ago

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

Updated by RickDeNatale (Rick DeNatale) over 2 years ago

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

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

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).

Updated by znz (Kazuhiro NISHIYAMA) about 2 years ago

  • Target version changed from 1.9.2 to 2.0.0

Updated by shyouhei (Shyouhei Urabe) over 1 year ago

  • Status changed from Open to Assigned

Also available in: Atom PDF