Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.ruby > #2426 > unrolled thread

Hash Surprises with Fixnum, #hash, and #eql?

Started byClifford Heath <no@spam.please.net>
First post2011-04-07 14:01 +1000
Last post2011-04-07 17:16 -0700
Articles 20 on this page of 44 — 9 participants

Back to article view | Back to comp.lang.ruby


Contents

  Hash Surprises with Fixnum, #hash, and #eql? Clifford Heath <no@spam.please.net> - 2011-04-07 14:01 +1000
    Re: Hash Surprises with Fixnum, #hash, and #eql? Robert Klemme <shortcutter@googlemail.com> - 2011-04-07 04:19 -0500
      Re: Hash Surprises with Fixnum, #hash, and #eql? Phillip Gawlowski <cmdjackryan@googlemail.com> - 2011-04-07 06:20 -0500
        Re: Hash Surprises with Fixnum, #hash, and #eql? Robert Klemme <shortcutter@googlemail.com> - 2011-04-07 06:53 -0500
          Re: Hash Surprises with Fixnum, #hash, and #eql? Phillip Gawlowski <cmdjackryan@googlemail.com> - 2011-04-07 08:21 -0500
            Re: Hash Surprises with Fixnum, #hash, and #eql? Robert Klemme <shortcutter@googlemail.com> - 2011-04-07 08:43 -0500
              Re: Hash Surprises with Fixnum, #hash, and #eql? Phillip Gawlowski <cmdjackryan@googlemail.com> - 2011-04-07 09:02 -0500
                Re: Hash Surprises with Fixnum, #hash, and #eql? Robert Klemme <shortcutter@googlemail.com> - 2011-04-07 09:28 -0500
                  Re: Hash Surprises with Fixnum, #hash, and #eql? Phillip Gawlowski <cmdjackryan@googlemail.com> - 2011-04-07 09:49 -0500
                    Re: Hash Surprises with Fixnum, #hash, and #eql? Robert Klemme <shortcutter@googlemail.com> - 2011-04-07 10:11 -0500
                      Re: Hash Surprises with Fixnum, #hash, and #eql? Phillip Gawlowski <cmdjackryan@googlemail.com> - 2011-04-07 10:31 -0500
                Re: Hash Surprises with Fixnum, #hash, and #eql? Brian Candler <b.candler@pobox.com> - 2011-04-07 11:06 -0500
                  Re: Hash Surprises with Fixnum, #hash, and #eql? Vincent Manis <vmanis@telus.net> - 2011-04-07 17:42 -0500
                  Re: Hash Surprises with Fixnum, #hash, and #eql? Robert Klemme <shortcutter@googlemail.com> - 2011-04-08 01:55 -0500
      Re: Hash Surprises with Fixnum, #hash, and #eql? Clifford Heath <no@spam.please.net> - 2011-04-08 17:29 +1000
        Re: Hash Surprises with Fixnum, #hash, and #eql? Robert Klemme <shortcutter@googlemail.com> - 2011-04-08 05:12 -0500
          Re: Hash Surprises with Fixnum, #hash, and #eql? Clifford Heath <no@spam.please.net> - 2011-04-09 11:21 +1000
            Re: Hash Surprises with Fixnum, #hash, and #eql? Charles Oliver Nutter <headius@headius.com> - 2011-04-10 19:02 -0500
              Re: Hash Surprises with Fixnum, #hash, and #eql? Clifford Heath <no@spam.please.net> - 2011-04-11 13:17 +1000
                Re: Hash Surprises with Fixnum, #hash, and #eql? Robert Klemme <shortcutter@googlemail.com> - 2011-04-12 04:09 -0500
                  Re: Hash Surprises with Fixnum, #hash, and #eql? Clifford Heath <no@spam.please.net> - 2011-04-13 12:02 +1000
                    Re: Hash Surprises with Fixnum, #hash, and #eql? Charles Oliver Nutter <headius@headius.com> - 2011-04-13 00:51 -0500
                      Re: Hash Surprises with Fixnum, #hash, and #eql? Clifford Heath <no@spam.please.net> - 2011-04-13 16:32 +1000
                        Re: Hash Surprises with Fixnum, #hash, and #eql? Charles Oliver Nutter <headius@headius.com> - 2011-04-13 07:23 -0500
                          Re: Hash Surprises with Fixnum, #hash, and #eql? Clifford Heath <no@spam.please.net> - 2011-04-14 07:38 +1000
                            Re: Hash Surprises with Fixnum, #hash, and #eql? Charles Oliver Nutter <headius@headius.com> - 2011-04-14 00:36 -0500
                              Re: Hash Surprises with Fixnum, #hash, and #eql? Clifford Heath <no@spam.please.net> - 2011-04-14 15:55 +1000
                                Re: Hash Surprises with Fixnum, #hash, and #eql? Charles Oliver Nutter <headius@headius.com> - 2011-04-19 17:41 -0500
                                  Re: Hash Surprises with Fixnum, #hash, and #eql? Clifford Heath <no@spam.please.net> - 2011-04-21 12:49 +1000
                                    Re: Hash Surprises with Fixnum, #hash, and #eql? Robert Klemme <shortcutter@googlemail.com> - 2011-04-21 06:28 -0500
                                      Re: Hash Surprises with Fixnum, #hash, and #eql? Clifford Heath <no@spam.please.net> - 2011-04-21 22:32 +1000
                                        Re: Hash Surprises with Fixnum, #hash, and #eql? Robert Klemme <shortcutter@googlemail.com> - 2011-04-21 08:51 -0500
                                        Re: Hash Surprises with Fixnum, #hash, and #eql? John W Higgins <wishdev@gmail.com> - 2011-04-21 11:11 -0500
                                      Re: Hash Surprises with Fixnum, #hash, and #eql? Charles Oliver Nutter <headius@headius.com> - 2011-04-27 00:33 -0500
                                        Re: Hash Surprises with Fixnum, #hash, and #eql? Robert Klemme <shortcutter@googlemail.com> - 2011-04-27 01:44 -0500
                                          Re: Hash Surprises with Fixnum, #hash, and #eql? Charles Oliver Nutter <headius@headius.com> - 2011-04-27 16:22 -0500
                                    Re: Hash Surprises with Fixnum, #hash, and #eql? Charles Oliver Nutter <headius@headius.com> - 2011-04-27 00:23 -0500
                                      Re: Hash Surprises with Fixnum, #hash, and #eql? Clifford Heath <no@spam.please.net> - 2011-04-30 17:43 +1000
                                        Re: Hash Surprises with Fixnum, #hash, and #eql? Charles Oliver Nutter <headius@headius.com> - 2011-05-01 23:01 -0500
                    Re: Hash Surprises with Fixnum, #hash, and #eql? Robert Klemme <shortcutter@googlemail.com> - 2011-04-13 02:08 -0500
                      Re: Hash Surprises with Fixnum, #hash, and #eql? Clifford Heath <no@spam.please.net> - 2011-04-14 07:36 +1000
        Re: Hash Surprises with Fixnum, #hash, and #eql? Xavier Noria <fxn@hashref.com> - 2011-04-08 19:00 -0500
          Re: Hash Surprises with Fixnum, #hash, and #eql? Clifford Heath <no@spam.please.net> - 2011-04-09 11:22 +1000
    Re: Hash Surprises with Fixnum, #hash, and #eql? Mazi Ayışığı <mazi.ayisigi@gmail.com> - 2011-04-07 17:16 -0700

