What should operator_convert do with a nil object?

In the midst of code-refactoring, I changed a class from an “isa” relationship to a “has a” relationship. Since the old code relied on the super/subclass relationship, I thought “hey, simple, I’ll just add an operator_convert” when I need the superclass.

Did . not . work . well.

One problem among many was that passing nil objects works great with super/subclass relationship, but fails with operator_convert.

  assume P is a subclass of Picture called cPicture
  dim p as Picture = GetNewCPicture(...) // a method which returns a Picture subclass.
  // works great : GetNewCPicture can return Nil

but…

  assume P is a class called cPicture which has a Picture property, 
 and an Operator_convert as Picture which returns this property

 dim p as Picture = GetNewCPicture(...)   // crashes with NilObjectException, presumably due to operator_convert being invoked on Nil

Dont think I quite follow your set up for the operator convert portion of your example

sounds like you’re asking what should

NIL_OBJECT.operator_convert() as Picture 

return
Nothing
It should raise a nil object exception
You cant invoke a method of a nil object

Originally, I had lots of code which expected a specific class (Picture). I wrote a subclass of Picture (with additional features) but code which handled Picture would be just fine with Picture-Subclass. That’s how OOP is supposed to work.

For efficiency reasons I had to refactor it to a class which no longer was a Picture, but merely owned a Picture. This would then break a lot of code since it was no longer a Picture.

I thought that operator_convert would be a nice way to make it “plug and play” with older code. If the code was expecting a Picture object, but had been passed a cPicture object, then operator_convert would save the day.

[quote=208507:@Norman Palardy]sounds like you’re asking what should
NIL_OBJECT.operator_convert() as Picture
return
[/quote]

Right - it’s a philosophical decision I think - just look at ObjectiveC which happily sends all messages targeted at a NIL object to the trash, without crashing.

In this case, the fact that Operator_convert(NIL) doesn’t return NIL is problematic, for me in how I was trying to use it in refactoring old code.

There’s a related gotcha with (If memory serves) the difference between “if x = nil” and “if x is nil” when operator_convert is in play.

Are you talking a bout a convert FROM and passing a nil parameter in ?
If so thats your code
Handle nil & be done with it
Thats in your hands

This cannot be. Both Operator_Convert (“From” and “To”) can of course return and take a Nil (assuming that the data type returned or taken as argument is defined to be a Picture instance). As you have not shown the code of your cPicture class, it is hard to tell, but a quickly whipped up project shows, that this indeed works with Nil values:

[code]Class OldPictureSubclass Inherits Picture
// just a subclass
End Class

Class NewPictureSubclass
Private mPicture As Picture
Sub Constructor(pic As Picture)
mPicture = pic
End Sub
Private Sub Operator_Convert(pic As Picture)
mPicture = pic
End Sub
Private Function Operator_Convert() As Picture
Return mPicture
End Function
End Class

Dim pOld1 As OldPictureSubclass = Nil
Dim pOld2 As OldPictureSubclass = New OldPictureSubclass(1, 1, 32)
Dim pNew1 As NewPictureSubclass = Nil
Dim pNew2 As NewPictureSubclass = New NewPictureSubclass(New Picture(1, 1, 32))
Dim pNew3 As NewPictureSubclass = New NewPictureSubclass(Nil)

BREAK
// In the debugger:
// pOld1 is Nil
// pOld2 is a valid instance of OldPictureSubclass showing an image
// pNew1 is Nil
// pNew2 is a valid instance of NewPictureSubclass holding a private picture property showing an image
// pNew3 is a valid instance of NewPictureSubclass holding a private picture property being Nil
[/code]

As stated in the original post, Operator_Convert (the “to” form, not the “from” form) does not work with a nil reference.

Eli, to extend your code example, imagine you have functions which return a Picture object, but sometimes returns NIL to signal a problem. I’ve named these “GetAPicture()” and “GetANilPicture()”

function GetAPicture as NewPictureSubclass
  return new NewPictureSubclass(new Picture(64,64))
end function

function GetANilPicture as NewPictureSubclass
  return NIL
end function

  dim pold3 as Picture = GetAPicture()   // this works
  dim pold4 as Picture = GetANilPicture() // fails with NOE

