Skip to content
Greg Hewgill edited this page Jul 25, 2019 · 1 revision

This proposal introduces a dynamically typed concept into Neon, called Object. The original motivation for this is that handling JSON data is very awkward using the variant module. The goal is to be able to handle JSON data naturally and easily, while fitting into a strongly typed framework. Further goals may involve interoperability with other runtime systems such as JVM objects, COM (Windows), or JS, depending on what the execution environment can offer.

This proposal would replace the variant module.

Simple Types

A value of type Object can hold a value of any type and can be reassigned with a different type.

VAR a: Object
a := 5
print(str(a))
a := "hello"
print(a)

When a type is requested that cannot be satisfied by the object, a runtime exception is raised:

VAR a: Object
a := "hello"
LET b: Number := a + 1

In this case an exception would be raised on the last statement because a does not contain a Number.

Comparisons

The = operator compares two values. What does the expression a = b mean, if a and b are both objects? It would be unreasonable to attempt to guess, at runtime, which type should be used for equality comparison. It would also be unreasonable to use object identity in this case, since two different objects may represent the same value. Probably best to disallow this specific kind of comparison.

A comparison with a value of a specific type, such as

a = 5

would do a numeric comparison after attempting to convert a to a Number.

Aggregate Types

Aggregate types (arrays and dictionaries) can also be stored in values of type Object.

VAR a: Object := [1, 2, 3]
LET b: Array<Object> := a
LET c: Array<Number> := a

Note that the array can remain an array of objects (b), or be deconstructed into an array of a specific type (c). In the b case, the array could contain values of different types. In the c case, each element must be a Number.

Type Testing

A new operator ISA can be used to test the type of an Object.

VAR a: Object := 5
ASSERT a ISA Number
a := [1, 2, 3]
ASSERT a ISA Array<Object>
ASSERT a ISA Array<Number>

A TRUE result for an ISA test means that converting the object to a value of that type will succeed.

External Object Systems

This can be extended in the future to handle objects defined by an external system, eg. JVM. For example:

EXTENSION OBJECT JavaObject

LET a: Object := NEW JavaObject("java.util.zip.ZipFile", "filename.zip")
LET files: Array<Object> := a.entries()
FOREACH e IN files DO
    print(e.getName())
END FOREACH

The implementation of an extension OBJECT would optionally provide functions similar to the following for conversion between Neon native types.

Implementation

The compiler is responsible for generating function calls behind the scenes for accesses to objects. For example, the following Neon declarations show the kinds of functions that will be used:

FUNCTION makeNull(): Object
FUNCTION makeBoolean(b: Boolean): Object
FUNCTION makeNumber(n: Number): Object
FUNCTION makeString(s: String): Object
FUNCTION makeArray(a: Array<Object>): Object
FUNCTION makeDictionary(d: Dictionary<Object>): Object

FUNCTION Object.toBoolean(self: Object): Boolean
FUNCTION Object.toNumber(self: Object): Number
FUNCTION Object.toString(self: Object): String
FUNCTION Object.toArray(self: Object): Array<Object>
FUNCTION Object.toDictionary(self: Object): Dictionary<Object>

FUNCTION Object.getArrayElement(self: Object, i: Number): Object
FUNCTION Object.getDictionaryElement(self: Object, k: String): Object
FUNCTION Object.setArrayElement(self: Object, i: Number, v: Object)
FUNCTION Object.setDictionaryElement(self: Object, k: String, v: Object)

Examples of how these might be expanded are as follows:

VAR a: Object
a := 5                      | a := makeNumber(5)
LET n: Number := a          | LET n: Number := a.toNumber()
a := [1, 2, 3]              | a := makeArray([makeNumber(1), makeNumber(2), makeNumber(3)])
n := a[0]                   | n := a.getArrayElement(0).toNumber()
a[0] := 5                   | a.setArrayElement(0, makeNumber(5))
LET b: Array<Number> := a   | LET b: Array<Number> := []
                            | FOREACH x OF a INDEX i DO
                            |     b[i] := x.toNumber()
                            | END FOREACH

It is not permitted to use an Object value as an INOUT parameter.