Page 1 of 3  [1] 2 3  Next page →


#2426 — Hash Surprises with Fixnum, #hash, and #eql?

FromClifford Heath <no@spam.please.net>
Date2011-04-07 14:01 +1000
SubjectHash Surprises with Fixnum, #hash, and #eql?
Message-ID<4d9d372e$0$13393$afc38c87@news.optusnet.com.au>
Folk,

I have a class which delegates for Integer, and wants to behave as much
like a real Integer as possible (except for being able to be subclassed).
It *mostly* works... but falls foul of Ruby's various hacks, errors, and
internal optimisations in the Fixnum and Hash classes.

In particular, the Hash implementations work (and break!) differently in
MRI, Rubinius and JRuby. It's documented to use only #hash and #eql?,
but that's not always true (sometimes these have hard-wired optimsations).

The Hash documentation does not say whether #eql? will be called only on
items in the hash, or only on keys being used to probe the hash. It should
be one or the other, since a.eql?(b) might not always mean b.eql?(a).

Please peruse this code: <https://gist.github.com/906998>, try it on the
various Ruby versions, and also try it with the Fixnum monkey-patches
removed.

You'll see that the behaviour is very unpredictable.

Clifford Heath.

[toc] | [next] | [standalone]


#2438

FromRobert Klemme <shortcutter@googlemail.com>
Date2011-04-07 04:19 -0500
Message-ID<BANLkTinemPaPqB8CTTb8eeGgxHzjXki+GQ@mail.gmail.com>
In reply to#2426
On Thu, Apr 7, 2011 at 6:05 AM, Clifford Heath <no@spam.please.net> wrote:
> I have a class which delegates for Integer, and wants to behave as much
> like a real Integer as possible (except for being able to be subclassed).

There's still a lot missing for a number replacement.  Please see
http://blog.rubybestpractices.com/posts/rklemme/019-Complete_Numeric_Class.html

I also doubt whether it is a good idea to allow for subclassing of an
integer like class.  What use case do you have in mind which would
make this necessary?

> It *mostly* works... but falls foul of Ruby's various hacks, errors, and
> internal optimisations in the Fixnum and Hash classes.

> In particular, the Hash implementations work (and break!) differently in
> MRI, Rubinius and JRuby. It's documented to use only #hash and #eql?,
> but that's not always true (sometimes these have hard-wired optimsations).

When you violate contracts you cannot expect code to work properly.

> The Hash documentation does not say whether #eql? will be called only on
> items in the hash, or only on keys being used to probe the hash. It should
> be one or the other, since a.eql?(b) might not always mean b.eql?(a).

But that is the contract as far as I can see.  Having different
results for both violates the equivalence relation which means all
bets are off.

> Please peruse this code: <https://gist.github.com/906998>, try it on the
> various Ruby versions, and also try it with the Fixnum monkey-patches
> removed.
>
> You'll see that the behaviour is very unpredictable.

Yes, because of your violation of the contract.  You have there a nice
demonstration why it is a bad idea most of the time to fiddle with
core class method implementations.  They are used everywhere and you
cannot foresee the effects of changing their implementation on other
code.

Kind regards

robert

-- 
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

[toc] | [prev] | [next] | [standalone]


#2447

FromPhillip Gawlowski <cmdjackryan@googlemail.com>
Date2011-04-07 06:20 -0500
Message-ID<BANLkTik3tkRKP_d4BTWyE02SXgBcG61mnQ@mail.gmail.com>
In reply to#2438
On Thu, Apr 7, 2011 at 11:19 AM, Robert Klemme
<shortcutter@googlemail.com> wrote:
>
> I also doubt whether it is a good idea to allow for subclassing of an
> integer like class.  What use case do you have in mind which would
> make this necessary?

Complex numbers come to mind:

3 - 2j [+|*] 45^(j * e * 44°).

Very different semantics for addition and multiplication of those than
for your normal space numbers, including conversion from Cartesian to
polar form. Bit of a textbook case for the benefits of inheritance and
function overloading. :P

It'd be better to have those be a sub-class of Float, though.

-- 
Phillip Gawlowski

Though the folk I have met,
(Ah, how soon!) they forget
When I've moved on to some other place,
There may be one or two,
When I've played and passed through,
Who'll remember my song or my face.

[toc] | [prev] | [next] | [standalone]


#2449

FromRobert Klemme <shortcutter@googlemail.com>
Date2011-04-07 06:53 -0500
Message-ID<BANLkTin=Q1M3Djs3e81RorreGw0rShXXqg@mail.gmail.com>
In reply to#2447
On Thu, Apr 7, 2011 at 1:20 PM, Phillip Gawlowski
<cmdjackryan@googlemail.com> wrote:
> On Thu, Apr 7, 2011 at 11:19 AM, Robert Klemme
> <shortcutter@googlemail.com> wrote:
>>
>> I also doubt whether it is a good idea to allow for subclassing of an
>> integer like class.  What use case do you have in mind which would
>> make this necessary?
>
> Complex numbers come to mind:

Why bother, it has been done already.

irb(main):006:0> x = Complex(0,-1)
=> (0-1i)
irb(main):007:0> x * x
=> (-1+0i)
irb(main):008:0> (x * x)+0
=> (-1+0i)
irb(main):009:0> (x * x).to_int
=> -1

And they do play nicely as ints - as long as it's possible:

irb(main):011:0> %w{foo bar baz}[x*x]
=> "baz"
irb(main):012:0> %w{foo bar baz}[x]
RangeError: can't convert 0-1i into Integer
        from (irb):12:in `to_i'
        from (irb):12:in `to_int'
        from (irb):12:in `[]'
        from (irb):12
        from /opt/bin/irb19:12:in `<main>'

> 3 - 2j [+|*] 45^(j * e * 44°).
>
> Very different semantics for addition and multiplication of those than
> for your normal space numbers, including conversion from Cartesian to
> polar form. Bit of a textbook case for the benefits of inheritance and
> function overloading. :P
>
> It'd be better to have those be a sub-class of Float, though.

Actually it's Numeric which is correct because not every Complex _is a_ Float!

irb(main):010:0> Complex.ancestors
=> [Complex, Numeric, Comparable, Object, Kernel, BasicObject]

Cheers

robert

-- 
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

[toc] | [prev] | [next] | [standalone]


#2455

FromPhillip Gawlowski <cmdjackryan@googlemail.com>
Date2011-04-07 08:21 -0500
Message-ID<BANLkTinFSSKEnkpbgTZDsYcrXVgFFxPkMg@mail.gmail.com>
In reply to#2449
On Thu, Apr 7, 2011 at 1:53 PM, Robert Klemme
<shortcutter@googlemail.com> wrote:
>
> Actually it's Numeric which is correct because not every Complex _is a_ Float!

Unless you convert from polar form* to Cartesian form:

Let's take "45e^(j * 44°)":

32.37029101523930127102246035055203587038080515238845970760... +
31.25962667065487789953828347402088034639160866937838052053... i

