OrmDate is a Date subclass that implements Operator_Convert(Date). All it does is call Constructor(Date), and that all works fine, unless someDate is nil. In that case, I want o to be nil too, but I don’t see how I can do that.
First, is the behavior I’m seeing intended or a bug? Second, is there any way for Operator_Convert to abort without raising an exception? In other words, how can I get the behavior I want?
OrmDate is a Date subclass that implements Operator_Convert(Date). All it does is call Constructor(Date), and that all works fine, unless someDate is nil. In that case, I want o to be nil too, but I don’t see how I can do that.
[/quote]
You cant
Intended AFAIK
Not that I know of
You cant
Recall that Operator_convert is more like an “initializer” than an “allocator”
But the time it is called the instance is created and the operator_convert is your opportunity ti convert some data and initialize this instance from that data
But you cannot change the fact the instance has been created
Mars and I had this discussion ages ago on the old forums (maybe the old NUG)
oh I can but then the object is basically a proxy and knowing that is was set with nil is important
Say you have object wrappers for data types (like Java does for “Integer” which wraps an integer)
Very handy for database stuff since you now have a way to represent an integer column that has a nil value
No because now I would have a nil object not an object that contains a nil
Big difference
Imagine some ORM code that created these objects (as I described)
Then at some point you could see if any contained a nil (since it might have an instance method like “IsNil”)
And that some time in the future you wanted to put a value IN that object so it was no longer “nil”
You have to change your code to check for nil objects and create the right one at that point
Objects that can exist but contain nil is a useful design in lots of places
I see that, but I don’t think that’s the usual case.
No matter, since it would change behavior, perhaps there is another solution. What comes to mind immediately is an additional form of Operator_Convert:
Function Operator_Convert (o As Object) As MyClass
if o is nil then
return nil
else
// Do some stuff with o
return self
end if
End Function
That would solve my problem without disturbing existing code. Thoughts?
[quote=329048:@Kem Tekinay]I see that, but I don’t think that’s the usual case.
[/quote]
Well not YOUR use case but there are several places where people have asked about exactly this https://forum.xojo.com/conversations/all?search=nullable
[quote=329048:@Kem Tekinay]
No matter, since it would change behavior, perhaps there is another solution. What comes to mind immediately is an additional form of Operator_Convert:
Function Operator_Convert (o As Object) As MyClass
if o is nil then
return nil
else
// Do some stuff with o
return self
end if
End Function
That would solve my problem without disturbing existing code. Thoughts?[/quote]
Not sure it does
I might be misunderstanding. I’m proposing an additional form of Operator_Convert that does not currently exist, one that will let me control exactly what is assigned when used. If the input is nil, I can return nil and the variable I’m assigning it to will be nil.
Ah well that would explain why I’m not sure it would fix it
It wasnt that clear you were proposing a new operator convert form
It looks a lot to the “Convert TO” forms that already exist
Obviously I misread your post
Not sure I’d call this “operator_convert” though as this would have to have entirely different semantics
Currently operator_convert behaves more like a constructor + setter code (an initializer as it were)
But note neither of those allocate an instance
Thats done before your code is called (constructor or operator_convert)
This one is more like an allocator and decides whether to allocate an instance or not
And thats sufficiently different that I’d call it “something else”
And of course then everything has to know to call this if it exists
That seems a very big change
I’d just design your stuff so it can be nullable and move on since the FR is unlikely to happen ASAP (or soon enough for your immediate needs)
Almost what you want is something along the lines of
Function Operator_Allocate (o As Object) As boolean
if o is nil then
return false // do not allocate an object and leave the caller "nil"
else
return true // do allocate an object & call whatever other operator_convert / constructor methods
// need to be called after allocating an object and initially setting it up
end if
End Function
Havent though about the ramifications of this (or whether it would even be actually entertained)
What I did for accessing databases was to introduce a different API to the built-in one. This API consist of three parts for each data type:
A replacement for Nil
A replacement for the built-in Variant methods to access values from DatabaseField
A replacement for the bind method for PreparedStatement
This is the simplified code for Int64 as an example:
[code]Module Integers
// Used for sorting Nil values before or after non-Nil values when sorting arrays of Int64
Private Property mNilsLast As Boolean
Global Function NilInteger() As Int64
// Use the maximum and the minimum Int64 values as placeholders for Nil
Return If(mNilsLast, 9223372036854775807, -9223372036854775807)
End
// The next two methods look weird, but the Extends on Int64 allow for this code: Int64.SortNilsLast = …
Global Sub SortNilsLast(Extends i As Int64, Assigns value As Boolean)
mNilsLast = value
End
Global Function SortNilsLast(Extends i As Int64) As Boolean
Return mNilsLast
End
// To be used instead of Int64Value
Global Function AsInteger(Extends dbf As DatabaseField) as Int64
Return If(dbf.Value Is Nil, NilInteger, dbf.Int64Value)
End
// To be used instead of the built-in Bind method
Global Sub BindInteger(Extends ps As MySQLPreparedStatement, index As Integer, value As Int64)
If value = NilInteger Then
ps.BindType(index, MySQLPreparedStatement.MYSQL_TYPE_NULL)
ps.Bind(index, Nil)
Else
ps.BindType(index, MySQLPreparedStatement.MYSQL_TYPE_LONGLONG)
ps.Bind(index, value)
End
End
End Module[/code]
And this is how to use it:
[code]Dim rst As RecordSet = someDatabase.SQLSelect(“SELECT * FROM someTable;”)
Dim someField() As Int64
Do Until rst.EOF
someField.Append rst.Field(“someField”).AsInteger // Never use Int64Value here, as Nil would become Zero
End
If someField(0) = NilInteger Then // Comparing to Nil would not compile
// someField is Nil
Else
// someField is not Nil
End
I have these kind of modules for Date, Double, Int64, Text. For Boolean database values I have a class Trilean which can hold the values NilTrilean, False, True (internally stored as Integer with the values -1 = NilTrilean, 0 = False, 1 = True, 2 = NilTrilean).
The advantages of all that is that you use the internal data types, which doesn’t slow down the application. Introducing wrapper classes showed to be two slow when handling large amounts of data.