Forum: Ruby Inheriting from Fixnum

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
gga (Guest)
on 2007-02-03 10:11
(Received via mailing list)
Now... this should be simple, but, alas, it is not.
I'm creating some special numeric classes, and I wanted to derive from
Fixnum, for example.

class Frame < Fixnum
end

While the above code works, doing then:

Frame.new(20)

will fail.  Technically, it is true that when creating a class derived
from a primitive type, most
operators (methods) will need to be recreated, but this should still
be possible.  It isn't clear to me
why this is not allowed.

Currently, I've gone and created a new class that contains a Fixnum
and redefined all the needed
methods.  That is:

class Frame
    def initialize(x)
       @x = Integer(x)
    end

   def to_frame
       self
   end

    def +(x)
        Frame.new( self + x.to_frame )
    end

   # many more methods here...

   def method_missing(m, *args)
        @x.send(m, *args)
   end
end


Unfortunately, since the class does not derive from Fixnum properly,
queries such as
kind_of?() or responds_to?() do not quite do what they are supposed
to.
Can anyone comment on why this design was chosen (or how can this
better be done)?
Wolfgang N. (Guest)
on 2007-02-03 11:31
(Received via mailing list)
gga schrieb:
> Now... this should be simple, but, alas, it is not.
> I'm creating some special numeric classes, and I wanted to derive from
> Fixnum, for example.

There is no "new" for "Fixnum"

irb(main):001:0> k = Fixnum.new
NoMethodError: undefined method `new' for Fixnum:Class
         from (irb):1

So it doesn't make sense to inherit from "Fixnum". The same ist true for
"Bignum" and "Float".

irb(main):001:0> i = Bignum.new
NoMethodError: undefined method `new' for Bignum:Class
         from (irb):1