From <http://www.wolframalpha.com/input/?i=45*e^%2844%C2%B0i%29>.

* Which you are much more likely to encounter than the Cartesian form,
considering complex numbers are most useful when dealing with wave
forms.

-- 
Phillip Gawlowski

Though the folk I have met,
(Ah, how soon!) they forget
When I've moved on to some other place,
There may be one or two,
When I've played and passed through,
Who'll remember my song or my face.

[toc] | [prev] | [next] | [standalone]


#2456

FromRobert Klemme <shortcutter@googlemail.com>
Date2011-04-07 08:43 -0500
Message-ID<BANLkTikw8TpU1mDXeU35Wx0-YJSnsYdKXw@mail.gmail.com>
In reply to#2455
On Thu, Apr 7, 2011 at 3:21 PM, Phillip Gawlowski
<cmdjackryan@googlemail.com> wrote:
> On Thu, Apr 7, 2011 at 1:53 PM, Robert Klemme
> <shortcutter@googlemail.com> wrote:
>>
>> Actually it's Numeric which is correct because not every Complex _is a_ Float!
>
> Unless you convert from polar form* to Cartesian form:
>
> Let's take "45e^(j * 44°)":
>
> 32.37029101523930127102246035055203587038080515238845970760... +
> 31.25962667065487789953828347402088034639160866937838052053... i
>
> From <http://www.wolframalpha.com/input/?i=45*e^%2844%C2%B0i%29>.
>
> * Which you are much more likely to encounter than the Cartesian form,
> considering complex numbers are most useful when dealing with wave
> forms.

My math is a bit rusty in that area, but I don't think your argument
holds: cartesian and polar are just two ways to represent a complex
number.  But this does not change numeric properties of the class of
complex numbers.  No matter what representation you use, (0+1i) is
neither a real number (what float conceptually models) nor a rational
number (what float technically implements).  Instead, real and
rational numbers are both subsets of complex.

If you see inheritance as "is a" relationship then inheritance would
be Rational < Real < Complex but not the other way round.  Otherwise
you cannot use a subclass instance everywhere you were using a
superclass instance.

Further links:

http://en.wikipedia.org/wiki/Rational_number
http://en.wikipedia.org/wiki/Real_number
http://en.wikipedia.org/wiki/Complex_number

Kind regards

robert

-- 
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

[toc] | [prev] | [next] | [standalone]


#2457

FromPhillip Gawlowski <cmdjackryan@googlemail.com>
Date2011-04-07 09:02 -0500
Message-ID<BANLkTik8kiEoTB0i3wCr6T4repej7=xd7Q@mail.gmail.com>
In reply to#2456
On Thu, Apr 7, 2011 at 3:43 PM, Robert Klemme
<shortcutter@googlemail.com> wrote:
>
> My math is a bit rusty in that area, but I don't think your argument
> holds: cartesian and polar are just two ways to represent a complex
> number.  But this does not change numeric properties of the class of
> complex numbers.  No matter what representation you use, (0+1i) is
> neither a real number (what float conceptually models) nor a rational
> number (what float technically implements).  Instead, real and
> rational numbers are both subsets of complex.

Given that there are infinitely more irrational than rational numbers,
it's much more common to represent a complex number with irrational
numbers than rational ones (i.e. floats instead of integers). Thus,
most (for want of a better word) real mathematical operations done
with complex numbers are done with irrational numbers. Cartesian and
polar forms make certain mathematical operations easier, but that's
more or less it (the truth is more complex, but I CBA to look into
trascendental numbers, Euler's number, &c.).

And yes, both rational and irrational numbers are subsets of complex,
obviously (with rational numbers being a subset of irrational numbers,
to simplify extremely). :)

> If you see inheritance as "is a" relationship then inheritance would
> be Rational < Real < Complex but not the other way round.  Otherwise
> you cannot use a subclass instance everywhere you were using a
> superclass instance.

Though, does the "is a" relationship hold up? I think it's more of a
"kind of" relationship, where subsequent classes are defined in ever
more detail (so, you'd inherit Floats from Integers, and Complex from
Float).

Of course, the clean world of maths doesn't map 1:1 to computational
systems, so I see the value in both approaches.

But, frankly, given the differences and additional properties of
complex numbers, I'd derive it from Numeric as well, simply to limit
the side effects the other numeric classes introduce (Floats and their
CPU-internal representation give me nightmares :P).

-- 
Phillip Gawlowski

Though the folk I have met,
(Ah, how soon!) they forget
When I've moved on to some other place,
There may be one or two,
When I've played and passed through,
Who'll remember my song or my face.

[toc] | [prev] | [next] | [standalone]


#2461

FromRobert Klemme <shortcutter@googlemail.com>
Date2011-04-07 09:28 -0500
Message-ID<BANLkTimEgB3NWgZUY3YK_Og+piPC2e9tNw@mail.gmail.com>
In reply to#2457
On Thu, Apr 7, 2011 at 4:02 PM, Phillip Gawlowski
<cmdjackryan@googlemail.com> wrote:
> On Thu, Apr 7, 2011 at 3:43 PM, Robert Klemme
> <shortcutter@googlemail.com> wrote:

>> If you see inheritance as "is a" relationship then inheritance would
>> be Rational < Real < Complex but not the other way round.  Otherwise
>> you cannot use a subclass instance everywhere you were using a
>> superclass instance.
>
> Though, does the "is a" relationship hold up? I think it's more of a
> "kind of" relationship, where subsequent classes are defined in ever
> more detail (so, you'd inherit Floats from Integers, and Complex from
> Float).

Well, even with technical inheritance ("kind of") sub often add state
(i.e. member variables) but do only restrict valid values of
superclass state if at all.  The cannot do otherwise because then
superclass methods may break.  Silly example: superclass holds an
index which must be >= 0.  All superclass methods use that index for
some kind of lookup.  Assuming a sub class would suddenly set that
value to -13 the superclass contract would be violated.  Now, if you
let Complex inherit from Real (trying to avoid "irrational" :-)) you
would add another field for imaginary part.  So far so good, but
method to_f would sometimes throw an exception in Complex which it
would never do in Real.  So suddenly Complex breaks Real's contract.

Of course, all those considerations are far less important in a nicely
duck typed language like Ruby compared to a statically typed language.
 Assuming you would do the same in Java you would have to declare the
exception (if you use checked exceptions) on Real class but state at
the same time that this class would never throw it.  Even worse, all
code using Real would have to deal with this exception by either
catching or propagating it.  Not nice.

That's why I prefer to look at inheritance as "is a" relationship:
after all OO is about better abstraction capabilities and to be able
to hide implementation details behind a clearly defined clean
interface.  If you let yourself get dragged too much into technical
issues chances are that the design comes out awful.  Only languages
which allow to inherit without publishing all features of the
inherited class (private inheritance e.g. in Eiffel) do not
necessarily suffer from these issues.  But then, inheritance is just
an implementation detail in such cases.

> Of course, the clean world of maths doesn't map 1:1 to computational
> systems, so I see the value in both approaches.

That's true.  I remember debates about the very question how to model
inheritance hierarchies for numeric types.  Unfortunately I can't
produce a reference right now.  Maybe someone else can.

