Advanced Computing Environment
Hosted by SourceForge
brix-os project page

Previous: Hygienic Macros ----- Up: Contents ----- Next: Properties


show notes
Features that need to be added to the page or ideas that haven't been thought out.
  • finish the complex parameters section - see the note on the extensions page for more details
  • types can conditionally inherit interfaces in the complex parameter parser (Numbers will inherit a bit_interface when unsigned)
  • the `from-constructor flag on a type parameter will set the parameter to the type of the constructor value or the common element type from a tuple, list or array. a default value for the parameter can be used to restrict the allowed element types. the default value is also used as the type when no constructor is available, a type must be specified in these cases if no default value is declared.
    	(`from-constructor T:Type = Object)
  • alias types are only needed if the alias can be parameterized, otherwise a simple declaration works
    	`export-root Int = `unbounded Number of 32 bit
  • some abstract types might not implement anything and appear to be interface types. interface types will need to be flagged as such or abstract types will need to be flagged. might be best to flag abstract types and throw an error if the flag isn't used.
  • an `abstract flag to convert a concrete type into an abstract type and always box it
  • a flag to treat a concrete type as an abstract type but allow instantiation. this would force boxing and allow subtypes to properly override methods without explicit boxing at the call sites.
  • (typeOf type) could return the type without its parameters
    	typeOf (Number from 1..10) --> Number
    should the resulting type be processed or unprocessed?
  • should interface collisions be ignored? (two parent types defining the same method interface)
  • replace copyOf, sizeOf, typeOf and parentOf with `deep-copy-of, `shallow-copy-of, `size-of, `type-of and `parent-of flags? they would be handled like `ref and reduce namespace pollution.
  • automatically bind slots to variables in accessors and methods so the "self." prefix isn't required?
  • syntax for objects that must support multiple interfaces?
    	obj :interface1 :interface2
    	obj :Object of (interface1, interface2)
  • allow IMPORT in the type body to pull in additional constructs?

Types, like extensions, are evaluated at compile-time, process any parameters and return a Type object. It is important to understand that this Type object is merely a representation of the type and not the data object that is later instantiated from the type. Type objects are then bound to identifiers, used to cast one object to another or define parameter interfaces. The constructor method on the Type object is invoked implicitly when initializing a variable or explicitly for anonymous objects. There is a set of extensions that may be used in the type body to define various components, immutable Properties can be declared with the = operator and nothing else happens in the type body.

DEFTYPE `final `open (parameters) definition
  • `final -- optional flag for final types
  • `open -- optional flag for open parameters
  • parameters -- parameter list, parentheses required even when empty
  • definition -- single or block expression [property declarations only]
	// declare the type
	foo := deftype () {
	  constructor (a, b)
	  slot a:Int = a
	  slot b:Int = b
	  method sum() return self.a + self.b

	x:foo = (1,2) // construct an object
	s := x.sum() // invoke object method
	x.a = 2 // set object slot
Slot and method names are declared for the object and do not exist in the type definition namespace, allowing the use of identical names for constructor parameters (private properties) and slots. The actual slots and methods are only available as members of the object or the self parameter in methods.

Parameterized Types
The parameters extend the type with an additional information to customize the data stored in slots. Optional values for each parameter allows the type to be used without any parameters. The compiler will throw a compile-time error if the type is used without parameters and no default values exist. Parameter labels may be used for function-style types.

Flag Parameters
See the flag parameters section on the Functions page.

Complex Parameters
All parameters must be declared in the interface using a special flag syntax to generate complex patterns. The parameters are then processed with code in a special extension to modify the resulting Type object. More to come later...

Open Parameters
The `open flag for DEFTYPE gets rid of the parentheses around parameters and enables unevaluated mode for complex parameters.

Unprocessed Types
The type returned from DEFTYPE is unprocessed (a type ID with uninitalized property slots) and may or may not be a valid type. There is no difference between parameterless and unprocessed types, since there are no property slots to initialize. A parameterized type is considered to be processed after it has been initialized with its minimum set of parameters, which initailizes all property slots with at least default values. Some parameterized types, such as Arrays and Numbers, can be processed without any parameters but still remain in an unprocessed state until evaluated in an expression. A type can not be reparameterized once it has been processed.

Self and self
The private property Self exists within the type body and may be used in interfaces for constructors, methods and function properties and may also be used in for slots and value properties. It is accessible inside methods and function properties when prefixed with the $ operator. The Self property contains the final implementation type, including any parameters. This allows an interface type to be defined around the final type without actually knowing what that may be.

