No Nulls?

Why are nulls not permitted in OSID contracts?

Primarily, this was done to avoid interoperability issues where two OSID Providers exhibit different behaviors. An application coded to a strict OSID Provider that never has nulls will look like this:

app_display(asset.getDescription());
app_display(asset.getGenusType());

I swap OSID Providers and I get a NullPointerException from the second OSID Provider. So what do I do? I patch my application code to add this:

if (asset.getDescription() != null) {
    app_display(asset.getDescription());
}

app_display(asset.getGenusType());

Then I swap with a third OSID Provider and get a NullPointerException on the type. Perhaps at this point I assume that I could get a null from anything and I have to go through and trap a null on every accessor.

It could have been said in the specification explicitly that every accessor "returns XYZ or null" and the spec is covered. The consuming code becomes an endless stream of null checks.

It can be refactored in such a way to consolidate these checks based on the primitive, e.g.

app_display_text(asset.getDescription());
app_display_type(asset.getGenusType());

app_display_text(DisplayText text) {
    if (text != null) {
       print (text);
    } else {
       print ("");
    }
}

However, the problem with this factoring is that the context is lost. I might want a different "default" for the name and the description.

From an interoperability perspective, there is no way to guarantee that an OSID Consumer is actually making these checks until the issue surfaces on a swap of OSID Providers.

What the OSIDs did was put to embed that default behavior inside the OsidObject implementation and make it an issue of the OSID Provider. Doing so comes at a cost and creates a few twists. There's no free lunch -- removing logic from an OSID Consumer puts the problem into an OSID Provider and vice versa. Understandably, no one else handles DTOs this way so its uncomfortable. But OsidObjects, at the service level, are full blown service contracts and unidirectional.

It turns out, default-ness is doable as long as the return is a primitive. Simple rules can be:

DisplayText ==> "" or some other string like "NONE"

number ==> 0

Type ==> Unknown Type

DateTime ==> infinite uncertainty ("I don't know")

These defaulting rules can be more complex and configurable based on the application environment.

Complex interfaces fall into one of two categories. Either the OSID is saying "you must have one of these and if you really don't because of your data issues, then fake it out." Or the OSID is saying "you may or may not have one of these." The answer to this question must be determined in the design of the interface.

A CourseOffering must have a Course. It can't exist without one. So, what to do when that column is NULL in your db? Make the "I don't know Course" and log a message that you have a data integrity problem.

An Event might have a Location. It might not. This will get wrapped in a hasLocation() and if it is false, then going after the Location throws an ILLEGAL_STATE. The determination of whether something is required is always made at a modeling level, never looking at use cases of real life data stores. Looking at people's data leads to anything is possible at any time. Annoying, but stable.

Because the create/update mechanism is completely independent of the OsidObject, here we get to communicate information pertinent to data administration that isn't visible in the OsidObject.

An examination of Metadata reveals what has and has not been set by an OSID Consumer, and what the default value would be when the value is cleared. In other frameworks, clearing a value is typically performed by setting it to null. In OSIDs, this is done explicitly through a clear operation in the OsidForm.

The OsidObjects in lookup and search sessions are for general application consumption, while the OsidForms in admin sessions provide an administrative data view.

Copyright © 2014 Okapia.