> But, frankly, given the differences and additional properties of
> complex numbers, I'd derive it from Numeric as well, simply to limit
> the side effects the other numeric classes introduce (Floats and their
> CPU-internal representation give me nightmares :P).

:-)  Also, with Ruby's concept of coercion inheritance between numeric
types is probably less of an issue.

Kind regards

robert

-- 
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

[toc] | [prev] | [next] | [standalone]


#2462

FromPhillip Gawlowski <cmdjackryan@googlemail.com>
Date2011-04-07 09:49 -0500
Message-ID<BANLkTinckn+5DVnuiDDfx4u2tzNifJRZ3g@mail.gmail.com>
In reply to#2461
On Thu, Apr 7, 2011 at 4:28 PM, Robert Klemme
<shortcutter@googlemail.com> wrote:
>
> Well, even with technical inheritance ("kind of") sub often add state
> (i.e. member variables) but do only restrict valid values of
> superclass state if at all.  The cannot do otherwise because then
> superclass methods may break.  Silly example: superclass holds an
> index which must be >= 0.  All superclass methods use that index for
> some kind of lookup.  Assuming a sub class would suddenly set that
> value to -13 the superclass contract would be violated.  Now, if you
> let Complex inherit from Real (trying to avoid "irrational" :-)) you
> would add another field for imaginary part.  So far so good, but
> method to_f would sometimes throw an exception in Complex which it
> would never do in Real.  So suddenly Complex breaks Real's contract.

But that's a failure of implementation, isn't it?

If I were to implement my own class Complex, I'd have to deal with the
edge-cases that my sub-class has and can produce.

Thus, I either undefine #to_f, or redefine it so that it throws an
Exception. the value of inheritance is, after all, generalization, so
that I don't have to reimplement the wheel all the time, instead
making the wheel bigger or smaller, as the implementation requires.
That conversely also means that that more specialized sub-classes
derived from a generic-er super-class, *has* to implement an interface
that works, and works consistently.

To stay with Complex as an example:
#to_f would require an additional argument to work properly: Either
convert the real, or the imaginary part into a Float, and so would
anything derived from Complex, whatever that may be, if it has the
same properties.

> That's why I prefer to look at inheritance as "is a" relationship:
> after all OO is about better abstraction capabilities and to be able
> to hide implementation details behind a clearly defined clean
> interface.  If you let yourself get dragged too much into technical
> issues chances are that the design comes out awful.  Only languages
> which allow to inherit without publishing all features of the
> inherited class (private inheritance e.g. in Eiffel) do not
> necessarily suffer from these issues.  But then, inheritance is just
> an implementation detail in such cases.

But isn't it always?

Regarding technical issues: Design is a bit of an art; knowing when to
stop abstracting is important. ;)

-- 
Phillip Gawlowski

Though the folk I have met,
(Ah, how soon!) they forget
When I've moved on to some other place,
There may be one or two,
When I've played and passed through,
Who'll remember my song or my face.

[toc] | [prev] | [next] | [standalone]


#2464

FromRobert Klemme <shortcutter@googlemail.com>
Date2011-04-07 10:11 -0500
Message-ID<BANLkTimeMBoPwmc3-KwzUPrBBEQPzsZ2fA@mail.gmail.com>
In reply to#2462
On Thu, Apr 7, 2011 at 4:49 PM, Phillip Gawlowski
<cmdjackryan@googlemail.com> wrote:
> On Thu, Apr 7, 2011 at 4:28 PM, Robert Klemme
> <shortcutter@googlemail.com> wrote:
>>
>> Well, even with technical inheritance ("kind of") sub often add state
>> (i.e. member variables) but do only restrict valid values of
>> superclass state if at all.  The cannot do otherwise because then
>> superclass methods may break.  Silly example: superclass holds an
>> index which must be >= 0.  All superclass methods use that index for
>> some kind of lookup.  Assuming a sub class would suddenly set that
>> value to -13 the superclass contract would be violated.  Now, if you
>> let Complex inherit from Real (trying to avoid "irrational" :-)) you
>> would add another field for imaginary part.  So far so good, but
>> method to_f would sometimes throw an exception in Complex which it
>> would never do in Real.  So suddenly Complex breaks Real's contract.
>
> But that's a failure of implementation, isn't it?

That too, but the root cause lies in the area of the incompatibility
of the design with the properties of numerical classes.

> If I were to implement my own class Complex, I'd have to deal with the
> edge-cases that my sub-class has and can produce.
>
> Thus, I either undefine #to_f, or redefine it so that it throws an
> Exception. the value of inheritance is, after all, generalization, so
> that I don't have to reimplement the wheel all the time, instead
> making the wheel bigger or smaller, as the implementation requires.
> That conversely also means that that more specialized sub-classes
> derived from a generic-er super-class, *has* to implement an interface
> that works, and works consistently.
>
> To stay with Complex as an example:
> #to_f would require an additional argument to work properly: Either
> convert the real, or the imaginary part into a Float, and so would
> anything derived from Complex, whatever that may be, if it has the
> same properties.

But we were talking about the case of Complex inheriting Float.  Your
arguments do not make sense with a Float class because there is no
imaginary part yet you would need them in order to be able to provide
a meaningful to_f in the subclass Complex.

>> That's why I prefer to look at inheritance as "is a" relationship:
>> after all OO is about better abstraction capabilities and to be able
>> to hide implementation details behind a clearly defined clean
>> interface.  If you let yourself get dragged too much into technical
>> issues chances are that the design comes out awful.  Only languages
>> which allow to inherit without publishing all features of the
>> inherited class (private inheritance e.g. in Eiffel) do not
>> necessarily suffer from these issues.  But then, inheritance is just
>> an implementation detail in such cases.
>
> But isn't it always?

If you treat it as such it is.  However then you using a powerful
feature for abstraction and modeling.

> Regarding technical issues: Design is a bit of an art; knowing when to
> stop abstracting is important. ;)

In my experience far too many people in our profession have the other
problem: they dive into details too fast and do not think on an
abstract level.  That's the reason why so much code I get to see has
issues.  And since design flaws are generally much more costly to
repair than mere technical issues I'd rather say people should learn
to _start_ abstracting. :-)

Thank you for the interesting discussion!

Cheers

robert


PS: I just read that there was another earthquake in Japan and there
is a tsunami warning.  I hope the best for everybody in that area and
I hope these catastrophes end rather sooner than later.

-- 
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

[toc] | [prev] | [next] | [standalone]


#2466

FromPhillip Gawlowski <cmdjackryan@googlemail.com>
Date2011-04-07 10:31 -0500
Message-ID<BANLkTinrMwepEwJQP6-FT5XSQqVuGnBB9g@mail.gmail.com>
In reply to#2464
On Thu, Apr 7, 2011 at 5:11 PM, Robert Klemme
<shortcutter@googlemail.com> wrote:
>
> But we were talking about the case of Complex inheriting Float.  Your
> arguments do not make sense with a Float class because there is no
> imaginary part yet you would need them in order to be able to provide
> a meaningful to_f in the subclass Complex.

That's why Complex adds to #to_f, to enable the #to_f functionality
(or raise an error, when it just doesn't make sense to have a
function). It's method overloading.

