https://twitter.com/lkdjiin/status/682209935237582848
1.respond_to?(:dup) #=> true
1.dup #=> TypeError
Have to manually :
class Fixnum
undef_method :dup
end
Any reasons?
TL;DR
I think ruby does what it does wrt Fixnum#dup
for human-readable error message
Ideally, would like Fixnum#respond_to?
to return false but keep the error message the same
I think defining Object#dup is reasonable
o = Object.new
o.object_id == o.dup.object_id #=> false
What it tells us is that - all Objects are potentially dup-able
so not a bad design decision IMO
Individial classes and objects can always:
- override dup and return TypeError
- undefined dup method
Not sure which method is right or i perfer!
But i think, throwing a TypeError: can't dup Fixnum
error is more human friendly than throwing a NoMethodError
Before this tweet never found it to be a problem.
But now, personally my ideal situation will be:
- there is a
Dupable
module likeComparable
with adup
method. and Fixnum does not include it - so
1.respond_to?(:dup)
to return false - but calling
1.dup
to throwNoMethodError: can't dup Fixnum
orTypeError: can't dup Fixnum
ie. throw an error with a human friendly error message
The problem with (1) is that there might be "too-many" modules :-)
can meet some of the requirements by monkey-patching it as:
class Fixnum
def respond_to?(msg_id, priv=false)
return false if msg_id.to_sym == :dup
super
end
end
1.class #=> Fixnum
1.respond_to? :dup #=> false
1.dup #~> TypeError: can't dup Fixnum
but atleast ruby is consistent:
- same error is there for symbols as well eg.
:foo.dup
- same error for Singletons
Do not know the exact technical detail, of how it is implemented
but as a mental rule of thumb,
I think of Symbols, Integers and Singletons as being interned - of sorts
I think of them being immutable and allocated once on the heap like Java's Strings
and so calling dup
on them does not make sense. hence the error
for symbols:
:foo.object_id == :foo.object_id #=> true
:foo.dup #~> TypeError: can't dup Symbol
it is consistent with Singleton as well:
require 'singleton'
class SingleKlass
include Singleton
end
SingleKlass.instance.object_id == SingleKlass.instance.object_id #=> true
SingleKlass.instance.dup #~> TypeError: can't dup instance of singleton SingleKlass
btw can dup BigDecimal
require 'bigdecimal'
BigDecimal.new("1").object_id == BigDecimal.new("1").object_id #=> false
o = BigDecimal.new("1")
o.object_id == o.dup.object_id #=> false
And all methods of Object are implemented in Fixnum, Symbol and Singleton
(Object.new.methods.sort - 1.methods.sort).empty? #=> true
(Object.new.methods.sort - :foo.methods.sort).empty? #=> true
(Object.new.methods.sort - SingleKlass.instance.methods.sort).empty? #=> true
I think, the decision for Fixnum#dup
to raise an error is similar to FrozenObject#taint returning an error
o = Object.new
o.freeze #=> true
o.frozen? #=> true
o.taint #~> RuntimeError: can't modify frozen Object
1.frozen? #=> true
1.taint #=> RuntimeError: can't modify frozen Fixnum
If a frozen object undefines taint
then, we will have to rescue NoMethodError
. but that is a very general error
Assuming that rescuing NoMethodError
is bad, we will have to check with respond_to?
before calling any method
I think both are sub-optimal.
But this is not a strong argument as both RuntimeError
and TypeError
feel like general errors
So i think ruby does what it does wrt Fixnum#dup
for human-readable error message.