The transparent self parameter exists within accessors and methods, and it represents the receiver object.

	Foo = deftype (T:addition_interface) {
	  constructor (n:T)
	  slot n = n
	  method add(o:Self) return (self.n + o.n) : $Self

The above code might be a bit confusing at first because self has two different meanings in the same statement. The method parameter list is in the context of the type body where Self is a type but self is in the method context where it represents the receiver object. The $Self type annotation is pointless but was added to demonstrate the usage of it inside the method.

$Self is a constant for non-parameterized types, otherwise it is a reference to the type information contained in the box for the given Type (function properties) or object (methods). Other properties are accessed as offsets into $Self. This forces boxing when a method of a parameterized type is invoked.

Interfaces and Implementations
Slots, methods and properties without an initial value or code are considered an interface definition while an implementation is a slot with a value or a method or property with a block of code. Interface types are those with only interfaces and no implementations. Abstract types are those with a mixture of interfaces and implementations. Concrete types are types with only implementations. Alias types are those which inherit from a parameterized concrete type and don't change the interface. Interface and abstract types can not be instantiated or aliased. Each type may only inherit one abstract or concrete type but may inherit any number of interface types. The Object type can not be explicitly inherited but is assumed to be the parent if no abstract or concrete type is inherited.

	slot foo:bar = _ // slot interface
	slot foo:Int = 1 // slot implementation

	method foo(a:bar) -> bar; // method interface
	method foo(a:bar) return a // method implementation

	foo = def (a:bar) -> bar; // property interface
	foo = def (a:bar) return a // property implementation

	// alias type
	Int = deftype () inherit (`unbounded `signed Number 32 bit)
Function and method interfaces must be terminated with a semicolon so the extension knows there is no body, otherwise it will process the next token as the body.

Final Types
A final type is a concrete type that can not be inherited.

Naming Conventions
Abstract and concrete types should begin with a capital letter to help distinguish them from other objects. Variables, parameters and slots containing types should also begin with a capital letter. Interface types should be all lowercase with underscores between words and suffixed with "_interface".

Boxed objects are always used for code that expects interface types, abstract types or incomplete parameterized types. The boxing allows interface and abstract types to lookup methods in a table belonging to the type of the object passed. This gives these types the ability to override parent methods. When a concrete type is expected the compiler assumes no boxing is required and subtypes are clipped if pass-by-value is used and in any case the method table of the expected type is always used. This prevents the use of overridden methods and extra slots are ignored. The `box flag will force concrete types to be boxed and preserve overrides and additional state in subtypes. Explicit boxing should be used wisely as it can cause performance problems for small objects such as Numbers and Characters.

The INHERIT extension inherits all interfaces and implementations from another type. This extension must be used before any interface modifying code. Modifiers are not allowed on inherited types. The single abstract or concrete type must be specified before any interface types. Only one instance of INHERIT per type body.

INHERIT parents
  • parents -- one or more types to inherit from
	// define an alias type
	Int = deftype () inherit (`unbounded `signed Number 32 bit)

	// inherit multiple parents
	Button = deftype () {
	  inherit (Widget, clickable_interface)

Recursive Types
Recursion is when slots have the same type as the one being defined. Tetra does not allow recursive slots but does allow recursive reference slots.

	ListNode = deftype (T:Type) {
	  constructor (data:T, `nullable next:Self)
	  slot next = next
	  slot data = data

Type Information
The SIZEOF extension returns the size in bytes of an object or type.

SIZEOF object
  • object -- object or type
	sizeOf foo
	sizeOf Number
The TYPEOF extension returns the type of an object.

TYPEOF object
  • object -- object
	typeOf foo
The PARENTOF extension returns the parent type of an object or type that inherits another type.

  • object -- object or type
	parentOf foo
	parentOf Number
The COMMONPARENTOF extension finds the common parent shared by two objects or types. Parameterized types can supply hooks to narrow the resulting parent. The DEFOP`common-infix extension uses this extension to populate its common variable.

COMMONPARENTOF object1 object2
  • object1 -- object or type
  • object2 -- object or type
	a:Int = 1
	b:Int8 = 2
	T := commonParentOf a b // T = Int

Previous: Hygienic Macros ----- Up: Contents ----- Next: Properties