> Thank you for the interesting discussion!

My pleasure. :)

We can probably go back and forth over the benefits of the approaches,
but it's largely academical now, I think. ;)
> PS: I just read that there was another earthquake in Japan and there
> is a tsunami warning.  I hope the best for everybody in that area and
> I hope these catastrophes end rather sooner than later.

Aye.

-- 
Phillip Gawlowski

Though the folk I have met,
(Ah, how soon!) they forget
When I've moved on to some other place,
There may be one or two,
When I've played and passed through,
Who'll remember my song or my face.

[toc] | [prev] | [next] | [standalone]


#2472

FromBrian Candler <b.candler@pobox.com>
Date2011-04-07 11:06 -0500
Message-ID<86e862423ebca60a6258afe41401d989@ruby-forum.com>
In reply to#2457
Phillip Gawlowski wrote in post #991471:
>> If you see inheritance as "is a" relationship then inheritance would
>> be Rational < Real < Complex but not the other way round. Otherwise
>> you cannot use a subclass instance everywhere you were using a
>> superclass instance.
>
> Though, does the "is a" relationship hold up? I think it's more of a
> "kind of" relationship, where subsequent classes are defined in ever
> more detail (so, you'd inherit Floats from Integers, and Complex from
> Float).

Isn't this the old "ellipse is_a circle, or vice versa" debate?

If you make Circle the top class, then Ellipse reimplements pretty much 
everything (draw, area, etc); there's no useful code sharing. If you 
make Ellipse the top class, then Circle is just a special constrained 
case of Ellipse.

Translating to the current discussion, substitute Float for Circle and 
Ellipse for Complex.

Ruby's answer is: neither is a subclass of the other. Both inherit from 
Numeric. That is, Circle and Ellipse are both a Shape. Or in other 
words, "who cares"?

Eventually you come to realise that a lot of what is taught in object 
oriented classes and textbooks is tosh :-)

-- 
Posted via http://www.ruby-forum.com/.

[toc] | [prev] | [next] | [standalone]


#2487

FromVincent Manis <vmanis@telus.net>
Date2011-04-07 17:42 -0500
Message-ID<163C6BA5-B6F9-4636-AB3E-93D46C71616C@telus.net>
In reply to#2472
On 2011-04-07, at 09:06, Brian Candler wrote:
> Eventually you come to realise that a lot of what is taught in object 
> oriented classes and textbooks is tosh :-)

And a lot of what is done by practitioners using object-oriented languages
is tosh as well, I know, I've seen it. (You haven't lived until you've had 
to review a C++ class with 9-way multiple inheritance, without using rude 
words!)

