sawa
sawa

Reputation: 168209

Telling mutable objects

Is there a method that tells if an object is mutable, similar to mutable? in the following? If not, what is the best way to implement it?

"abcde".mutable? # => true
0.mutable? # => false

To answer mu is too short and dbenhur's question, I do not like the syntax of enumerated.inject(initial){...} or enumerated.each_with_object(initial){...}. I wanted a method that reverses the receiver and the argument, and I wanted it to be available to a wide variety of classes; so that I have:

initial.my_new_method(enumerated){...}

0.my_new_method(1..10){|sum, i| sum + i} # => 55
"a".my_new_method(b: 3, c: 4){|s, (k, v)| s + k.to_s * v} # => "abbbcccc"

This will make the return a modified version of the receiver, and is conceptually more natural. And with my_new_method, I wanted it to be non destructive. When the receiver is mutable, I further wanted to define a destructive version

initial.my_new_method!(enumerated){...}

"a".my_new_method!(b: 3, c: 4){|s, (k, v)| s << k.to_s * v} # => "abbbcccc"

So to detect whether the receiver is mutable or not is necessary. It does not matter if it is frozen. If I use the destructive version of the method with a frozen object, it will simply raise an error. Nothing wrong with that.

Upvotes: 1

Views: 175

Answers (4)

dbenhur
dbenhur

Reputation: 20398

There are two properties that can make an object immutable in ruby

1) the object may be frozen with Object#freeze, in which case your immutability check is Object#frozen?

2) The object may be an immediate value. There's no built-in method I know of to tell that an object is immediate, so one must rely on a side-effect of the immediate nature. Immediate values are not permitted to have singleton-classes defined on them, so I might try the following as a proxy:

class Object
  def immediate_value?
    class <<self; end
    return false
  rescue TypeError
    return true
  end

  def mutable?
    !(frozen? || immediate_value?)
  end
end

While this is probably a pretty reliable detector (I don't know of another mechanism that prevents opening the singleton class of an object), it does have the unfortunate side-effect of creating a singleton class for each object so queried.

Upvotes: 1

sawa
sawa

Reputation: 168209

I came up with this

class Object
  def mutable?; !!(dup rescue false) end
end

Upvotes: -2

steenslag
steenslag

Reputation: 80085

AFAIK all (unfrozen) objects are mutable, except nil, true, false and all integers and symbols.

Upvotes: 2

Hunter McMillen
Hunter McMillen

Reputation: 61540

You can call .frozen? on an object to see if that instance of an object is immutable:

1.9.3p194 :001 > a = Array.new
 => [] 
1.9.3p194 :002 > a.frozen?
 => false 
1.9.3p194 :003 > a.freeze
 => [] 
1.9.3p194 :004 > a.frozen?
 => true 

After calling .freeze no other changes will be allowed on that instance of an Object, a RuntimeError will be thrown if anyone attempts to change a frozen object.

EDIT:

As check mentions below in the comments 0.frozen? will correctly return false because 0 is an instance of the Fixnum class.

Upvotes: 1

Related Questions