What this means in practice is that you can’t use Operator_Convert in this case to refactor your code, if your code ever uses NIL objects. It simply won’t be a drop-in replacement.

On a side note, it’s also a little confusing in the debugger because the code fails on this line:

   dim pold4 as Picture = GetANilPicture() 

and yet in the debugger it’s not clear why the NOE is being triggered. It might be nice if there was more info available, e.g. “NOE in Operator_convert from X to Y”

[quote=208666:@Michael Diehr]function GetANilPicture as NewPictureSubclass
return NIL
end function[/quote]
… should be:

function GetANilPicture as NewPictureSubclass return new NewPictureSubclass(Nil) end function

[quote=208789:@Eli Ott]… should be:

function GetANilPicture as NewPictureSubclass return new NewPictureSubclass(Nil) end function[/quote]

I suppose one could do it that way, but that gets really messy - you would then have to remember while debugging whether NIL means NIL or whether “NIL” is flagged by sending a non-nil object that would automatically operator_convert() to NIL on the fly.

In the end, I decided that the Superclass/Subclass relationship that I had started with made more sense, so I reverted back to that pre-refactoring code. Thank goodness for SVN!

I wonder, though: Would it not make sense in general for the “TO” version of operator_convert(NIL) to just be defined as NIL ? Since the TO version of Operator_convert(NIL) will always result in a NOE.

Possibly related question - does Xojo track the type for NIL objects?

What will this code do:

  dim v as variant
  dim p as picture = nil
  dim o as object

  v = p
  o = v  // does this work at runtime?
  o = p   // this shouldn't be allowed at compile time

I’m guessing that this is allowed, due to the magic of Variants.

Why this is relevant is the question of whether NIL objects have a class type, or whether NIL is an un-typed entity?

Given

dim pold4 as Picture = GetANilPicture() 

function GetANilPicture as NewPictureSubclass
  return NIL
end function

The line

dim pold4 as Picture = GetANilPicture() 

REQUIRES a conversion since GetANilPicture returns a NewPIctureSubclass
So the compiler is smart enough to know to call a suitable operator_convert

You can try this yourself manually in fact - just write the code to load a temporary and call operator_convert
In effect what you’re trying to do is

dim tmp as NewPictureSubclass
tmp = GetANilPicture() 
dim pold4 as Picture = tmp.Opertaor_convert() // and this is the convert TO Picture

now tmp is NIL … so exactly how would you call operator convert on a nil object ?

It makes perfect sense you get an NOE

And no - NIL has no type about where it came from - nil is nil is nil is nil

Its doing exactly what I’d expect it to do

Nil denotes the absence of an object in a variable which is typed to hold an object. Hence Nil is not typed and it is not an Object (the Xojo base class) or a subclass thereof.

Again – since Norman already stated that – you are trying to apply Operator_Convert to Nil – that is the real problem.

Dim p As Picture = Nil.Operator_Convert()   // not possible

What you need is an instance of the class where Operator_Convert is defined:

Dim p As Picture = aNewPictureSubclassInstance.Operator_Convert() // this will work and return Nil or the // picture stored in mPicture
Of course one leaves away Operator_Convert:

Dim p As Picture = aNewPictureSubclassInstance   // this will work

[quote=208795:@Michael Diehr]function GetANilPicture as NewPictureSubclass
return new NewPictureSubclass(Nil)
end function
I suppose one could do it that way, but that gets really messy - you would then have to remember while debugging whether NIL means NIL or whether “NIL” is flagged by sending a non-nil object that would automatically operator_convert() to NIL on the fly.[/quote]
I don’t understand what would be messy here. That is the proper way to do it. That’s what Operator_Convert exists for. It is clean and works well.

To be honest, I don’t understand the second part of your sentence [i]“NIL” is flagged by sending a non-nil object that would automatically operator_convert() to NIL). What do you mean by that?

Operator_Convert (the “To” one, the function) returns the picture it wraps (in the above case the private mPicture property). If mPicture is Nil it will return Nil, if mPicture contains a picture it will return that. You define it. There is no built-in, automatic conversion.

