Hmmm sending at:put: to an undefined object (nil) gives a confusing error message

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

Hmmm sending at:put: to an undefined object (nil) gives a confusing error message

Tim Mackinnon

If you forget to initialise a variable which you thought was a Dictionary - you get a confusing error message: "Error: only integers should be used as indices” if you try an at:put:

This is a consequence of having at:put: defined on Object (which is a bit nasty)

Should UndefinedObject at least override this and signal something a bit more obvious? This is a common and easily done thing and we don't support it very well.

I just did it in some code, and was scratching my head initially until I read the stack properly and realised it was something much simpler than I thought I had done.

Any thoughts on this?
Reply | Threaded
Open this post in threaded view
|

Re: Hmmm sending at:put: to an undefined object (nil) gives a confusing error message

Richard O'Keefe
I understand #basicAt:[put:] being in Object, but I never understood #at:[put:] being there.
GNU Smalltalk:
st> nil at: 1
Object: nil error: Invalid value nil: object not indexable
In my Smalltalk, you get a DNU.
In Squeak you get a debugger window with title
Error: instances of UndefinedObject are not indexable
The same for Boolean, Character, and Integer,
except Squeak where (20 factorial at: 1) answers 0 (oh dear oh dear oh dear).
In VW you get a "Subscript out of bounds" error, which is disappointing,
as do true and $a, but a LargePositiveinteger gets #shouldNotImplement.


On Tue, 12 Mar 2019 at 15:28, Tim Mackinnon <[hidden email]> wrote:

If you forget to initialise a variable which you thought was a Dictionary - you get a confusing error message: "Error: only integers should be used as indices” if you try an at:put:

This is a consequence of having at:put: defined on Object (which is a bit nasty)

Should UndefinedObject at least override this and signal something a bit more obvious? This is a common and easily done thing and we don't support it very well.

I just did it in some code, and was scratching my head initially until I read the stack properly and realised it was something much simpler than I thought I had done.

Any thoughts on this?
Reply | Threaded
Open this post in threaded view
|

Re: Hmmm sending at:put: to an undefined object (nil) gives a confusing error message

Marcus Denker-4
Hi,

Yes, I do not like it… I think it is there so that you can just make a variable subclass and at: / at:put: will work without
having to re-implement them…

Marcus

On 12 Mar 2019, at 04:55, Richard O'Keefe <[hidden email]> wrote:

I understand #basicAt:[put:] being in Object, but I never understood #at:[put:] being there.
GNU Smalltalk:
st> nil at: 1
Object: nil error: Invalid value nil: object not indexable
In my Smalltalk, you get a DNU.
In Squeak you get a debugger window with title
Error: instances of UndefinedObject are not indexable
The same for Boolean, Character, and Integer,
except Squeak where (20 factorial at: 1) answers 0 (oh dear oh dear oh dear).
In VW you get a "Subscript out of bounds" error, which is disappointing,
as do true and $a, but a LargePositiveinteger gets #shouldNotImplement.


On Tue, 12 Mar 2019 at 15:28, Tim Mackinnon <[hidden email]> wrote:

If you forget to initialise a variable which you thought was a Dictionary - you get a confusing error message: "Error: only integers should be used as indices” if you try an at:put:

This is a consequence of having at:put: defined on Object (which is a bit nasty)

Should UndefinedObject at least override this and signal something a bit more obvious? This is a common and easily done thing and we don't support it very well.

I just did it in some code, and was scratching my head initially until I read the stack properly and realised it was something much simpler than I thought I had done.

Any thoughts on this?

Reply | Threaded
Open this post in threaded view
|

Re: Hmmm sending at:put: to an undefined object (nil) gives a confusing error message

Ben Coman
On Tue, 12 Mar 2019 at 21:56, Marcus Denker <[hidden email]> wrote:
>
> Hi,
>
> Yes, I do not like it… I think it is there so that you can just make a variable subclass and at: / at:put: will work without
> having to re-implement them…

So we should consider...
* how often are variable subclasses created ?  by novices or experts ?
* how much work is it to reimplement those methods ? how difficult to
know what needs to be done ?
* what is the balance

cheers -ben