irb(main):002:0> r = Float.new
NoMethodError: undefined method `new' for Float:Class
         from (irb):2

Wolfgang NĂ¡dasi-Donner
Robert K. (Guest)
on 2007-02-03 12:06
(Received via mailing list)
On 03.02.2007 09:07, gga wrote:
> will fail.  Technically, it is true that when creating a class derived
> from a primitive type, most
> operators (methods) will need to be recreated, but this should still
> be possible.  It isn't clear to me
> why this is not allowed.
>
> Currently, I've gone and created a new class that contains a Fixnum
> and redefined all the needed
> methods.  That is:

> Unfortunately, since the class does not derive from Fixnum properly,
> queries such as
> kind_of?() or responds_to?() do not quite do what they are supposed
> to.

Note that you generally should not use #kind_of? and like methods (->
Duck Typing).

> Can anyone comment on why this design was chosen (or how can this
> better be done)?

Inheriting from Fixnum is not a a good idea since instances cannot be
constructed as Wolfgang pointed out.  The way to go is delegation, as
you did.  You can however inherit any of the other classes in the
inheritance chain of Fixnum:

irb(main):006:0> 1.class
=> Fixnum
irb(main):007:0> 1.class.ancestors
=> [Fixnum, Integer, Precision, Numeric, Comparable, Object, Kernel]

One of Integer, Precision or Numeric seems most appropriate.

Few hints: there is "delegate" which greatly simplifies this task.

http://ruby-doc.org/stdlib/libdoc/delegate/rdoc/index.html

Also, if you want to do math you should implement #coerce.  Note that
unary operators have an at appended:

irb(main):016:0> class Foo; def -@; "minus" end end
=> nil
irb(main):017:0> -Foo.new
=> "minus"

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/...

Kind regards

  robert
gga (Guest)
on 2007-02-03 13:55
(Received via mailing list)
Thanks, Robert, for the answer, but it still does not fully explain
why it works like this.

> Note that you generally should not use #kind_of? and like methods (->
> Duck Typing).
>

I understand what you want to say but to have a class behave EXACTLY
like a numeric class, it should mimic the behavior of those functions,
too.  By using delegation, I'm forced to do two checks for it, which
sucks.

> Inheriting from Fixnum is not a a good idea since instances cannot be
> constructed as Wolfgang pointed out.

Obviously, I already found about that.  But the fact that it works
like that does not explain WHY this is so.  There's no reason, afaik.
Even thou in C Fixnums as stored as a single value, you can still do
stuff like:

class Fixnum
    attr_accessor :x
end

# create a new fixnum class with some additional x data
a = 10
a.x = 20
a.x
>> 20

# create another one
b = 50
b.x = 100
b.x
>> 100

# copy it
a = b
a.x
>> 100

which means Fixnum has some place where it stores additional methods
and data and its "copy constructor" knows how to properly duplicate
the whole class, without me needing to use dup.  Which means to me
Numerics SHOULD be inheritable as is.

> The way to go is delegation, as
> you did.

I know, but I am trying NOT to.  Doing so is a big mess.  Plus, I
don't get any additional methods other users may add to Numeric or
Fixnum or have to rely on method_missing which then blocks you from
calling the original's Numeric method_missing (bad idea).

> You can however inherit any of the other classes in the
> inheritance chain of Fixnum:
>
> irb(main):006:0> 1.class
> => Fixnum
> irb(main):007:0> 1.class.ancestors
> => [Fixnum, Integer, Precision, Numeric, Comparable, Object, Kernel]
>
> One of Integer, Precision or Numeric seems most appropriate.
>

You think I have not tried it? Try any of these:

Fixnum.new(20)
Integer.new(20)
Precision.new(20)
Numeric.new(20)

ALL of them are abstract classes, without a new method.  You simply
CANNOT inherit from any numeric class (or, to put it more correctly,
you can, but you cannot instantiate the object afterwards which makes
this kind of useless). Period.  It still makes no sense to me why, and
seems more like a big issue to me.

> Few hints: there is "delegate" which greatly simplifies this task.
>
> http://ruby-doc.org/stdlib/libdoc/delegate/rdoc/index.html
>

Delegate is pretty weak.  The ancestors method, for example, is not
handled correctly.

> Also, if you want to do math you should implement #coerce.  Note that
> unary operators have an at appended:
>

Well.  That's the kind of stuff I was HOPING to avoid by inheriting
from it.  Numeric#coerce as is should work fine for my own class.  I
shouldn't have to rewrite it (or several other functions, for that
matter).  I should, of course, have to rewrite operators that return
the original base type, but not everything else.

> irb(main):016:0> class Foo; def -@; "minus" end end

This tidbit is indeed useful.  I didn't know about the unary operator
in ruby.
_Kevin (Guest)
on 2007-02-03 18:05
(Received via mailing list)
On Feb 3, 6:49 am, "gga" <removed_email_address@domain.invalid> wrote:
>
> end
> b.x = 100
> which means Fixnum has some place where it stores additional methods
> calling the original's Numeric method_missing (bad idea).
>
> this kind of useless). Period.  It still makes no sense to me why, and
> > unary operators have an at appended:
> in ruby.
Part of the problem may be related to the fact that many of the built
in numeric types are immediate values.

Whenever I've needed to implement a Numeric class, I just inherit from
Numeric and then freeze it.

I suppose it might help if we understood why you want to tie your
class to Fixnum and not something more generic.

_Kevin
Robert K. (Guest)
on 2007-02-03 18:50
(Received via mailing list)
On 03.02.2007 12:49, gga wrote:
>> Note that you generally should not use #kind_of? and like methods (->
>> Duck Typing).
>
> I understand what you want to say but to have a class behave EXACTLY
> like a numeric class, it should mimic the behavior of those functions,
> too.  By using delegation, I'm forced to do two checks for it, which
> sucks.

Duck Typing is all about *not* checking.

> which means Fixnum has some place where it stores additional methods
> and data and its "copy constructor" knows how to properly duplicate
> the whole class, without me needing to use dup.  Which means to me
> Numerics SHOULD be inheritable as is.

Instead of ranting about things that do not work the way you want it you
may achieve better results by adjusting to the way things are.  It's not
that what you want is impossible.  You seem to be stuck in some other
language's / environment's mindset and apparently you did not embrace
Ruby yet.

>> The way to go is delegation, as
>> you did.
>
> I know, but I am trying NOT to.  Doing so is a big mess.  Plus, I
> don't get any additional methods other users may add to Numeric or
> Fixnum or have to rely on method_missing which then blocks you from
> calling the original's Numeric method_missing (bad idea).

(see above)

> You think I have not tried it? Try any of these:
> seems more like a big issue to me.
Ooops, you're right.  Sorry for the bad advice.  Even defining new does
not help because there is no allocate.  Alas, you don't need inheritance
because of Ruby's dynamic nature.  There's a documented way to implement
numeric classes that play nicely with built in types - and it works.

> Delegate is pretty weak.  The ancestors method, for example, is not
> handled correctly.

For good reasons.

>> Also, if you want to do math you should implement #coerce.  Note that
>> unary operators have an at appended:
>
> Well.  That's the kind of stuff I was HOPING to avoid by inheriting
> from it.  Numeric#coerce as is should work fine for my own class.  I
> shouldn't have to rewrite it (or several other functions, for that
> matter).  I should, of course, have to rewrite operators that return
> the original base type, but not everything else.

I don't believe you can use Numeric.coerce since it does not know
anything about your class.  All the math etc. will be broken.

I try differently: what kind of class do you want to implement?  Why do
you think you need to inherit?  What capabilities should instances of
your class have?

Regards

  robert
Logan C. (Guest)
on 2007-02-03 22:58
(Received via mailing list)
On Sat, Feb 03, 2007 at 08:55:06PM +0900, gga wrote:
> too.  By using delegation, I'm forced to do two checks for it, which
> class Fixnum
> b = 50
> b.x = 100
> b.x
> >> 100
>
> # copy it
> a = b
a is not a copy
> a.x
> >> 100
>
> which means Fixnum has some place where it stores additional methods
> and data and its "copy constructor" knows how to properly duplicate
> the whole class, without me needing to use dup.  Which means to me
You didn't make a copy. There is no copy constructor as in C++.
Ruby variables have reference semantics (even the special case
immediate values).

50.x #=> 100

All 50s in your program is the same 50, there is no way to create a new
one.
> Numerics SHOULD be inheritable as is.
>
You'll have to take that up with the language designer (matz.).
gga (Guest)
on 2007-02-04 18:15
(Received via mailing list)
>
> I don't believe you can use Numeric.coerce since it does not know
> anything about your class.  All the math etc. will be broken.
>

According to the docs of Numeric.coerce, it says it returns the
original base class.
If the method had been coded using a form like:
self.class.new()
it would work properly across any numeric and derived type (assuming
of course new existed in the first place).
Looking at the C source, I can see of course this is not case, so I
need to provide my own coerce, but oh well... the docs description are
somewhat incorrect.

> I try differently: what kind of class do you want to implement?  Why do
> you think you need to inherit?  What capabilities should instances of
> your class have?
>

Simple.  I want a Fixnum.   I don't want to pollute Fixnum's namespace
and I don't want a Fixnum to automatically be promoted to my class,
while at the same time, my class should be compatible with ALL of
fixnum methods (odd? even?, etc).
Think of a Year class (yes, I know there's Date and DateTime -- my
class is not exactly this but will do as an example).  A year is
represented by a Fixnum, but it is not a Fixnum.
Yet I expect all Fixnum operations to work on it, returning a new Year
in case of additions, etc.  I also don't want a normal Fixnum to be
treated as a year without a conversion function or instantiation thru
Year.new().  Also, I want to have a lof of conversion functions within
Year that are not applicable to Fixnum ( to_months, to_days, etc ).
Right now, to do that, I have to actually recreate all of the methods
in Fixnum/Numeric/etc. while if I was inheriting from it, I would only
have to recreate half of them or less or rely on method_missing
(yuck).
Looking at the C code, I see that new() exists in Numeric and Float,
but it is specifically removed:
    rb_cInteger = rb_define_class("Integer", rb_cNumeric);
    rb_undef_alloc_func(rb_cInteger);
    rb_undef_method(CLASS_OF(rb_cInteger), "new");

#....and for floats..
    rb_undef_alloc_func(rb_cFloat);
    rb_undef_method(CLASS_OF(rb_cFloat), "new");

The ruby source code contains no explanation for the removal of new.
That's why I'm wondering what the reasoning behind this was.  It does
not seem to be technical limitation in the ruby interpreter.
Currently, it does not make much sense to me.
Robert K. (Guest)
on 2007-02-05 00:36
(Received via mailing list)
On 04.02.2007 17:13, gga wrote:
> represented by a Fixnum, but it is not a Fixnum.
> Yet I expect all Fixnum operations to work on it, returning a new Year
> in case of additions, etc.  I also don't want a normal Fixnum to be
> treated as a year without a conversion function or instantiation thru
> Year.new().  Also, I want to have a lof of conversion functions within
> Year that are not applicable to Fixnum ( to_months, to_days, etc ).

Is it a year of the century or years difference?  How would you
implement to_days? I don't think this is worth another class - and btw.
there are classes for date and time handling in Ruby which support all
the math you typically need.  Plus, you can also define methods in a
more functional way (i.e. def year_to_months(year) etc.).

> Right now, to do that, I have to actually recreate all of the methods
> in Fixnum/Numeric/etc. while if I was inheriting from it, I would only
> have to recreate half of them or less or rely on method_missing
> (yuck).

You can still use delegator...

> The ruby source code contains no explanation for the removal of new.
> That's why I'm wondering what the reasoning behind this was.  It does
> not seem to be technical limitation in the ruby interpreter.
> Currently, it does not make much sense to me.

It's an internal optimization.  Fixnums do not have to be created and
they never have to be GC'ed.  This goes a long way to make math faster
than it would be with ordinary objects (at least for Fixnums).

Regards

  robert
gga (Guest)
on 2007-02-05 03:10
(Received via mailing list)
On 4 feb, 19:33, Robert K. <removed_email_address@domain.invalid> wrote:
> > while at the same time, my class should be compatible with ALL of
> Is it a year of the century or years difference?  How would you
> implement to_days? I don't think this is worth another class - and btw.
> there are classes for date and time handling in Ruby which support all
> the math you typically need.  Plus, you can also define methods in a
> more functional way (i.e. def year_to_months(year) etc.).

Robert, I just made an example for you to understand.  I'm not quite
creating a Year class, albeit I am creating something that is somewhat
related ( if you want to know, for video/film work, it is a Frame
class, where a frame represents usually represents 1/24 or 1/30 --or 1/
fps- of a second).  As such, I do want to have conversion functions
that will report the length of time the frames represent but in
seconds, minutes, hours, etc.  I don't want a Fixnum to be treated as
a Frame, as, for example, in the context I'll be using it, negative
"frames" make no sense.

>
> > Right now, to do that, I have to actually recreate all of the methods
> > in Fixnum/Numeric/etc. while if I was inheriting from it, I would only
> > have to recreate half of them or less or rely on method_missing
> > (yuck).
>
> You can still use delegator...

I can (and as I said, I have already), it is just pretty inelegant and
inefficient.  Taking care of kind_of? and similar is pretty messy (but
needed as I do want to have Range of frames, for example).
Therefore, I want to know the reason of WHY I am forced to use a
delegator for deriving one of the most basic classes possible.  So
far, I don't know why I need to go through such a cumbersome approach.

>
> It's an internal optimization.  Fixnums do not have to be created and
> they never have to be GC'ed.  This goes a long way to make math faster
> than it would be with ordinary objects (at least for Fixnums).
>

Very well.  It still does not explain much.  Why can't I take
advantage of this optimization for my own class?  As I showed already,
Fixnums are also allowed to contain additional data and new methods
with them... so I know they are somehow GCed (not the number itself,
but any associated data with the Fixnum class).  I don't think Ruby is
doing *that* much of an optimization if you ask me (ie. every operator
always pays the cost of method dispatching).  There's obviously a
memory optimization of having the Fixnum be represented by VALUE
itself (instead of it being a pointer to something else), but not much
else, as far as I can tell.

Perhaps I should rephrase my question in a simpler manner.   Why does
the Ruby source code contain an undef() of "new" for Numeric and
Floats?  Once I understand why those lines in the source code are
there, I can then decide whether indeed a delegator is the one and
only solution to whatever problem those lines are trying to address.
So far, I am seeing it as annoying (and unneeded) limitation.

If I remove those lines from the C code, sure enough I can then
instantiate a Numeric or an Integer through inheritance, but still not
pass a default value to the constructor as the initialize in Integer
fails (it seems it defaults to Object's initialize).
It seems like if I create my own initialize function in C that sets
the FIXNUM flag, but not the IMMEDIATE flag, it should be possible to
create an efficient pseudo-int class that you can inherit from
normally.  I understand some of the dangers of this, but I think it
can be done safely, by hacking the ruby source code some more.
gga (Guest)
on 2007-02-05 03:25
(Received via mailing list)
Another thing probably worth looking is in how Python handles this
with their new classes in their C code, as it is definitively more
elegant than what Ruby does here (usually, it is the other way around,
but not for this).

In Python:

class A(int):
     def A(x):
         super

a = A(5)

# you obviously still need to redefine operators if you want # math
stuff to work, but it all seems to work more or less
# the way you expect a full-OO language to work.
gga (Guest)
on 2007-02-05 04:15
(Received via mailing list)
On 4 feb, 22:22, "gga" <removed_email_address@domain.invalid> wrote:
> Another thing probably worth looking is in how Python handles this
> with their new classes in their C code, as it is definitively more
> elegant than what Ruby does here (usually, it is the other way around,
> but not for this).

Well, I looked at how it is being done in Python2.5.  Somewhat
disappointed.  There's nothing special about them, unlike ruby's
handling of them.  They are treated just like normal objects.
The only smart thing about them in python is that the memory
allocation for numbers is done from a memory pool, so they avoid
calling malloc/free most of the time.
Logan C. (Guest)
on 2007-02-05 05:52
(Received via mailing list)
On Mon, Feb 05, 2007 at 10:10:05AM +0900, gga wrote:

> I can (and as I said, I have already), it is just pretty inelegant and
> inefficient.  Taking care of kind_of? and similar is pretty messy (but
> needed as I do want to have Range of frames, for example).
> Therefore, I want to know the reason of WHY I am forced to use a
> delegator for deriving one of the most basic classes possible.  So
> far, I don't know why I need to go through such a cumbersome approach.
>
1) Fixnum and Bignum are implementation details. If you want to
inherit from anything it should be from Integer. Programmer's that add
methods to just Fixnum are likely making an error.

2) There is no need to "take care of" kind_of? for Ranges.

Ranges rely on #succ and #<=>, that's it.
% irb
irb(main):001:0> class Frame
irb(main):002:1>   def initialize( value )
irb(main):003:2>     @value = value
irb(main):004:2>   end
irb(main):005:1>
irb(main):006:1*   def succ
irb(main):007:2>     Frame.new( @value.succ )
irb(main):008:2>   end
irb(main):009:1>
irb(main):010:1*   def <=>(other)
irb(main):011:2>     @value <=> other.instance_variable_get("@value")
irb(main):012:2>   end
irb(main):013:1>
irb(main):014:1*   include Comparable
irb(main):015:1> end
=> Frame
irb(main):016:0> start = Frame.new(0)
=> #<Frame:0x31dd3c @value=0>
irb(main):017:0> stop = Frame.new(5)
=> #<Frame:0x31ae20 @value=5>
irb(main):018:0> (start..stop).to_a
=> [#<Frame:0x31dd3c @value=0>, #<Frame:0x318684 @value=1>,
#<Frame:0x31865c @value=2>, #<Frame:0x318634 @value=3>, #<Frame:0x31860c
@value=4>, #<Frame:0x3185e4 @value=5>]
irb(main):019:0> (start..stop).each { |x| p x }
#<Frame:0x31dd3c @value=0>
#<Frame:0x314020 @value=1>
#<Frame:0x313f94 @value=2>
#<Frame:0x313f44 @value=3>
#<Frame:0x313ef4 @value=4>
#<Frame:0x313ea4 @value=5>
=> #<Frame:0x31dd3c @value=0>..#<Frame:0x31ae20 @value=5>
irb(main):020:0> (start..stop).include? Frame.new(3)
=> true
irb(main):021:0> (start..stop).include? Frame.new(9)
=> false

3) The reason you are forced to use a delegator to "derive" from one of
the most basic classes possible is because it is one of the most basic
classes possible. A Fixnum is a primitive. Inheriting from it is akin to
trying to implement arrays in C w/o using arrays. (Note that saying
"well I can do it in python" doesn't hold water, as python selected
different primitives)
_Kevin (Guest)
on 2007-02-05 05:55
(Received via mailing list)
On Feb 4, 8:08 pm, "gga" <removed_email_address@domain.invalid> wrote:
> > > Simple.  I want a Fixnum.   I don't want to pollute Fixnum's namespace
> > > Year that are not applicable to Fixnum ( to_months, to_days, etc ).
> class, where a frame represents usually represents 1/24 or 1/30 --or 1/
> > > have to recreate half of them or less or rely on method_missing
>
> but any associated data with the Fixnum class).  I don't think Ruby is
> only solution to whatever problem those lines are trying to address.
> can be done safely, by hacking the ruby source code some more.
You know, there are a number of nice packages out there that can
handle the time/unit math just fine.
ruby-units, facets, at least.

>From your description, it sounds like making a numeric class to
describe your frames is perhaps incorrect.

Wouldn't it be better to have a Frame class that acted as a container
for other attributes, like time_offset, frame_rate, etc..??

To me it makes no sense to add frame 12 to frame 23, which I would
think is required for a numeric class.

Now I do understand that you might want to know the difference in time
between frame 12 and frame 23, which is a different sort of thing.

_Kevin
Robert K. (Guest)
on 2007-02-06 15:21
(Received via mailing list)
On 05.02.2007 02:08, gga wrote:
> Robert, I just made an example for you to understand.  I'm not quite
> creating a Year class, albeit I am creating something that is somewhat
> related ( if you want to know, for video/film work, it is a Frame
> class, where a frame represents usually represents 1/24 or 1/30 --or 1/
> fps- of a second).

In my world a frame /is not a/ number but it may /have a/ number.  You
seem to live in a very different world.  I'm afraid I can't help you
there.

Good luck

  robert
Rolf Gebauer (Guest)
on 2007-02-06 18:20
(Received via mailing list)
On 3 Feb., 09:07, "gga" <removed_email_address@domain.invalid> wrote:
>
> will fail

As I' have learned from this thread, you can't inherit from Fixnum !
But maybe You can get along with Rational and its non private method !
new

require 'rational'
class Frame < Rational
end


f1 = F.new!(99)
f2 = F.new!66)


puts f2 - f1   # 33

Rolf Gebauer
This topic is locked and can not be replied to.