[code]Dim pNew1 As NewPictureSubclass = New NewPictureSubclass(New Picture(1, 1, 32))
Dim pNew2 As NewPictureSubclass = New NewPictureSubclass(Nil)

// the two lines above could be written as:
Dim pNew1 As NewPictureSubclass = New Picture(1, 1, 32) // same as first line
Dim pNew2 As NewPictureSubclass = Nil // same as second line, but will NOT work as Operator_Convert
// can not be applied to Nil

Dim pic1 As Picture = pNew1 // will work, pic1 will contain the picture
Dim pic2 As Picture = pNew2 // will work, pic2 will be Nil[/code]

Excuse me, but why not create a myOperator_Convert which takes a variant and contains the proper code to manage a Nil, so it does not NOE ?

I’m confused. Don’t “TO” versions of Operator_Convert return a value and accept zero arguments?

Yes
The way Michaels written it is confusing since operator_convert() as TYPE takes no params
Thats why I wrote out the code he could try manually that does essentially what the compiler writes for him which is using a temp then invoking operator convert on the temp

If temp is nil its going to NOE
No getting around that

Think about it this way : there is the philosophy - “What is the sound of a NIL object converting”

And then there is the on-the-ground reality: what happens when you try to refactor an ISA class to a HASA class in an existing codebase that returns NIL objects to mean “failure”?

My experience (only one data point, given) is that the fact that Operator_Convert ( the “TO” version) does not do what I hoped or expected, and introduced about 50 subtle bugs in an otherwise mature (10+ years) code base. When you are doing refactoring of a class, it’s really nice to be able to just alter the Class and it and have it be a “drop in” replacement for the old code. This doesn’t seem possible. True : this is rather esoteric, and if it hasn’t hurt you - who cares.

But I still do wonder whether the “Operator_Convert on Nil is ALWAYS a NilObjectException” is the right choice design-wise.

If, as some are claiming, “NIL” is a generic “everyman” object representing “nothing” then it seems to me that Operator_convert(NIL) must be defined as NIL.

This is a little bit like arguing about division-by-zero. I think both sides have valid points. :slight_smile:

You’re not calling operator_convert( nil )

That IS the crux of the problem

You’re doing

dim tmp as NewPictureSubclass
tmp = Nil
dim pold4 as Picture = tmp.Operator_convert() // and this is the convert TO Picture but calling convert ON A NIL OBJECT

No matter HOW you slice this or what philosophical sound it makes the end result is calling a method on a nil object IS going to give you a NIL OBJECT EXCEPTION

Nil is not an object.
Nil does not point to anything in memory, it does not reference anything, it is not associated with any object.
Nil has therefore no type.
Nil is like void* in C, or null in C#, or Nothing in VB.NET, or null in Java, and so on.

Operator_Convert is defined to be called on NewPictureSubclass instances, and by now it should dawn to you, Nil ist not a NewPictureSubclass instance.

We are mainly in agreement : Nil is “special” and the semantics of it are up to the compiler designer / language designer. We just don’t necessarily agree about one of the specific edge cases.

Here’s an interesting variation:

  dim p as Picture = nil
  
  dim pNew as new NewPictureSubclass(nil)
  
  dim v as variant = p
  pNew = NewPictureSubclass(p)  // works
  pNew = NewPictureSubclass(v)  // works
  
  p  = Picture(GetANilPicture())  // works!!!!
  p = GetANilPicture()  // fails with NOE due to operator_convert being called on NIL

Notice in the line

  p  = Picture(GetANilPicture()) 

I’m guessing the explicit cast to Picture tricks the compiler into skipping the operator_convert() call? I don’t think this would be a general solution however, as it would fail with non-nil objects.

This is equivalent to:

Dim p As Picture = Picture(Nil)

Since you cast Nil to a Picture Nil is assigned to p. This does not trick the compiler into skipping the operator_convert() call, it is just not necessary to call it.

Wrong, why should this fail. You can do such a cast without any troubles:

Dim p As Picture = Picture(New Picture(1, 1, 32))

This is not an edge case. Again: Nil is Nil, null, Nothing, zero, 0, the absence of an object, void*… It is not a language designer issue, it is just not possible to Operator_Convert on Nil.