>
> Marcus
>
> On 12 Mar 2019, at 04:55, Richard O'Keefe <[hidden email]> wrote:
>
> I understand #basicAt:[put:] being in Object, but I never understood #at:[put:] being there.
> GNU Smalltalk:
> st> nil at: 1
> Object: nil error: Invalid value nil: object not indexable
> In my Smalltalk, you get a DNU.
> In Squeak you get a debugger window with title
> Error: instances of UndefinedObject are not indexable
> The same for Boolean, Character, and Integer,
> except Squeak where (20 factorial at: 1) answers 0 (oh dear oh dear oh dear).
> In VW you get a "Subscript out of bounds" error, which is disappointing,
> as do true and $a, but a LargePositiveinteger gets #shouldNotImplement.
>
>
> On Tue, 12 Mar 2019 at 15:28, Tim Mackinnon <[hidden email]> wrote:
>>
>> If you forget to initialise a variable which you thought was a Dictionary - you get a confusing error message: "Error: only integers should be used as indices” if you try an at:put:
>>
>> This is a consequence of having at:put: defined on Object (which is a bit nasty)
>>
>> Should UndefinedObject at least override this and signal something a bit more obvious? This is a common and easily done thing and we don't support it very well.
>>
>> I just did it in some code, and was scratching my head initially until I read the stack properly and realised it was something much simpler than I thought I had done.
>>
>> Any thoughts on this?
>
>

Reply | Threaded
Open this post in threaded view
|

Re: Hmmm sending at:put: to an undefined object (nil) gives a confusing error message

Richard O'Keefe
Putting public methods in Object that it cannot honestly support makes
#respondsTo: pretty unreliable.  It is an obsolete practice, because
having Traits means that those methods can be mixed in with (part of)
a single line.

On Wed, 13 Mar 2019 at 03:20, Ben Coman <[hidden email]> wrote:
On Tue, 12 Mar 2019 at 21:56, Marcus Denker <[hidden email]> wrote:
>
> Hi,
>
> Yes, I do not like it… I think it is there so that you can just make a variable subclass and at: / at:put: will work without
> having to re-implement them…

So we should consider...
* how often are variable subclasses created ?  by novices or experts ?
* how much work is it to reimplement those methods ? how difficult to
know what needs to be done ?
* what is the balance

cheers -ben


>
> Marcus
>
> On 12 Mar 2019, at 04:55, Richard O'Keefe <[hidden email]> wrote:
>
> I understand #basicAt:[put:] being in Object, but I never understood #at:[put:] being there.
> GNU Smalltalk:
> st> nil at: 1
> Object: nil error: Invalid value nil: object not indexable
> In my Smalltalk, you get a DNU.
> In Squeak you get a debugger window with title
> Error: instances of UndefinedObject are not indexable
> The same for Boolean, Character, and Integer,
> except Squeak where (20 factorial at: 1) answers 0 (oh dear oh dear oh dear).
> In VW you get a "Subscript out of bounds" error, which is disappointing,
> as do true and $a, but a LargePositiveinteger gets #shouldNotImplement.
>
>
> On Tue, 12 Mar 2019 at 15:28, Tim Mackinnon <[hidden email]> wrote:
>>
>> If you forget to initialise a variable which you thought was a Dictionary - you get a confusing error message: "Error: only integers should be used as indices” if you try an at:put:
>>
>> This is a consequence of having at:put: defined on Object (which is a bit nasty)
>>
>> Should UndefinedObject at least override this and signal something a bit more obvious? This is a common and easily done thing and we don't support it very well.
>>
>> I just did it in some code, and was scratching my head initially until I read the stack properly and realised it was something much simpler than I thought I had done.
>>
>> Any thoughts on this?
>
>

Reply | Threaded
Open this post in threaded view
|

Re: Hmmm sending at:put: to an undefined object (nil) gives a confusing error message

K K Subbu
In reply to this post by Richard O'Keefe
On 12/03/19 9:25 AM, Richard O'Keefe wrote:
> Squeak where (20 factorial at: 1) answers 0 (oh dear oh dear oh
> dear).

Richard,

Could you please elaborate on why this is an error?

Large integers are place value encoded (base 256 little endian) and
stored in byte arrays, so they need #at:/#at:put: (as private methods).
Place value encoding is not architecture-specific so it doesn't affect
portability of an image.

Regards .. Subbu

Reply | Threaded
Open this post in threaded view
|

Re: Hmmm sending at:put: to an undefined object (nil) gives a confusing error message

Richard O'Keefe
Let's start with portability.
I have ST/X, VAST, VW, Squeak, Pharo, GST,
Dolphin, and some minor systems.
GST and ST/X do not define #at: on any kind of
integer at all.  Why would they?  #basicAt:
will do the job, will it not?
Dolphin defines #at:[put:] on Integer as
self shouldNotImplement
and so does VW.

In VAST,
20 at: 1 => primitive failed
20 factorial at: 1 => 2192834560

