BRiX
Advanced Computing Environment
Hosted by SourceForge
brix-os project page

Previous: Parsing and Rewrite Patterns ----- Up: Contents ----- Next: Static and Dynamic Typing

Identifiers, Objects and References

show notes
Features that need to be added to the page or ideas that haven't been thought out.
  • the language does not support references to references, it does however support reference slots which can be combined with a parameterized type to create a double reference.
    	Reference = deftype (T:Type) {
    	  constructor (r:T)
    	  `ref slot r = r
    	  setter (r) -> self.r = r
    	}
    	s := "string"
    	ss:Reference(String) = s

Identifiers
A named identifier is a means of accessing an object. The identifier maps a name to a physical location that either contains the object or a pointer to it. The identifier has a type and modifiers associated with it that define how it references the object and what objects it may reference.

Objects
An object resides on the stack or heap and is a container for various data. Every object has a type and modifiers associated with it that defines how the object may be accessed. Information about static objects is managed internally by the compiler while dynamically typed objects are wrapped in a box carrying type information.

References
These are transparent pointers from identifiers to objects. By minimizing explicit reference use the compiler can better optimize how objects are created and passed.


Modifier Flags
Flags applied to objects, identifiers and types modify how each are used. Excessive use of explicit modifiers will generate compile-time errors.

	`flags identifier : `flags Type
	`flags Type
	`flags object

Access Modifiers
Variables and slots default to mutable identifiers referencing mutable objects. Function parameters default to immutable identifiers referencing immutable objects. Access modifiers may also be applied to types not bound to identifiers.

	foo := 0				//   mutable :   mutable
	`const pi := 3.14			// immutable : immutable
	foo : `const String = "string"		//   mutable : immutable
	`const foo : `mutable String = "string"	// immutable :   mutable
When `const is applied to a variable or slot the immutability applies to both the identifier and the type. The third example has `const applied to only the type allowing the identifier to reference other immutable objects. The fourth example allows the object contents to change but the identifier may only reference that object. The situation is reversed for function parameters since they default to immutable.
	foo : String			// immutable : immutable
	`mutable foo : String		//   mutable :   mutable
	`mutable foo : `const String	//   mutable : immutable
	foo : `mutable String		// immutable :   mutable
The second example is similar to `const on variables and its mutability applies to both identifier and type.

String literals default to immutable references and applying `mutable forces a copy to be made from the immutable object. Character and Number literals default to mutable objects.

	s := "string"		// mutable : immutable
	s := `mutable "string"	// mutable :   mutable copy

Reference Modifiers
The default reference-style selected by the compiler for variables and function parameters is usually correct and should only be changed when necessary. Object slots are however stored inline and this causes dynamically sized slots to become the last declarable slot in an object. The `ref flag may be used to declare the slot as a reference to an external object. Explicit references don't change the mutability of the identifier or object.

	`ref slot foo = `mutable ""
The `nullable flag marks the identifier as a reference that may be undefined.
	`nullable foo := "string"
	foo = null
Nullable references are automatically null-checked before each use and throw a null_reference_error when undefined. Iterating over a nullable collection object using an incrementing loop will generate a null-check for every index. However, extensions like EACH solve this problem with a single null-check.

Reference modifiers may be used on identifiers, types and objects but the compiler will attempt to optimize them away. Applying the flag to the identifier is the best way to ensure it contains a reference. Nullable references can be placed anywhere and will always propagate into the identifier.

In-Out Modifiers
Function parameters have an additional `inout reference modifier that defines an immutable identifier referencing a mutable object. No other access or reference modifiers may be used on the identifier or its type.

	`inout foo : String
The `inout flag is also required for each in-out parameter when calling functions. The compiler knows they are references but requires the flag to make the side-effects stand out.
	def foo(`inout s:String) ...
	s := `mutable "string"
	foo(`inout s)

Identity
The language requires special identity operators to compare reference pointers that would otherwise not be accessible due to their transparency. Two objects are equal if they share the same memory address.

	defop `infix === (`ref left, `ref right)
	  -> tetra.compiler.isIdentityEqual(left, right)
	defop `infix !== (`ref left, `ref right)
	  -> not tetra.compiler.isIdentityEqual(left, right)

	`nullable slot head = null
	`nullable slot tail = null
	...
	if (self.head === null) ...
	if (self.head === self.tail) ...

Null Predicate
The prefix null? predicate operator can be used by conditional statements to test for undefined references instead of using the identity operator to compare against the null literal.

	null? = defx`open-macro (`nullable right:Object)
	  -> tetra.compiler.isNull(right)

	`nullable slot head = null
	...
	if (not null? self.head) ...

Previous: Parsing and Rewrite Patterns ----- Up: Contents ----- Next: Static and Dynamic Typing