Vision Class: Undefined (NA)
Overview
The Undefined class has a single instance that represents a value that is not available. This instance is named NA. Any object can be compared to the NA object using the messages isNA and isntNA. For example:
- "x" isntNA print ;
- NA isNA print ;
- 3 isNA print ;
- NA isntNA print ;
The first two examples return and print the value TRUE. The last two examples return and print the value FALSE.
You cannot directly create new instances of the Undefined class. You can, however, create subclasses of the Undefined class which have any number of instances.
The Undefined class is a direct subclass of Object:
Object | Undefined | |-- NoValue
Generating NA Values
The message NA is defined at the class Object to return the object representing the NA value. NA values can be generated in a number of ways:
- All property values are initially NA
- The NA value can be assigned into a variable or property
- A method or class constant can return an NA
When you define a variable, a fixed property, or a time series property, its initial value is NA. For example:
!variable ; variable print ;and
Currency defineFixedProperty: 'fixed' ; Named Currency USD fixed print ;and
Currency define: 'ts' ; Named Currency USD ts print ;all return and print the value NA.
Since NA is just an object, it can be assigned as the value of a property. For example:
!variable <- 10 ; #- assign value variable print ; #- print it :variable <- NA ; #- assign NA variable print ; #- print itand
Named Currency USD :fixed <- 10 ; #- assign value Named Currency USD fixed print ; #- print it Named Currency USD :fixed <- NA ; #- assign NA Named Currency USD fixed print ; #- print itand
Named Currency USD :ts asOf: 93 put: 10 ; #- assign value Named Currency USD ts print ; #- print it Named Currency USD :ts asOf: 94 put: NA ; #- assign NA Named Currency USD ts print ; #- print it
Many methods return NA values. For example, the asDate message defined at the class Integer returns NA if the recipient integer cannot be converted to a valid date:
9513 asDate whatAmI print ;The arithmetic messages return NA if the recipient or parameter value is NA. For example:
9513 asDate asInteger + 5returns the NA value because the expression 9513 asDate asInteger returns NA and adding 5 to NA returns NA.
You can define a class constant to return the NA value if you want all instances of the class to return NA independent of the instance. For example:
String define: 'returnNA' toBe: NA ; "abc" returnNA print ;returns and prints the value NA.
NA Messages
Most of the numeric computation and various conversion messages have been defined at the NA class to return the NA value. If you perform any calculation using NA as the recipient or parameter, the NA value is returned. For example the following calculations will all return and print the NA value:
(3 + NA) print ; (NA * 5) print ; 3 min: NA . print ; NA max: 5 . print ;
Many of the messages that convert objects from one class to another are also defined at NA to return the NA value. For example:
NA asIntegerreturns the value NA.
NA is used as a value for many properties that contain numeric data. Therefore, when you define messages at one of the Number classes, it is often useful to define the same message at the NA class. Normally the message returns the value NA. For example:
Number defineMethod: [ | double | ^self * 2 ] ; NA define: 'double' toBe: NA ;You can define the double message as a class constant for the NA class since it always returns the NA value. If you want to define a parameterized selector as a class constant, use:
NA define: 'within:percentOf:' toBe: NA ;Note that the message name must be a valid selector with the ':' character following each parameter and no blank spaces between the single quotes.
NAs and Conditional Tests
Several messages that perform conditional tests can be sent to NA:
If any of the first three of these messages is sent to NA, the else: clause will be evaluated. For example:
"10" asNumber > 5 # case 1 ifTrue: [ " passes test " printNL ] else: [ " fails test" printNL ] ; "abc" asNumber > 5 # case 2 ifTrue: [ " passes test " printNL ] else: [ " fails test" printNL ] ;In case 1, the string "10" converts to a number greater than 5 so the ifTrue: clause is executed and the "passes test" message is displayed. In case 2, the string "abc" cannot be converted to a number so the value NA is returned. The test NA > 5 returns NA. Since the value is not TRUE, the else: clause is executed and the "fails test" message is displayed.
The messages ifFalse:else: and ifTrue:ifFalse:else: work in a similar manner. Note that the messages ifTrue:, ifFalse:, and ifTrue:ifFalse: are only defined for the Boolean classes. If you send any of these messages to the NA object, you will generate a Selector Not Found error.
The messages else: and elseIf:then: can be sent to any object. When either message is sent to a non-NA object, that object is returned. When the else: message is sent to an NA, the parameter is returned. If the parameter is a block, it is evaluated before it is returned. When the elseIf:then: message is sent to an NA, the first parameter is evaluated. If it returns TRUE, the second parameter is evaluated and returned, otherwise NA is returned. These two messages are often used in unison to perform a series of conditional tests. For example:
v isNumber ifTrue: [ "it's a number" ] else: [ NA ] . elseIf: [ v isString ] then: [ "it's a string" ] . else: [ "Unknown object type" ] . print ;
If the variable v is a number, the ifTrue: clause will be evaluated. This message returns the string "it's a number" if it is evaluated; otherwise, the value NA is returned. If the string is returned, the elseIf:then: and else: messages will return the same object. If an NA is returned, the elseIf:then: message will evaluate the v isString clause. If it returns TRUE, the then: clause will be evaluated and will return the string "it's a string"; otherwise, the value NA is returned. The else: message will return the string "Unknown object type" if it is sent to an NA. The final print will display the actual string returned.
Creating NA Subclasses
You can create subclasses of the Undefined class using the standard procedures. Although you cannot create new instances of the Undefined class directly, you can create new instances of any subclasses you create. For example, you might want to create a subclass to store different types of NA values.
Warning!! Because the Undefined class is a built-in class that is created primitively as part of the Vision database bootstrap, its subclasses do not inherit a place to store fixed property values defined at parent classes. As a result, you cannot set values for the properties defined at Object in objects that are subclasses of Undefined. If you want to use these properties, you must redefine them at your subclass. Also, you should use the clusterNew version of the new message to instantiate this class if you want to be able to access the instances with the instanceList message.For example:
Undefined createSubclass: "MyNA" ; #- create subclass MyNA defineFixedProperty: 'code' ; #- redefine property MyNA defineFixedProperty: 'name' ; #- new property MyNA clusterNew #- new instance do: [ :code <- "NM" ; :name <- "Not Meaningful" ; ] ; MyNA clusterNew #- new instance do: [ :code <- "NS" ; :name <- "Not Significant" ; ] ; MyNA instanceList #- get all instances do: [code print: 5 ; name printNL ] ;
The NoValue Value
To distinguish between programs that return no value and the NA value, the built-in class NoValue has been defined as a subclass of the Undefined class. This class has a single instance which is returned when a block returns "nothing". In the expression:
!x <- 3 < 5 ifTrue: [ 2 ; ]the value of the variable x is set to the value returned by the ifTrue: block. Since this block ends with the ';' character, it is returning "nothing". The NoValue object is defined not to print anything when it is returned. If you send the showInheritance message to this object, you will see the class hierarchy:
Object | Undefined | NoValueBecause the NoValue object is a subclass of Undefined, it responds to the isNA and isntNA messages with the values TRUE and FALSE.