In Squeak and Pharo,
20 at: 1 => not indexable
20 factorial at: 1 => 0

So sending #at: to an integer is not portable
between mainstream Smalltalk systems, and for
those Smalltalks that define it on large
integers, the value is not consistent on the
same machine and operating system.


But it is worse than that.  Maybe you do not
care about any Smalltalk but Pharo, and there
is a case to be made for that.  But this
is not consistent with *itself*.
1000000000000 at: 1
fails in a 64-bit VM,
where that is a SmallInteger, but
answers 0 in a 32-bit VM,
where that is a LargePositiveInteger.

But it gets *worse*.  Numbers are not
supposed to be mutable.  But in a 32-bit
VM,
  x := 1000000000000.
  x at: 1 put: 77.
  x
=> 1000000000077.

I'm sorry, but I had enough grief with
constants that weren't in Fortran 66.

Again, there isn't the slightest need to
define #at:put: on Large...Integers because
#basicAt:put: exists. Indeed, we have
#{basicAt:,at:,digitAt:}[put:] and surely
with #digitAt:[put:] around we can let
#at:[put:] be unimplemented on integers?

There's a conceptual problem with #at: and
#digitAt: as well.  An integer can be viewed
as a sequence of bits, right enough, but in
a language without silly machine-word limits
on integers, it's an *infinite* sequence.
At least, if you define logical operations on
bignums using a 2s-complement model, a
LargePositiveInteger extends infinitely far
with 0s and a LargeNegativeInteger extends
infinitely far with 1s. GST gets this right.
VW gets it right for large positive integers
but wrong for large negative ones (or at
least works on the absolute value without
bothering to explain that).

Ah heck, the whole thing is a mess.





On Wed, 13 Mar 2019 at 17:18, K K Subbu <[hidden email]> wrote:
On 12/03/19 9:25 AM, Richard O'Keefe wrote:
> Squeak where (20 factorial at: 1) answers 0 (oh dear oh dear oh
> dear).

Richard,

Could you please elaborate on why this is an error?

Large integers are place value encoded (base 256 little endian) and
stored in byte arrays, so they need #at:/#at:put: (as private methods).
Place value encoding is not architecture-specific so it doesn't affect
portability of an image.

Regards .. Subbu

Reply | Threaded
Open this post in threaded view
|

Re: Hmmm sending at:put: to an undefined object (nil) gives a confusing error message

Richard O'Keefe
In reply to this post by K K Subbu
Base 256: that's an implementation detail.
Little-endian: that's an implementation detail.
My Smalltalk uses base 65536 native-endian and
takes some care not to let Smalltalk code find out.
(Not least because on 64-bit machines I want to
use base 2**32.)
For *private* methods, depending on otherwise
hidden implementation details is fair enough.
My Smalltalk picked up a convention from Squeak
-- or rather, from the Squeak mailing list --
that a 'pvt' prefix on a selector makes it truly
private and *enforces* that.  Pharo does not seem
to have anything similar, and #at: is the epitome
of a selector in extremely wide general use.
Since #digitAt: exists -- and is what the integer
classes use -- it is practically certain that #at:
is sent to an integer only by mistake.


On Wed, 13 Mar 2019 at 17:18, K K Subbu <[hidden email]> wrote:
On 12/03/19 9:25 AM, Richard O'Keefe wrote:
> Squeak where (20 factorial at: 1) answers 0 (oh dear oh dear oh
> dear).

Richard,

Could you please elaborate on why this is an error?

Large integers are place value encoded (base 256 little endian) and
stored in byte arrays, so they need #at:/#at:put: (as private methods).
Place value encoding is not architecture-specific so it doesn't affect
portability of an image.

Regards .. Subbu

Reply | Threaded
Open this post in threaded view
|

Re: Hmmm sending at:put: to an undefined object (nil) gives a confusing error message

Sven Van Caekenberghe-2


> On 13 Mar 2019, at 08:15, Richard O'Keefe <[hidden email]> wrote:
>
> My Smalltalk

Where can I have a look at that ?

Reply | Threaded
Open this post in threaded view
|

Re: Hmmm sending at:put: to an undefined object (nil) gives a confusing error message

Richard O'Keefe
It's supposed to be on GitHub but I botched it.


On Wed, 13 Mar 2019 at 21:08, Sven Van Caekenberghe <[hidden email]> wrote:


> On 13 Mar 2019, at 08:15, Richard O'Keefe <[hidden email]> wrote:
>
> My Smalltalk

Where can I have a look at that ?

Reply | Threaded
Open this post in threaded view
|

