Adding methods to Integers...

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|

Adding methods to Integers...

Patrick Stein-2

Background:
::::::::::::::::::::::

        The most recent MathFactor Podcast ( http://mathfactor.uark.edu/ )
        ended with a request to write a computer program that could, in
        principal, given enough time and memory, compute Graham's Number
        ( http://mathworld.wolfram.com/GrahamsNumber.html ).

        Smalltalk was a natural choice since it already supports LargeIntegers.

Problem:
:::::::::::::::

        Being new to Smalltalk, my first thought was that I should make my
        own class as a subclass of LargePositiveInteger, put my methods there,
        and violá.  Alas, no love.

        I ran into the following problems:
                * I couldn't find a way to give a value to myself.
                * I couldn't find a way to get integers to adapt to my class anyway.

        I ended up just adding my methods to the Integer class.  But, this felt
        very naughty.  Is it the usual approach?

Thanks,
Patrick



Bonus Questions:
:::::::::::::::::::::::::::::

        [ Actually, this section should be titled, "Bonus Questions: which  
it turns
        out that I managed to answer for myself, but have left here for  
pedagogic
        reasons". ]

        What I really want at the moment is to know if adding methods to the
        Kernel classes is considered legit.  But, I ran into a whole set of  
other
        questions along the way.  So, I wouldn't mind a little guidance on  
these
        either.

        The new: method:
        ::::::::::::::::::::::::::::::

        The withAtLeastNDigits method of LargePositiveInteger does this
        (variable names changed for clarity):

                nn := self class new: nDigits.

        I cannot find the "new:" method in the class browser under Class,
        nor can I find any hint of it somehow being part of  
LargePositiveInteger,
        Integer, Number, Magnitude, Object, or ProtoObject.  Actually, I just
        answered this one myself.... the Class class's hierarchy is
        Class < ClassDescription < Behavior < Object.  The "new:" is in  
Behavior.

        Initialization methods?:
        :::::::::::::::::::::::::::::::::::::::

        The only way that I can understand the "i" and "hours" methods of the
        Number class are if I somehow think the Complex and Duration classes
        have class methods in addition to their instance methods.  For example,
        here is the "i" method of the Number class.

                i
                        ^ Complex real: 0 imaginary: self

        That seems straightforward enough.  Given a number, say "5" and pass
        it the "i" message.  It will return a complex number with 0 for the  
real
        and 5 for the imaginary.  Except, I can't where the "real: imaginary:"
        method is defined with the class browser.

        Blah, and I just answered this one, too.  I hadn't twigged on the  
"class"
        button at the bottom of the class-list in the browser.  Now, I see._______________________________________________
Beginners mailing list
[hidden email]
http://lists.squeakfoundation.org/mailman/listinfo/beginners
Reply | Threaded
Open this post in threaded view
|

Re: Adding methods to Integers...

Ralph Johnson
It is fine to add methods to kernel classes like LargePositiveInteger.
 Just make sure you use new names, because you don't want to override
an inherited method by accident.

There are several problems with adding methods to existing classes.
One is when you override a method by accident.  To prevent this, pick
unique names for methods.  Another is when someone changes the class,
breaking your code.  you can run into this problem when you subclass a
kernel class, too.  It is not likely to happen in the near future, but
if you wait long enough, the class is likely to change.  Finally,
someone else might want to change the same class, and your changes
will conflict.

If you are writing code that you expect will last a long time and that
will be reused by many people, avoid changing kernel classes as much
as possible.  if you are writing a quick hack to see if you can do
something, do whatever seems easiest.

One of the big advantages of Smalltalk is that you can change
anything.  Changing system classes has the potential to increase the
cost of maintenance, but it definitely can reduce the cost of making
your system in the first place.

-Ralph Johnson
_______________________________________________
Beginners mailing list
[hidden email]
http://lists.squeakfoundation.org/mailman/listinfo/beginners
Reply | Threaded
Open this post in threaded view
|

Re: Adding methods to Integers...

Klaus D. Witzel
In reply to this post by Patrick Stein-2
Hi Patrick,

some of the symptoms you describe have to do with a small set of classes  
being "hardwired" in Squeak's virtual machine. To see which they are,  
evaluate (printIt)

  Smalltalk specialObjectsArray select: [:each | each isBehavior]

So when you do primitive arithmethic with your own subclass of  
LargePositiveInteger, the VM returns an instance of LargePositiveInteger  
(and not your subinstance of it).

Of course the specialObjectsArray can be changed and from then on the VM  
(after being notified) will use your subclass but, I think this is not  
what you really want ;-)

Putting your methods into Integer is fine as long as they do not conflict  
with anything else. Yes, this is the usual approach for adding new  
behavior to all the integers :)

/Klaus

On Thu, 12 Apr 2007 17:28:01 +0200, you wrote:

>
> Background:
> ::::::::::::::::::::::
>
> The most recent MathFactor Podcast ( http://mathfactor.uark.edu/ )
> ended with a request to write a computer program that could, in
> principal, given enough time and memory, compute Graham's Number
> ( http://mathworld.wolfram.com/GrahamsNumber.html ).
>
> Smalltalk was a natural choice since it already supports LargeIntegers.
>
> Problem:
> :::::::::::::::
>
> Being new to Smalltalk, my first thought was that I should make my
> own class as a subclass of LargePositiveInteger, put my methods there,
> and violá.  Alas, no love.
>
> I ran into the following problems:
> * I couldn't find a way to give a value to myself.
> * I couldn't find a way to get integers to adapt to my class anyway.
>
> I ended up just adding my methods to the Integer class.  But, this felt
> very naughty.  Is it the usual approach?
>
> Thanks,
> Patrick

_______________________________________________
Beginners mailing list
[hidden email]
http://lists.squeakfoundation.org/mailman/listinfo/beginners
Reply | Threaded
Open this post in threaded view
|

Re: Re: Adding methods to Integers...

Bert Freudenberg
This is actually wrong. Only SmallIntegers are special [*].

What happens is this: When you add two SmallIntegers (like "3 + 4"),  
and the result is a SmallInteger, the result is calculated in the  
bytecode directly. Otherwise, a regular send of #+ is performed. From  
there, everything else happens in the image, including conversion to  
LargeIntegers - see implementors of #+. Like, if the receiver was a  
SmallInteger, it tries the "add" primitive, which fails, and then the  
implementation of #+ in class Integer is invoked. This method then  
creates a large integer.

For the implementation of all this see #bytecodePrimAdd and  
#primitiveAdd. You may have to load VMMaker first.

- Bert -

[*] Well, Floats are optimized a bit, and we have a plugin to speed  
up LargeIntegers, but this is all optional and  doesn't matter in  
this discussion.

On Apr 12, 2007, at 18:05 , Klaus D. Witzel wrote:

> Hi Patrick,
>
> some of the symptoms you describe have to do with a small set of  
> classes being "hardwired" in Squeak's virtual machine. To see which  
> they are, evaluate (printIt)
>
>  Smalltalk specialObjectsArray select: [:each | each isBehavior]
>
> So when you do primitive arithmethic with your own subclass of  
> LargePositiveInteger, the VM returns an instance of  
> LargePositiveInteger (and not your subinstance of it).
>
> Of course the specialObjectsArray can be changed and from then on  
> the VM (after being notified) will use your subclass but, I think  
> this is not what you really want ;-)
>
> Putting your methods into Integer is fine as long as they do not  
> conflict with anything else. Yes, this is the usual approach for  
> adding new behavior to all the integers :)
>
> /Klaus
>
> On Thu, 12 Apr 2007 17:28:01 +0200, you wrote:
>
>>
>> Background:
>> ::::::::::::::::::::::
>>
>> The most recent MathFactor Podcast ( http://mathfactor.uark.edu/ )
>> ended with a request to write a computer program that could, in
>> principal, given enough time and memory, compute Graham's Number
>> ( http://mathworld.wolfram.com/GrahamsNumber.html ).
>>
>> Smalltalk was a natural choice since it already supports  
>> LargeIntegers.
>>
>> Problem:
>> :::::::::::::::
>>
>> Being new to Smalltalk, my first thought was that I should make my
>> own class as a subclass of LargePositiveInteger, put my methods  
>> there,
>> and violá.  Alas, no love.
>>
>> I ran into the following problems:
>> * I couldn't find a way to give a value to myself.
>> * I couldn't find a way to get integers to adapt to my class  
>> anyway.
>>
>> I ended up just adding my methods to the Integer class.  But,  
>> this felt
>> very naughty.  Is it the usual approach?
>>
>> Thanks,
>> Patrick
>
> _______________________________________________
> Beginners mailing list
> [hidden email]
> http://lists.squeakfoundation.org/mailman/listinfo/beginners




_______________________________________________
Beginners mailing list
[hidden email]
http://lists.squeakfoundation.org/mailman/listinfo/beginners
Reply | Threaded
Open this post in threaded view
|

Re: Adding methods to Integers...

Klaus D. Witzel
Hi Bert,

on Thu, 12 Apr 2007 18:31:55 +0200, you wrote:

> This is actually wrong.

:)

> Only SmallIntegers are special [*].
>
> What happens is this: When you add two SmallIntegers (like "3 + 4"), and  
> the result is a SmallInteger, the result is calculated in the bytecode  
> directly. Otherwise, a regular send of #+ is performed.

Right.

> From there, everything else happens in the image,

Not really. (Integer>>#+ aNumber) sends (self digitAdd: aNumber) which is  
implemented as <primitive: 'primDigitAdd' module: 'LargeIntegers'> which  
is part of the VM. Of course the LargeIntegers module may be absent, have  
failed to load, may not like the argument, etc.

Only if (LargeIntegers>>#primDigitAdd: secondInteger) fails [whatever the  
reason, must not have anything to do with arithmetics] then "everything  
else" may happen in the image.

Back to the situation I had in mind (assuming MyPositiveInteger is a  
subclass of LargePositiveInteger):

| x y |
  x := LargePositiveInteger initializedInstance.
  y := (MyPositiveInteger basicNew: x basicSize)
   replaceFrom: 1 to: x basicSize with: x startingAt: 1.
(1 + y) class
  =>
LargePositiveInteger

But not: (y + 1) class
  =>
MyPositiveInteger

See, associativity at work ;-)

I have not checked whether the LargeIntegers module really does not like  
the argument (would seem plausible but it looks like it all relies on the  
#isInteger check in Integer>>#+, since from there on it "normally" is a  
SmallInteger or a #variableByteSubclass:).

> including conversion to LargeIntegers - see implementors of #+. Like, if  
> the receiver was a SmallInteger, it tries the "add" primitive, which  
> fails, and then the implementation of #+ in class Integer is invoked.  
> This method then creates a large integer.

It looks like in the (1 + y) case the LargeIntegers module creates the  
large integer. A simple test would be, to subclass ByteArray and have that  
respond true for #isInteger when used as argument in Integer>>#+ (but I  
haven't tried that).

> For the implementation of all this see #bytecodePrimAdd and  
> #primitiveAdd. You may have to load VMMaker first.

Sure, almost always have loaded that ;-)

/Klaus

> - Bert -
>
> [*] Well, Floats are optimized a bit, and we have a plugin to speed up  
> LargeIntegers, but this is all optional and  doesn't matter in this  
> discussion.
>
> On Apr 12, 2007, at 18:05 , Klaus D. Witzel wrote:
>
>> Hi Patrick,
>>
>> some of the symptoms you describe have to do with a small set of  
>> classes being "hardwired" in Squeak's virtual machine. To see which  
>> they are, evaluate (printIt)
>>
>>  Smalltalk specialObjectsArray select: [:each | each isBehavior]
>>
>> So when you do primitive arithmethic with your own subclass of  
>> LargePositiveInteger, the VM returns an instance of  
>> LargePositiveInteger (and not your subinstance of it).
>>
>> Of course the specialObjectsArray can be changed and from then on the  
>> VM (after being notified) will use your subclass but, I think this is  
>> not what you really want ;-)
>>
>> Putting your methods into Integer is fine as long as they do not  
>> conflict with anything else. Yes, this is the usual approach for adding  
>> new behavior to all the integers :)
>>
>> /Klaus
>>
>> On Thu, 12 Apr 2007 17:28:01 +0200, you wrote:
>>
>>>
>>> Background:
>>> ::::::::::::::::::::::
>>>
>>> The most recent MathFactor Podcast ( http://mathfactor.uark.edu/ )
>>> ended with a request to write a computer program that could, in
>>> principal, given enough time and memory, compute Graham's Number
>>> ( http://mathworld.wolfram.com/GrahamsNumber.html ).
>>>
>>> Smalltalk was a natural choice since it already supports  
>>> LargeIntegers.
>>>
>>> Problem:
>>> :::::::::::::::
>>>
>>> Being new to Smalltalk, my first thought was that I should make my
>>> own class as a subclass of LargePositiveInteger, put my methods there,
>>> and violá.  Alas, no love.
>>>
>>> I ran into the following problems:
>>> * I couldn't find a way to give a value to myself.
>>> * I couldn't find a way to get integers to adapt to my class anyway.
>>>
>>> I ended up just adding my methods to the Integer class.  But, this  
>>> felt
>>> very naughty.  Is it the usual approach?
>>>
>>> Thanks,
>>> Patrick
>>
>> _______________________________________________
>> Beginners mailing list
>> [hidden email]
>> http://lists.squeakfoundation.org/mailman/listinfo/beginners


_______________________________________________
Beginners mailing list
[hidden email]
http://lists.squeakfoundation.org/mailman/listinfo/beginners
Reply | Threaded
Open this post in threaded view
|

Re: Re: Adding methods to Integers...

Bert Freudenberg

On Apr 13, 2007, at 13:27 , Klaus D. Witzel wrote:

> Hi Bert,
>
> on Thu, 12 Apr 2007 18:31:55 +0200, you wrote:
>
>> This is actually wrong.
>
> :)
>
>> Only SmallIntegers are special [*].
>>
>> What happens is this: When you add two SmallIntegers (like "3 +  
>> 4"), and the result is a SmallInteger, the result is calculated in  
>> the bytecode directly. Otherwise, a regular send of #+ is performed.
>
> Right.
>
>> From there, everything else happens in the image,
>
> Not really. (Integer>>#+ aNumber) sends (self digitAdd: aNumber)  
> which is implemented as <primitive: 'primDigitAdd' module:  
> 'LargeIntegers'> which is part of the VM. Of course the  
> LargeIntegers module may be absent, have failed to load, may not  
> like the argument, etc.

No, this is an *optional* primitive. It's there purely to speed up  
computation and can be safely removed.

You could change the Integer classes to handle subclasses properly  
(via species etc.). So far, this has not been necessary. My point was  
that this (unlike SmallIntegers) is *not* hard-coded in the VM.

- Bert -




_______________________________________________
Beginners mailing list
[hidden email]
http://lists.squeakfoundation.org/mailman/listinfo/beginners
Reply | Threaded
Open this post in threaded view
|

Re: Adding methods to Integers...

Klaus D. Witzel
On Fri, 13 Apr 2007 13:39:08 +0200, Bert Freudenberg wrote:

> On Apr 13, 2007, at 13:27 , Klaus D. Witzel wrote:
>
>> Hi Bert,
>>
>> on Thu, 12 Apr 2007 18:31:55 +0200, you wrote:
>>
>>> This is actually wrong.
>>
>> :)
>>
>>> Only SmallIntegers are special [*].
>>>
>>> What happens is this: When you add two SmallIntegers (like "3 + 4"),  
>>> and the result is a SmallInteger, the result is calculated in the  
>>> bytecode directly. Otherwise, a regular send of #+ is performed.
>>
>> Right.
>>
>>> From there, everything else happens in the image,
>>
>> Not really. (Integer>>#+ aNumber) sends (self digitAdd: aNumber) which  
>> is implemented as <primitive: 'primDigitAdd' module: 'LargeIntegers'>  
>> which is part of the VM. Of course the LargeIntegers module may be  
>> absent, have failed to load, may not like the argument, etc.
>
> No, this is an *optional* primitive. It's there purely to speed up  
> computation and can be safely removed.
Sure, but this thread is not about what happens when the optional  
primitive is removed, and I was not reflecting that, either. I was rather  
discussing the point when the primitive does *not* fail on one of  
Patrick's subclasses and, what the returned species is.

> You could change the Integer classes to handle subclasses properly (via  
> species etc.). So far, this has not been necessary. My point was that  
> this (unlike SmallIntegers) is *not* hard-coded in the VM.

I think it depends on operand order. When the LargeIntegers module is  
invoked (in my previous example when sending + myLargeInteger to  
SmallInteger 1) then a "hard-coded" instance of LargePositiveIntegers is  
returned.

The attached class can be used (together with my previous example) to  
illustrate my point. The class does not inherit from some default large  
integer class and the primitive does not fail, regardless of operand  
order. I hope I didn't base my argument on a bug or mistake.

FWIW I do not disagree with anything about your argument, except a bit  
with "hard-coded".

> - Bert -


_______________________________________________
Beginners mailing list
[hidden email]
http://lists.squeakfoundation.org/mailman/listinfo/beginners

MyPositiveInteger.st (1K) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: Re: Adding methods to Integers...

Bert Freudenberg

On Apr 13, 2007, at 14:35 , Klaus D. Witzel wrote:

> On Fri, 13 Apr 2007 13:39:08 +0200, Bert Freudenberg wrote:
>
>> On Apr 13, 2007, at 13:27 , Klaus D. Witzel wrote:
>>
>>> Hi Bert,
>>>
>>> on Thu, 12 Apr 2007 18:31:55 +0200, you wrote:
>>>
>>>> This is actually wrong.
>>>
>>> :)
>>>
>>>> Only SmallIntegers are special [*].
>>>>
>>>> What happens is this: When you add two SmallIntegers (like "3 +  
>>>> 4"), and the result is a SmallInteger, the result is calculated  
>>>> in the bytecode directly. Otherwise, a regular send of #+ is  
>>>> performed.
>>>
>>> Right.
>>>
>>>> From there, everything else happens in the image,
>>>
>>> Not really. (Integer>>#+ aNumber) sends (self digitAdd: aNumber)  
>>> which is implemented as <primitive: 'primDigitAdd' module:  
>>> 'LargeIntegers'> which is part of the VM. Of course the  
>>> LargeIntegers module may be absent, have failed to load, may not  
>>> like the argument, etc.
>>
>> No, this is an *optional* primitive. It's there purely to speed up  
>> computation and can be safely removed.
>
> Sure, but this thread is not about what happens when the optional  
> primitive is removed, and I was not reflecting that, either. I was  
> rather discussing the point when the primitive does *not* fail on  
> one of Patrick's subclasses and, what the returned species is.
>
>> You could change the Integer classes to handle subclasses properly  
>> (via species etc.). So far, this has not been necessary. My point  
>> was that this (unlike SmallIntegers) is *not* hard-coded in the VM.
>
> I think it depends on operand order. When the LargeIntegers module  
> is invoked (in my previous example when sending + myLargeInteger to  
> SmallInteger 1) then a "hard-coded" instance of  
> LargePositiveIntegers is returned.
>
> The attached class can be used (together with my previous example)  
> to illustrate my point. The class does not inherit from some  
> default large integer class and the primitive does not fail,  
> regardless of operand order. I hope I didn't base my argument on a  
> bug or mistake.

I don't see your point. Do you want the primitive to fail for your  
class? This is outside of its spec, so to say. Do not use the  
primitive if you are not satisfied with its behavior.

> FWIW I do not disagree with anything about your argument, except a  
> bit with "hard-coded".

It is not hard-coded in the VM, exactly as I wrote above.  
SmallInteger arithmetic is hard-coded: There is no way to generally  
redefine SmallInteger>>+ unless you modify the bytecode interpreter,  
because method is never actually activated unless you use #perform:,  
or the result is not a SmallInteger.

But you are free to redefine what happens outside the realm of  
SmallIntegers. Invokation of the LargeInteger primitives is  
voluntary, you are not forced to use them.

- Bert -


_______________________________________________
Beginners mailing list
[hidden email]
http://lists.squeakfoundation.org/mailman/listinfo/beginners