The Circle and Ellipse example is a good one. In fact, a Circle is no more 
than an Ellipse with a constraint (eccentricity = 0, or, equivalently, the 
two foci (`focuses') of the Ellipse are at the same point). So in almost 
all cases, I wouldn't have two separate classes, but one, Ellipse. 

In the case of Complex and Float, the operative design principle is the 
Liskov Substitution Principle, which can be roughly stated in OO form as
`you can derive class Sub from class Super if and only if every instance 
of Sub can be regarded as an instance of Super'. 

Thus it's perfectly reasonable to derive JetPlane from Airplane, because 
every JetPlane should be able to respond to all Airplane operations. 
However, you can't derive Airplane from Wheel, or Wheel from Airplane, 
even though there is some connection between wheels and planes. Like all 
design principles, there are exceptional cases where the LSP doesn't apply, 
but it seems to be the best heuristic for permissible subclassing.

In the Complex/Float case, the LSP tells us that we _could_ consider Float 
a subclass of Complex (because every Float is, as has been pointed out, a 
Complex with an imaginary part of zero), but that Complex can't reasonably 
be considered a subclass of Float. A good designer would go further and say
`yes, the LSP allows me to derive Float from Complex, but that's a waste of 
storage, because it means I must store imaginary parts that are always zero.'
Thus a better design (which Ruby follows) derives Complex from Numeric.

That's the OO theory, and it's not tosh :)

-- vincent manis

[toc] | [prev] | [next] | [standalone]


#2509

FromRobert Klemme <shortcutter@googlemail.com>
Date2011-04-08 01:55 -0500
Message-ID<BANLkTiktcr1JzmwJ9GaD_ghv1zhutrm2YQ@mail.gmail.com>
In reply to#2472
On Thu, Apr 7, 2011 at 6:06 PM, Brian Candler <b.candler@pobox.com> wrote:
> Phillip Gawlowski wrote in post #991471:
>>> If you see inheritance as "is a" relationship then inheritance would
>>> be Rational < Real < Complex but not the other way round. Otherwise
>>> you cannot use a subclass instance everywhere you were using a
>>> superclass instance.
>>
>> Though, does the "is a" relationship hold up? I think it's more of a
>> "kind of" relationship, where subsequent classes are defined in ever
>> more detail (so, you'd inherit Floats from Integers, and Complex from
>> Float).
>
> Isn't this the old "ellipse is_a circle, or vice versa" debate?
>
> If you make Circle the top class, then Ellipse reimplements pretty much
> everything (draw, area, etc); there's no useful code sharing. If you
> make Ellipse the top class, then Circle is just a special constrained
> case of Ellipse.
>
> Translating to the current discussion, substitute Float for Circle and
> Ellipse for Complex.
>
> Ruby's answer is: neither is a subclass of the other. Both inherit from
> Numeric. That is, Circle and Ellipse are both a Shape. Or in other
> words, "who cares"?

ACK, ACK and ACK.

> Eventually you come to realise that a lot of what is taught in object
> oriented classes and textbooks is tosh :-)

I'd rather say they are incomplete.  You need those simple examples to
explain what inheritance is for but often books stop right there.
Looking at inheritance on a very abstract level is worthwhile to start
musing about inheritance and how it can best be utilized.  But you
then need to progress discussing technical aspects etc. like we do
here.  But that is more difficult and complex and maybe some authors
are lazy, not aware of the complexity or do not delve into this for
other reasons.  I found Betrand Meyer's "Object Oriented Software
Construction" very comprehensive as he covers a lot aspects of
inheritance.  I would readily recommend it to anyone who wants to dive
a bit deeper into the matter.

Kind regards

robert

-- 
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

[toc] | [prev] | [next] | [standalone]


#2516

FromClifford Heath <no@spam.please.net>
Date2011-04-08 17:29 +1000
Message-ID<4d9eb95b$0$29364$afc38c87@news.optusnet.com.au>
In reply to#2438
On 04/07/11 19:19, Robert Klemme wrote:
> On Thu, Apr 7, 2011 at 6:05 AM, Clifford Heath<no@spam.please.net>  wrote:
>> I have a class which delegates for Integer, and wants to behave as much
>> like a real Integer as possible (except for being able to be subclassed).
> There's still a lot missing for a number replacement.  Please see
> http://blog.rubybestpractices.com/posts/rklemme/019-Complete_Numeric_Class.html

Yes, you wrote that about the time we discussed it last time.

> I also doubt whether it is a good idea to allow for subclassing of an
> integer like class.  What use case do you have in mind which would
> make this necessary?

What's wrong with the case you use in that blog post? But as it turns out,
I'm implementing a fact-based modeling DSL, where it's sensible to have
classes like "AgeInYears" being a subclass of an integer like class.
The formalism for this comes directly from sorted first-order logic,
which makes a good deal more sense than the broken O-O paradigm discussed
elsewhere in this thread.

I suspect that you "doubt it is a good idea" only because Ruby's object
model for numbers is inconsistent, and you're defensive about that. Not
because Ruby 2.0 shouldn't move in the direction of fixing it, where
possible. (BTW, I tried to join Ruby Core to discuss this, but all
possible means of subscription are silently failing me).

Note that I'm not actually subclassing any core integer class. I'm just
defining a new base class "Int" which contains an integer, and so far
as is possible, acts like one, including being found in a Hash using a
Fixnum/Bignum key.

If Fixnum and Bignum can act like Integer subclasses, why can't my class?

>> In particular, the Hash implementations work (and break!) differently in
>> MRI, Rubinius and JRuby. It's documented to use only #hash and #eql?,
>> but that's not always true (sometimes these have hard-wired optimsations).
> When you violate contracts you cannot expect code to work properly.

I have not violated that (unstated!) contract. Read again; I redefine
Fixnum#eql? as self.orig_eql?(i.to_i) - the to_i makes it symmetrical.
(Debate the wisdom if you wish, it's just for demonstration purposes.)

However the Ruby interpreters do not honor that. In short, *all three*
mentioned Ruby interpreters violate the Hash contract, which states that
hash and eql? are used for Hash lookups. Not just sometimes, but all the
time, including for integers.

MRI uses a Fixnum as its own hash value, even if you've monkey-patched a
hash method into Fixnum. This optimisation should not be always-on. Instead,
Ruby should detect when Fixnum has been patched, and bypass the optimisation.
That would require a single test and branch, with insignificant impact on
performance. MRI does however use a monkey-patched Fixnum#eql? method.

Rubinius does the opposite. It calls a patched Fixnum#hash, but not Fixnum#eql?

JRuby calls neither.

The Ruby interpreters should behave the way the Hash documentation says they do.

The Ruby documentation should explicitly state that eql? must be defined
symmetrically, or should require that the Hash implementation uses it only
in a known direction or both.

>> The Hash documentation does not say whether #eql? will be called only on
>> items in the hash, or only on keys being used to probe the hash. It should
>> be one or the other, since a.eql?(b) might not always mean b.eql?(a).
>
> But that is the contract as far as I can see.

That's not documented anywhere I can see. Certainly not in TRPL, see sections
3.4.2 on page 68, and section 3.8.5.3 page 77. It makes sense, but it's not
stated.

>  Having different
> results for both violates the equivalence relation which means all
> bets are off.

No. I can fix the asymmetry. I can't make the interpreters honor that fix.

>> You'll see that the behaviour is very unpredictable.
> Yes, because of your violation of the contract.

No. Because the Ruby interpreters don't honor the Hash contract.

Please try to read more carefully.

Clifford Heath.

[toc] | [prev] | [next] | [standalone]


#2521

FromRobert Klemme <shortcutter@googlemail.com>
Date2011-04-08 05:12 -0500
Message-ID<BANLkTi=EaS5m7EE+hfCjvkRsR_37bNZoXg@mail.gmail.com>
In reply to#2516
On Fri, Apr 8, 2011 at 9:30 AM, Clifford Heath <no@spam.please.net> wrote:
> On 04/07/11 19:19, Robert Klemme wrote:
>>
>> On Thu, Apr 7, 2011 at 6:05 AM, Clifford Heath<no@spam.please.net>  wrote:

>> I also doubt whether it is a good idea to allow for subclassing of an
>> integer like class.  What use case do you have in mind which would
>> make this necessary?
>
> What's wrong with the case you use in that blog post?

You mean, make HexNum a subclass of Integer?  Yes, actually that's
what I had attempted at the time but failed for technical reasons
(explained in the blog).  As it turns out it's generally not necessary
to inherit Integer in Ruby to create a class which behaves like an
integer (most of the time).

> But as it turns out,
> I'm implementing a fact-based modeling DSL, where it's sensible to have
> classes like "AgeInYears" being a subclass of an integer like class.
> The formalism for this comes directly from sorted first-order logic,
> which makes a good deal more sense than the broken O-O paradigm discussed
> elsewhere in this thread.
>
> I suspect that you "doubt it is a good idea" only because Ruby's object
> model for numbers is inconsistent, and you're defensive about that.

Where exactly do you see the inconsistency?  I can see that a few
things in that area do not match common expectations.  But I don't
think it's really inconsistent.

Your problem is not so much with numeric classes IMHO but rather with
implementations of class Hash in different versions of Ruby.  Namely
do they have issues treating instances from different class as
equivalent.

> Note that I'm not actually subclassing any core integer class. I'm just
> defining a new base class "Int" which contains an integer, and so far
> as is possible, acts like one, including being found in a Hash using a
> Fixnum/Bignum key.
>
> If Fixnum and Bignum can act like Integer subclasses, why can't my class?

Fixnum and Bignum do not share common values so you never have
instances of different classes representing the same numeric integer
value:

irb(main):003:0> (1<<100).class
=> Bignum
irb(main):004:0> (1<<100)>>99
=> 2
irb(main):005:0> ((1<<100)>>99).class
=> Fixnum

So that situation is a bit different.

>>> In particular, the Hash implementations work (and break!) differently in
>>> MRI, Rubinius and JRuby. It's documented to use only #hash and #eql?,
>>> but that's not always true (sometimes these have hard-wired
>>> optimsations).
>>
>> When you violate contracts you cannot expect code to work properly.
>
> I have not violated that (unstated!) contract. Read again; I redefine
> Fixnum#eql? as self.orig_eql?(i.to_i) - the to_i makes it symmetrical.
> (Debate the wisdom if you wish, it's just for demonstration purposes.)

Yes, you're right.  I probably mixed in a discussion about equals() in
Java needing to test for the same class (and not instanceof) to
achieve real equivalence.  At least we had a nice discussion about OO
and inheritance because of that. :-)

> However the Ruby interpreters do not honor that. In short, *all three*
> mentioned Ruby interpreters violate the Hash contract, which states that
> hash and eql? are used for Hash lookups. Not just sometimes, but all the
> time, including for integers.

Apparently there are optimizations done under the hood (similarly to
duping an unfrozen String as key) which is probably OK from a
pragmatic point of view (what you attempt seems rather seldom done).

> The Ruby interpreters should behave the way the Hash documentation says they
> do.

Well, they do - most of the time. :-)

> The Ruby documentation should explicitly state that eql? must be defined
> symmetrically, or should require that the Hash implementation uses it only
> in a known direction or both.

Right, there is certainly room for improvement.

>>> The Hash documentation does not say whether #eql? will be called only on
>>> items in the hash, or only on keys being used to probe the hash. It
>>> should
>>> be one or the other, since a.eql?(b) might not always mean b.eql?(a).
>>
>> But that is the contract as far as I can see.
>
> That's not documented anywhere I can see. Certainly not in TRPL, see
> sections
> 3.4.2 on page 68, and section 3.8.5.3 page 77. It makes sense, but it's not
> stated.

Right again.  Maybe the requirement can be inferred from other
properties but it would certainly make sense to stress it.

>>  Having different
>> results for both violates the equivalence relation which means all
>> bets are off.
>
> No. I can fix the asymmetry. I can't make the interpreters honor that fix

[toc] | [prev] | [next] | [standalone]


#2564

FromClifford Heath <no@spam.please.net>
Date2011-04-09 11:21 +1000
Message-ID<4d9fb493$0$2443$afc38c87@news.optusnet.com.au>
In reply to#2521
On 04/08/11 20:12, Robert Klemme wrote:
> On Fri, Apr 8, 2011 at 9:30 AM, Clifford Heath<no@spam.please.net>  wrote:
>> On 04/07/11 19:19, Robert Klemme wrote:
>>> On Thu, Apr 7, 2011 at 6:05 AM, Clifford Heath<no@spam.please.net>    wrote:
>>> I also doubt whether it is a good idea to allow for subclassing of an
>>> integer like class.  What use case do you have in mind which would
>>> make this necessary?
>> What's wrong with the case you use in that blog post?
> You mean, make HexNum a subclass of Integer?  Yes, actually that's
> what I had attempted at the time but failed for technical reasons

No, I don't mean making HexNum a subclass of Integer, but making it an
"integer like class" which can be subclassed.

> (explained in the blog).  As it turns out it's generally not necessary
> to inherit Integer in Ruby to create a class which behaves like an
> integer (most of the time).

Right. I'd like to see that work *more* of the time :). Or at least,
that each Ruby interpreter should fail in the same way.

>> I suspect that you "doubt it is a good idea" only because Ruby's object
>> model for numbers is inconsistent, and you're defensive about that.
> Where exactly do you see the inconsistency?  I can see that a few
> things in that area do not match common expectations.  But I don't
> think it's really inconsistent.

By inconsistent, I mean that Ruby doesn't make it possible to make
subclasses of Integer that play nicely with other Integers. Fixnum
and Bignum are mutually compatible and automatically and invisibly
convert back and forth, but it's not possible for an user's class to
do the same. That's inconsistent. A few more calls to coerce and some
more circumspect interpreter optimisations and it would all be pretty
ok.

Note that I expect there will still be a need for Java-style boxed
and unboxed integer values. C# makes the boxing even more transparent
than Java,  but Ruby doesn't even try.

> Your problem is not so much with numeric classes IMHO but rather with
> implementations of class Hash in different versions of Ruby.  Namely
> do they have issues treating instances from different class as
> equivalent.

Yes. It's documented to use #hash and #eql?, so that's what it should do.
If it also has invisible optimisations, fine. So long as they're invisible.

> Fixnum and Bignum do not share common values so you never have
> instances of different classes representing the same numeric integer
> value:

Yes. But I never need to know where the cut-over is, and it can be different
with different Ruby build targets. It's almost completely transparent.

> Apparently there are optimizations done under the hood (similarly to
> duping an unfrozen String as key)

Except that the case of String is documented, and works the same in all
interpreters.

> which is probably OK from a
> pragmatic point of view (what you attempt seems rather seldom done).

Mainly because it doesn't work :)

Clifford Heath.

[toc] | [prev] | [next] | [standalone]


#2604

FromCharles Oliver Nutter <headius@headius.com>
Date2011-04-10 19:02 -0500
Message-ID<BANLkTimx8Wf9AfVH0qPPruDWcJb-EususQ@mail.gmail.com>
In reply to#2564
Top-replying with a general observation: you can't please everyone all the time.

The special-cased logic for Fixnums and Symbols in hashes is obviously
done for performance purposes. No matter what you do, checking for
method redefinitions every single time will have a performance impact.
Even checking an inline cache has an impact. When you look at how
frequently hashes are used with Fixnum or Symbol keys, you'd basically
be asking everyone to take a perf hit to do it the "right way" for a
tiny minority of use cases.

There are also plenty of other cases in all the implementations where
modifying critical core classes does not get reflected during
execution. For example, some impls treat operator calls against Fixnum
as always being the Fixnum version, regardless of modifications. This
allows using a fast type-identity check rather than a cache check or
class-modification check, and it can make a *huge* difference for raw
numeric performance.

In this case I think there's a fine line between consistency and
zealotry. The *vast* majority of Ruby users will never reopen and
modify Fixnum or Symbol, so it's a 99%-safe assumption that "fast"
logic for those types is just fine, especially if it's a noticeable
perf boost for the 99% of users. We're talking about the lowest-level
values in the system...if they can't be made fast, everything else
suffers.

JRuby follows MRI largely because of the perf improvement, but also
partially because MRI does it this way. If MRI always dispatched, we'd
do what we need to do to always dispatch (and we do have other ways
internally to reduce -- but not eliminate -- the modification check).

A side note on JRuby's optimization strategy over the years:

1. We find a largely-invariant piece of logic that could be optimized,
like fixnum operators or hashes of symbols
2. We come up with an optimization that may diverge slightly from
"pure" behavior and add an opt-in flag for that optimization
3. Based on user reports, test runs, and so on, we may eventually turn
the optimization on all the time and make the flag be opt-out

We've been more conservative than other impls, even.

- Charlie

On Fri, Apr 8, 2011 at 8:25 PM, Clifford Heath <no@spam.please.net> wrote:
> On 04/08/11 20:12, Robert Klemme wrote:
>>
>> On Fri, Apr 8, 2011 at 9:30 AM, Clifford Heath<no@spam.please.net>  wrote:
>>>
>>> On 04/07/11 19:19, Robert Klemme wrote:
>>>>
>>>> On Thu, Apr 7, 2011 at 6:05 AM, Clifford Heath<no@spam.please.net>
>>>>  wrote:
>>>> I also doubt whether it is a good idea to allow for subclassing of an
>>>> integer like class.  What use case do you have in mind which would
>>>> make this necessary?
>>>
>>> What's wrong with the case you use in that blog post?
>>
>> You mean, make HexNum a subclass of Integer?  Yes, actually that's
>> what I had attempted at the time but failed for technical reasons
>
> No, I don't mean making HexNum a subclass of Integer, but making it an
> "integer like class" which can be subclassed.
>
>> (explained in the blog).  As it turns out it's generally not necessary
>> to inherit Integer in Ruby to create a class which behaves like an
>> integer (most of the time).
>
> Right. I'd like to see that work *more* of the time :). Or at least,
> that each Ruby interpreter should fail in the same way.
>
>>> I suspect that you "doubt it is a good idea" only because Ruby's object
>>> model for numbers is inconsistent, and you're defensive about that.
>>
>> Where exactly do you see the inconsistency?  I can see that a few
>> things in that area do not match common expectations.  But I don't
>> think it's really inconsistent.
>
> By inconsistent, I mean that Ruby doesn't make it possible to make
> subclasses of Integer that play nicely with other Integers. Fixnum
> and Bignum are mutually compatible and automatically and invisibly
> convert back and forth, but it's not possible for an user's class to
> do the same. That's inconsistent. A few more calls to coerce and some
> more circumspect interpreter optimisations and it would all be pretty
> ok.
>
> Note that I expect there will still be a need for Java-style boxed
> and unboxed integer values. C# makes the boxing even more transparent
> than Java,  but Ruby doesn't even try.
>
>> Your problem is not so much with numeric classes IMHO but rather with
>> implementations of class Hash in different versions of Ruby.  Namely
>> do they have issues treating instances from different class as
>> equivalent.
>
> Yes. It's documented to use #hash and #eql?, so that's what it should do.
> If it also has invisible optimisations, fine. So long as they're invisible.
>
>> Fixnum and Bignum do not share common values so you never have
>> instances of different classes representing the same numeric integer
>> value:
>
> Yes. But I never need to know where the cut-over is, and it can be different
> with different Ruby build targets. It's almost completely transparent.
>
>> Apparently there are optimizations done under the hood (similarly to
>> duping an unfrozen String as key)
>
> Except that the case of String is documented, and works the same in all
> interpreters.
>
>> which is probably OK from a
>> pragmatic point of view (what you attempt seems rather seldom done).
>
> Mainly because it doesn't work :)
>
> Clifford Heath.
>
>

[toc] | [prev] | [next] | [standalone]


#2609

FromClifford Heath <no@spam.please.net>
Date2011-04-11 13:17 +1000
Message-ID<4da272c3$0$2443$afc38c87@news.optusnet.com.au>
In reply to#2604
On 04/11/11 10:02, Charles Oliver Nutter wrote:
> The special-cased logic for Fixnums and Symbols in hashes is obviously
> done for performance purposes. No matter what you do, checking for
> method redefinitions every single time will have a performance impact.

Yes.

> Even checking an inline cache has an impact.

You are mixing up the situations where there is no sane
case for allowing modifications, from the many fewer
ones where there is. No sane person would want to change
the implementation of 1+1; but someone can and has
implemented Fixnum+Complex. That works because Fixnum
will always call coerce where needed, so there's no need
to guard the optimisations.

In the few remaining cases where there is a good case
for supporting modifications, the minuscule cost of
a check would be justified. A single variable (saying
"this class has been modified from its standard form")
would take up a cache line, but the test would play
into branch prediction, so the actual effect would be
tiny.

I know you guys have done amazing thing to achieve the
performance that we now have, but please don't forget
why people choose Ruby; it's clean and consistent.

Another thing that could be done to assist; make sure
that a Hash only ever calls eql? on objects in the hash,
not on lookup keys. At least that way, if a non-standard
object is in the hash, it can still be found using values
that *it* considers equivalent. This change would cost
*nothing*, it would just make Ruby more consistent.

> In this case I think there's a fine line between consistency and
> zealotry. The *vast* majority of Ruby users will never reopen and
> modify Fixnum or Symbol,

People don't do it because it doesn't work, not because
it wouldn't be useful. Inability to make classes that act
like numbers is perhaps the biggest wart on an otherwise
clean language, on a par with Javascript using float for
all numbers.

> JRuby follows MRI largely because of the perf improvement, but also
> partially because MRI does it this way.

But JRuby does it differently from MRI. Try the code in
the gist I previously sent, you'll see that's true.

> We've been more conservative than other impls, even.

and yet JRuby's Hash optimises both eql? and hash for Fixnums,
where MRI only optimises hash.

Clifford Heath.

[toc] | [prev] | [next] | [standalone]


#2673

FromRobert Klemme <shortcutter@googlemail.com>
Date2011-04-12 04:09 -0500
Message-ID<BANLkTinxoFG9O4GXxQWf+0rMiRHb8Xi7zg@mail.gmail.com>
In reply to#2609
On Mon, Apr 11, 2011 at 5:20 AM, Clifford Heath <no@spam.please.net> wrote:
> On 04/11/11 10:02, Charles Oliver Nutter wrote:
>>
>> The special-cased logic for Fixnums and Symbols in hashes is obviously
>> done for performance purposes. No matter what you do, checking for
>> method redefinitions every single time will have a performance impact.
>
> Yes.
>
>> Even checking an inline cache has an impact.
>
> You are mixing up the situations where there is no sane
> case for allowing modifications, from the many fewer
> ones where there is. No sane person would want to change
> the implementation of 1+1; but someone can and has
> implemented Fixnum+Complex. That works because Fixnum
> will always call coerce where needed, so there's no need
> to guard the optimisations.

I think Charly got it exactly right.

> In the few remaining cases where there is a good case
> for supporting modifications, the minuscule cost of
> a check would be justified. A single variable (saying
> "this class has been modified from its standard form")
> would take up a cache line, but the test would play
> into branch prediction, so the actual effect would be
> tiny.

Frankly, since what you are attempting seems a rather rare case I'd
say it should be the way it is.  After all, you can easily monkeypatch
Hash[] etc. to get the behavior you desire.  That way not 99% of
usages of Hash have to suffer for 1% needing to code less.  I think
that is a fair balance.

I don't understand why you insist on changing a core class for your
rare case of making different classes equivalent and causing potential
harm for many, many users of Ruby instead of just going ahead and also
monkey patch Hash since you did already so for Fixnum.  On one hand
you use Ruby's openness to change core classes to achieve what you
want but on the other you seem to refuse to change another to make
your change complete.  The only reason for this that I can detect is
that you were surprised and your expectations were not met.  But now
since you have learned otherwise what stops you from dealing with the
situation in the pragmatic way that is so typical for Ruby?

> I know you guys have done amazing thing to achieve the
> performance that we now have, but please don't forget
> why people choose Ruby; it's clean and consistent.

.. most of the time.  But it also tries to balance reasonable
usability with above than awful performance.

> Another thing that could be done to assist; make sure
> that a Hash only ever calls eql? on objects in the hash,
> not on lookup keys. At least that way, if a non-standard
> object is in the hash, it can still be found using values
> that *it* considers equivalent. This change would cost
> *nothing*, it would just make Ruby more consistent.

But it also would not have any positive impact for all others plus
that a change always brings a certain amount of risk of introducing
errors.  Note though, that there might be situations where you want
the exact opposite: you have a Fixnum key and pass a
SomethingFixnumLinke lookup key and want the match to succeed.  You
can only have it one way.  You happen to need the way that is not
possible right now.

Apart from that it feels more natural to me to let the key passed in
compare internal key for equivalence because this key is what should
determine whether I have a match or not.

>> In this case I think there's a fine line between consistency and
>> zealotry. The *vast* majority of Ruby users will never reopen and
>> modify Fixnum or Symbol,
>
> People don't do it because it doesn't work, not because
> it wouldn't be useful.

How do you know?

> Inability to make classes that act
> like numbers is perhaps the biggest wart on an otherwise
> clean language, on a par with Javascript using float for
> all numbers.

We *can* make classes that act like numbers (as has been demonstrated
often enough).  And this works remarkably well.

>> We've been more conservative than other impls, even.
>
> and yet JRuby's Hash optimises both eql? and hash for Fixnums,
> where MRI only optimises hash.

It is in the nature of optimizations that they are done differently on
different platforms.  Actually they have to because characteristics of
all platforms are different.

Cheers

robert

-- 
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

[toc] | [prev] | [next] | [standalone]


Page 1 of 3  [1] 2 3  Next page →

Back to top | Article view | comp.lang.ruby


csiph-web