Re: Hmmm sending at:put: to an undefined object (nil) gives a confusing error message

K K Subbu
In reply to this post by Richard O'Keefe
On 13/03/19 12:45 PM, Richard O'Keefe wrote:
> Base 256: that's an implementation detail.
> Little-endian: that's an implementation detail.

Architecture-specific no. Implementation yes. But that should be fine
for private methods.

> My Smalltalk uses base 65536 native-endian and
> takes some care not to let Smalltalk code find out.
> (Not least because on 64-bit machines I want to
> use base 2**32.)

This would make it host-specific. Byte arrays are not host-specific.

> For *private* methods, depending on otherwise
> hidden implementation details is fair enough.
> My Smalltalk picked up a convention from Squeak
> -- or rather, from the Squeak mailing list --
> that a 'pvt' prefix on a selector makes it truly
> private and *enforces* that.  Pharo does not seem

pvt prefix is possibly inspired by Hungarian notation. I find that it
impairs readability and maintenance. Should a method change from private
to public or vice versa down the line, maintenance will be a nightmare.
My own preference is placing them in 'private' category (or even
private-* categories).

> to have anything similar, and #at: is the epitome
> of a selector in extremely wide general use.
> Since #digitAt: exists -- and is what the integer
> classes use -- it is practically certain that #at:
> is sent to an integer only by mistake.

You make a good point here. Perhaps #at:/#at:put: should throw an error
for all numbers and private methods could use basic versions instead.

Regards .. Subbu

Reply | Threaded
Open this post in threaded view
|

Re: Hmmm sending at:put: to an undefined object (nil) gives a confusing error message

Richard O'Keefe
Byte arrays are not host specific.  True.
But integers are not byte arrays.  They don't even contain byte arrays.
Just like (boxed) Floats are not word arrays and don't contain word arrays.

For what it's worth, I find that it's quite unusual for a method to
change from private to public or vice versa.

The basic problem with putting methods in a "private" category is
that it has no actual effect.  It was Smalltalk that taught me OO and
the importance of encapsulation.  But Smalltalk doesn't actually
support encapsulation.  Every detail of the implementation of
Dictionary, for example, is exposed.  And the practice of
"self-encapsulation" is actually ANTI-encapsulation for this reason.

Now there are several ways to implement hashed collections.
The one I've had for years is pleasantly fast and pleasantly
free of weird restrictions (keys/elements can be nil and why
not).  I've got a new one I'm working on that uses less space
and is faster still.  Because my dialect *enforces* encapsulation,
when my testing is complete, I'll be able to swap the new
version in, knowing that no external code can possibly depend on
the implementation details.obj

As a particular example, it was only when I implemented
serialisation (nowhere up to Fuel's standards, of course) that
I implemented #pvtInstVarAt:[put:] and an object is *still*
completely and exclusively in charge of its own instance
variables.

I agree that using a prefix is not the best way to do this,
but the eye soon learns to skip over it.  It's not that much
worse than "Inst" or "Var" or the pervasive and obtrusive
use of prefixes on selectors in VisualAge Smalltalk.  I think
Pharo would be the better for having *some* way to enforce
encapsulation, but what the best way is I don't claim to know.

On Wed, 13 Mar 2019 at 21:35, K K Subbu <[hidden email]> wrote:
On 13/03/19 12:45 PM, Richard O'Keefe wrote:
> Base 256: that's an implementation detail.
> Little-endian: that's an implementation detail.

Architecture-specific no. Implementation yes. But that should be fine
for private methods.

> My Smalltalk uses base 65536 native-endian and
> takes some care not to let Smalltalk code find out.
> (Not least because on 64-bit machines I want to
> use base 2**32.)

This would make it host-specific. Byte arrays are not host-specific.

> For *private* methods, depending on otherwise
> hidden implementation details is fair enough.
> My Smalltalk picked up a convention from Squeak
> -- or rather, from the Squeak mailing list --
> that a 'pvt' prefix on a selector makes it truly
> private and *enforces* that.  Pharo does not seem

pvt prefix is possibly inspired by Hungarian notation. I find that it
impairs readability and maintenance. Should a method change from private
to public or vice versa down the line, maintenance will be a nightmare.
My own preference is placing them in 'private' category (or even
private-* categories).

> to have anything similar, and #at: is the epitome
> of a selector in extremely wide general use.
> Since #digitAt: exists -- and is what the integer
> classes use -- it is practically certain that #at:
> is sent to an integer only by mistake.

You make a good point here. Perhaps #at:/#at:put: should throw an error
for all numbers and private methods could use basic versions instead.

Regards .. Subbu