Developer Edition
2026
Version: Draft 0.1
The design of Buoy is guided by the following principles.
Source code is written more often than it is read. Buoy therefore favors clarity and consistency over syntactic brevity.
A language feature should justify its existence. Features that duplicate existing capabilities or introduce unnecessary complexity are avoided.
The compiler should detect programming errors whenever possible before program execution. Type information is used to verify program correctness while remaining unobtrusive through judicious use of type inference.
Program structure should communicate programmer intent. Distinct
language constructs are used to represent distinct concepts. For
example, a Function computes and returns a value, while a
Sub performs an action without producing a result.
Language features should compose naturally. A feature that applies to one category of declaration should, where practical, apply consistently to others. Generic programming is an example of this principle.
Buoy is intended for writing real applications. The language favors solutions that are straightforward to understand, implement, and maintain over solutions that are theoretically elegant but difficult to use.
This document specifies the Buoy programming language.
It defines:
This document does not define implementation-specific behaviour unless explicitly stated.
The traditional first program displays the text “Hello, World!” on the standard output stream.
print("Hello, World!")
The precise entry point and program structure depend upon the execution model described in Chapter 3.
A Buoy program consists of one or more source files.
A source file contains declarations, executable statements, or both, depending upon the context in which the file is compiled.
The organization of source files into modules, packages, or compilation units is described in a later chapter.
Unless explicitly stated otherwise, identifiers are case-sensitive.
The identifiers
Value
value
VALUE
represent three distinct identifiers.
The spelling of language keywords is fixed.
The Buoy language is expected to evolve over time.
New language features may be introduced in future language revisions while preserving source compatibility whenever practical.
When compatibility cannot be preserved, the language specification shall identify the behavioural changes introduced by the newer revision.
Throughout this reference the following terminology is used consistently.
indicates a mandatory language rule.
indicates prohibited behaviour.
indicates a recommendation.
indicates implementation-defined or optional behaviour.
Examples are informative and are not themselves part of the language specification.
The remainder of this reference is organized as follows.
Appendices provide the complete grammar, keyword list, operator precedence table, and other reference material.
A Buoy program consists of one or more source files containing Unicode text.
During compilation, each source file is transformed into a sequence of lexical elements known as tokens. Tokens are the smallest meaningful elements of the language and include identifiers, keywords, literals, operators, punctuation, and comments.
Except where explicitly stated, whitespace and comments serve only to separate tokens and have no semantic meaning.
Buoy source files are encoded as Unicode text.
The compiler shall recognize the Unicode characters required to represent identifiers, string literals, comments, and source code punctuation.
Implementations should accept UTF-8 encoded source files.
A source file is composed of a sequence of source lines.
A source line may contain:
The significance of line endings is determined by the grammar of the enclosing construct.
Whitespace separates adjacent lexical elements.
Whitespace may appear between any two tokens unless prohibited by the grammar.
The following characters are considered whitespace:
Multiple whitespace characters are equivalent to a single separator unless they occur within a string literal.
Comments are ignored by the compiler except where required for documentation generation.
Comments may appear wherever whitespace is permitted.
The syntax of comments is described in Section 2.11.
Identifiers name language entities including variables, constants, functions, subroutines, types, modules, properties, parameters, and classes.
An identifier consists of a sequence of characters beginning with a valid identifier-start character followed by zero or more identifier characters.
Unless otherwise specified, identifiers are case-sensitive.
The following identifiers are distinct:
Value
value
VALUE
Identifiers shall not be identical to reserved words.
The complete list of reserved words is provided in Appendix A.
Keywords are reserved identifiers having predefined meaning within the language.
Examples include:
If
Then
Else
For
While
Function
Sub
Class
Structure
Interface
Module
Return
Const
Var
Dim
Keywords shall be used only according to the grammar of the language.
A literal denotes a constant value written directly in source code.
Buoy defines literal forms for:
The syntax and semantics of each literal form are described in later chapters.
Operators perform computations upon one or more operands.
Examples include arithmetic, comparison, logical, assignment, and member-access operators.
Punctuation characters delimit grammatical constructs such as parameter lists, array indexing expressions, generic type arguments, and statement blocks.
Operator precedence and associativity are defined in Appendix B.
Keyword spelling is fixed.
Identifier comparison is case-sensitive.
String comparison semantics are determined by the operations performing the comparison and are independent of identifier matching.
Buoy supports documentation comments for generating API documentation.
Documentation comments are associated with the declaration immediately following the comment.
The syntax and processing rules for documentation comments are described in Chapter 27.
A lexical error occurs when the compiler encounters source text that cannot be tokenized.
Examples include:
A conforming compiler shall report the location of the offending source text before terminating compilation.
The lexical structure defined by this chapter establishes the sequence of tokens consumed by the parser.
Subsequent chapters describe how those tokens combine to form declarations, expressions, statements, and complete programs.
A Buoy program consists of one or more source files compiled as a single program or library.
A source file contains declarations that define the program’s types, variables, constants, callable routines, and other program elements. Depending upon the declaration, executable statements may also appear within the source file.
The organization of declarations determines the visibility and lifetime of the entities they introduce.
A compilation unit is the basic unit of source code accepted by the compiler.
Each source file forms a compilation unit.
During compilation, all compilation units participating in a build are parsed, analysed, and combined according to the language’s name-resolution rules.
The order in which source files are presented to the compiler shall not affect the meaning of a valid program.
Programs are organized through declarations.
Typical declarations include:
Each declaration introduces one or more names into a scope.
The precise syntax of each declaration is defined in the corresponding chapter.
A scope is the region of source code within which a declared name is visible.
Every declaration introduces its name into exactly one scope.
Nested declarations create nested scopes.
When multiple declarations use the same identifier, the declaration in the innermost enclosing scope is selected.
Rules governing shadowing and name hiding are described in Chapter 5.
A declaration introduces a program entity.
Depending upon the declaration kind, it may specify:
Some declarations introduce executable code.
Others exist solely to define program structure.
Executable statements describe actions performed when a program runs.
Statements may:
Statements appear only where permitted by the grammar.
A block is a sequence of declarations and statements treated as a single grammatical unit.
Blocks establish nested scopes.
Variables declared within a block cease to exist when execution leaves that block.
Control-flow constructs such as conditional statements and loops typically introduce blocks.
Whenever an identifier appears within a program, the compiler attempts to resolve that identifier to a declaration.
Name resolution considers:
If more than one declaration is applicable, overload resolution or ambiguity rules are applied as appropriate.
Failure to resolve an identifier results in a compile-time error.
Executable programs begin execution at the language-defined entry point.
The exact form of the entry point and its permitted signatures are specified in Chapter 22.
Library projects are not required to define an entry point.
Implementations may compile source files independently.
Regardless of compilation strategy, a conforming implementation shall produce the same observable behaviour as if the entire program had been analysed as a single translation.
Declarations may be annotated with attributes that provide additional information to the compiler or to external tools.
Attributes do not alter the syntax of the language.
The semantics of individual attributes are implementation-defined unless otherwise specified.
Buoy supports conditional compilation to enable source code to be included or excluded according to compile-time conditions.
Conditional compilation directives are processed before syntactic analysis.
The syntax and semantics of these directives are described in Chapter 25.
The structure of a Buoy program is determined entirely by its declarations.
Subsequent chapters define each declaration kind and the rules governing its visibility, lifetime, and behaviour.
Every value in Buoy has a type.
A type defines the set of values that may be represented together with the operations that may be performed upon those values. The compiler uses type information to verify program correctness, resolve overloaded routines, infer the types of expressions, and generate efficient executable code.
Buoy is a statically typed language. Except where explicitly permitted by the language, the type of every expression is determined during compilation.
Type checking is performed at compile time.
A program that attempts an operation not defined for the operand types is ill-formed and shall be rejected by the compiler.
Examples of compile-time type errors include:
The compiler should diagnose type errors before code generation.
The Buoy type system consists of several categories of types.
These include:
Each category is described in a subsequent chapter.
A named type is introduced by a type declaration.
Once declared, the type may be referenced wherever a type specification is permitted.
The scope and visibility of a type are determined by the declaration that introduces it.
Two types are identical if they denote the same declared type or if they are defined by the language to be equivalent.
Distinct declarations introduce distinct types, even when their internal representation is identical, unless otherwise specified by the language.
Type identity is used during assignment, parameter passing, overload resolution, and generic type substitution.
A value may be assigned to a destination only when the source type is compatible with the destination type.
Compatibility may arise through:
The precise compatibility rules are defined throughout this reference.
In certain contexts, the compiler may infer a type rather than requiring it to be written explicitly.
Type inference never changes the meaning of a program. It merely allows the compiler to determine a type that could otherwise have been written by the programmer.
Inference may occur for:
If more than one type satisfies the language rules, the compiler shall report an ambiguity.
Some Buoy types exhibit value semantics.
Assignment of a value type copies the value.
Other types exhibit reference semantics.
Assignment of a reference type copies the reference rather than the referenced object.
The semantic category of each built-in and user-defined type is specified by its declaration.
Every type has an associated default value.
When an object or variable is created without an explicit initializer, the implementation supplies the default value appropriate for its type.
The default value of each built-in type is described in the corresponding chapter.
Generic types define families of related types parameterized by one or more type parameters.
A generic declaration describes the structure and behaviour common to every specialization.
A constructed type is produced by substituting concrete type arguments for the generic parameters.
Generic types are discussed in detail in Chapter 18.
A conversion transforms a value from one type to another.
Conversions may be:
An implicit conversion shall never lose information unless explicitly permitted by the language specification.
Conversions that may fail or lose information require explicit programmer intent.
The primary objective of the Buoy type system is to detect programming errors before execution.
A conforming implementation shall reject any program that violates the static type rules defined by this specification.
Programs accepted by the compiler may assume that every expression has a well-defined compile-time type.
The Buoy type system provides the foundation upon which declarations, expressions, and generic programming are built.
Subsequent chapters describe each category of type together with the operations supported by those types.
Variables and constants associate names with values.
A variable denotes a storage location whose value may change during program execution.
A constant denotes a value that, once established, cannot subsequently be modified.
Both variables and constants are introduced by declarations.
A variable declaration introduces one or more variables into the current scope.
A declaration specifies:
The variable becomes visible immediately after its declaration, subject to the scope rules described in Chapter 3.
The lifetime of a variable is determined by the declaration that introduces it.
Local variables are created when execution enters their enclosing scope and cease to exist when execution leaves that scope.
Member variables exist for the lifetime of the object or type to which they belong.
Static storage duration, if supported, is described in the chapter covering declarations.
A variable may be initialized as part of its declaration.
When an initializer is present, the initializer expression is evaluated before the variable becomes available for subsequent use.
When no initializer is supplied, the compiler provides the default value for the variable’s type, unless the language requires definite assignment before use.
Where permitted, the compiler may infer the type of a variable from its initializer.
For example, if an initializer produces a value of type
Integer, the declared variable acquires that type without
requiring it to be written explicitly.
Type inference affects only the declaration syntax.
The inferred type is fixed once compilation succeeds.
Assignment replaces the current value stored by a variable.
The value being assigned shall be compatible with the variable’s declared type.
Assignment does not alter the variable’s type.
Where a conversion is required, the conversion rules defined in Chapter 4 apply.
A variable shall not be read before a value has been assigned to it.
The compiler performs flow analysis to determine whether every execution path initializes the variable before its first use.
Failure to satisfy the definite assignment rules results in a compile-time error.
A constant declaration introduces a named value that cannot be modified after its initialization.
Every constant shall be initialized exactly once.
The initializer shall satisfy the restrictions imposed by the language for constant expressions.
Attempting to assign a new value to a constant is a compile-time error.
Variables and constants obey the lexical scope rules defined in Chapter 3.
A declaration hides any declaration of the same name in an enclosing scope.
Programs should avoid unnecessary shadowing, as it reduces readability.
When a local variable is referenced by a nested function, delegate, or anonymous routine, the implementation may capture the variable rather than its current value.
The semantics of captured variables are described in the chapter covering delegates and closures.
Variables provide mutable storage during program execution.
Constants provide immutable named values.
Together they form the foundation upon which expressions, statements, and callable routines operate.
An expression specifies a computation that produces a value.
Expressions are formed from literals, variables, constants, function invocations, operators, and other expressions. Every expression has a compile-time type that determines the operations that may be performed upon the resulting value.
Unless otherwise specified, evaluation of an expression does not modify program state.
Expressions include, but are not limited to:
Each expression category is defined in the sections that follow.
A literal expression denotes a constant value written directly within the source code.
Examples include:
The type of a literal is determined by its lexical form and the surrounding context.
A variable reference evaluates to the current value stored in the referenced variable.
The variable shall be visible at the point of reference and shall satisfy the definite assignment rules defined in Chapter 5.
A constant reference evaluates to the value associated with the constant declaration.
The value of a constant cannot change during program execution.
Invoking a function evaluates each argument, transfers control to the function body, and produces the value returned by the function.
Every execution path through a function shall produce a value compatible with the declared return type.
A function invocation is itself an expression and may therefore appear wherever an expression is permitted.
Example:
total = CalculateTotal(price, quantity)
A Sub performs an action but does not produce a value.
Because a Sub has no result, a Sub invocation is a statement rather than an expression.
The following is valid:
WriteReport(customer)
The following is invalid:
total = WriteReport(customer)
Attempting to use a Sub where a value is required is a compile-time error.
Parentheses may be used to control evaluation order or improve readability.
A parenthesized expression has the same type and value as the enclosed expression.
A unary operator acts upon a single operand.
Examples include:
The operand type shall support the selected operator.
A binary operator combines two operands to produce a result.
Categories of binary operators include:
Each operator specifies:
An assignment expression replaces the value stored by a variable.
The right-hand expression is evaluated before the assignment occurs.
The resulting value shall be compatible with the destination type.
Member access selects a member of a composite value.
Members may include:
The referenced member shall be visible and accessible.
Indexing selects an element of an indexed value.
The index expression shall evaluate to a valid index type.
The semantics of indexing are determined by the indexed type.
Unless otherwise specified, operands are evaluated from left to right.
The language guarantees that each operand is evaluated exactly once.
Programs shall not depend upon any alternative evaluation order.
The compiler may evaluate expressions during compilation when every operand is known at compile time.
Compile-time evaluation shall not alter the observable behaviour of a valid program.
Expressions compute values.
Every expression has a well-defined compile-time type, and every operator defines the relationship between its operands and its result.
A statement describes an action performed during program execution.
Statements control the flow of execution, modify program state, invoke routines, and introduce local declarations.
A Buoy program executes by evaluating statements in the order determined by the language’s control-flow rules.
The language defines the following categories of statements:
Additional statement forms may be introduced by future revisions of the language.
A block is a sequence of zero or more statements treated as a single syntactic unit.
Blocks establish a new lexical scope.
Names declared within a block are visible only within that block and any nested blocks unless otherwise specified.
Execution proceeds sequentially from the first statement of the block to the last unless altered by a control-flow statement.
An empty statement performs no action.
An implementation may permit empty statements where the grammar requires a statement but no operation is desired.
Empty statements have no observable effect.
An assignment statement evaluates an expression and stores the resulting value into a variable, property, or other assignable storage location.
The destination shall be writable and the assigned value shall be compatible with the destination type.
The assignment completes only after the right-hand expression has been fully evaluated.
Invoking a Sub performs an action.
Because a Sub does not return a value, its invocation forms a statement rather than an expression.
Examples include:
Execution resumes with the statement immediately following the invocation once the Sub completes.
Conditional statements execute one of several alternative statement sequences depending upon the value of a Boolean expression.
The language provides conditional constructs including:
IfSelectOnly one alternative is executed for each evaluation of the conditional construct.
Iteration statements repeatedly execute a statement or block.
The language provides several iteration constructs appropriate for different programming tasks.
Typical forms include:
Each iteration construct specifies the conditions under which execution begins, continues, and terminates.
Jump statements alter the normal sequential flow of execution.
Examples include:
ReturnExitContinueBreakEach jump statement transfers control to a well-defined destination determined by the enclosing construct.
Jump statements shall not transfer control into a nested scope.
A Return statement terminates execution of the current
Function or Sub.
Within a Function, Return specifies the value produced
by the invocation.
Within a Sub, Return transfers control to the caller
without producing a value.
A Function shall not complete execution without returning a value compatible with its declared return type.
Unless otherwise specified, statements execute in source order.
Each statement completes before execution begins for the following statement.
The language defines additional ordering guarantees for expressions and routine invocations where necessary.
The compiler performs control-flow analysis to determine whether a statement is reachable.
Statements that cannot be executed are considered unreachable.
A conforming implementation may diagnose unreachable statements during compilation.
Statements describe the executable behaviour of a Buoy program.
Expressions compute values.
Statements use those values to perform work, direct control flow, and modify program state.
Functions and Subs are the primary executable units of a Buoy program.
Both define reusable sequences of statements that may accept parameters. A Function computes and returns a value to its caller. A Sub performs an action and does not return a value.
Although Functions and Subs share much of their syntax and behaviour, they represent distinct language constructs and are treated separately throughout this specification.
A Function declaration introduces a named routine that computes and returns a value.
A Function declaration specifies:
The return type forms part of the function declaration and determines the type of every invocation.
A Sub declaration introduces a named routine that performs an action.
A Sub declaration specifies:
Unlike a Function, a Sub has no return type.
Parameters introduce named values that are supplied by the caller when a routine is invoked.
Each parameter has:
Parameters are local to the routine in which they are declared.
A routine is invoked by specifying its name followed by an argument list.
Each argument is evaluated before control transfers to the invoked routine.
Arguments shall satisfy the parameter requirements established by the declaration.
If no applicable declaration exists, the invocation is ill-formed.
A Function returns control and a value to its caller.
Every execution path through a Function shall produce a value compatible with the declared return type.
A Sub returns only control.
Execution of a Sub completes when:
Return statement transfers control to the
caller.Variables declared within a Function or Sub are local to that routine.
Local variables are created upon entry to the routine and destroyed when execution leaves the routine.
Each invocation receives an independent set of local variables.
Multiple Functions or Subs may share the same name provided their parameter lists satisfy the language rules for overload resolution.
Overload resolution is performed entirely at compile time.
The compiler selects the most appropriate declaration according to the argument types supplied by the invocation.
The overload resolution algorithm is described in Chapter 9.
A Function or Sub may declare one or more generic type parameters.
Generic routines describe families of related routines whose behaviour depends upon the supplied type arguments.
Type arguments may be written explicitly or inferred by the compiler when permitted by the language.
Generic programming is described in Chapter 10.
A Function or Sub may invoke itself either directly or indirectly.
Each recursive invocation creates a new execution context with its own parameters and local variables.
The language imposes no restriction upon recursive declarations beyond those required by available execution resources.
The visibility of a Function or Sub is determined by its declaration.
A routine may be visible only within its declaring scope or may be exported for use by other compilation units, according to the applicable accessibility rules.
A Function or Sub may invoke another routine declared elsewhere within the same program.
The compiler resolves routine references during semantic analysis.
The order of routine declarations within source files does not affect program correctness.
A Function and a Sub are distinct declaration kinds.
A Function cannot be substituted for a Sub.
Likewise, a Sub cannot be used where a Function is required to produce a value.
This distinction is enforced during compilation.
Functions compute values.
Subs perform actions.
Together they provide the primary abstraction mechanism for executable behaviour in the Buoy programming language.
Buoy permits multiple Functions and Subs to share the same name, provided that each declaration is distinguishable by its parameter list.
When a routine is invoked, the compiler determines which declaration is intended by applying the overload resolution rules defined in this chapter.
Overload resolution is performed entirely at compile time.
Given a routine invocation, the compiler first constructs the candidate set.
The candidate set consists of every visible Function or Sub having the specified name and which is eligible for invocation in the current context.
Declarations that are inaccessible or otherwise unavailable are excluded.
A candidate is applicable if each supplied argument can be matched with a corresponding parameter.
The compiler considers:
Candidates that fail to satisfy these requirements are discarded.
If more than one applicable candidate remains, the compiler determines which candidate most closely matches the supplied arguments.
Preference is given to candidates requiring fewer or less significant conversions.
If a single best candidate exists, that declaration is selected.
If more than one applicable candidate is equally suitable, the invocation is ambiguous.
An ambiguous invocation is a compile-time error.
The programmer may resolve the ambiguity by:
Generic Functions and Subs participate in overload resolution.
Before comparing candidates, the compiler attempts to infer generic type arguments from the supplied arguments.
A candidate for which type inference fails is not considered applicable.
If more than one generic specialization is applicable, the normal overload resolution rules apply.
Functions and Subs occupy distinct semantic roles.
A Function invocation is valid only where a value is required.
A Sub invocation is valid only as a statement.
Accordingly, overload resolution never substitutes a Sub for a Function, nor a Function for a Sub.
Only declarations visible at the invocation point participate in overload resolution.
Hidden or inaccessible declarations are ignored.
Routine selection is completed before program execution.
The selected declaration remains fixed for that invocation.
Runtime behaviour does not influence overload selection except where explicitly specified by later language features.
Overload resolution enables related routines to share a common name while preserving compile-time type safety.
The compiler selects a single declaration using the argument list, parameter types, type inference, and the rules defined in this chapter.
Generic programming enables algorithms and data types to operate upon values whose types are specified when the generic entity is used rather than when it is declared.
A single generic declaration therefore describes a family of related declarations.
Generic programming promotes type safety, code reuse, and compile-time verification without sacrificing performance.
Generic facilities are integrated throughout the Buoy language and are not limited to a particular category of declaration.
A declaration is generic when it introduces one or more type parameters.
Type parameters represent placeholder types whose concrete values are supplied or inferred when the declaration is used.
The language permits generic:
Additional declaration kinds may support generic parameters where explicitly specified.
Each type parameter introduces a symbolic name representing an unknown type.
Within the generic declaration, the type parameter may appear wherever a type specification is permitted.
Each distinct type parameter represents an independent type.
Type parameter names obey the normal rules governing identifier visibility and scope.
A constructed declaration is produced by substituting concrete type arguments for every type parameter.
Each unique set of type arguments identifies a distinct specialization.
The compiler shall ensure that every substituted type satisfies the requirements imposed by the generic declaration.
Generic routines behave exactly like ordinary routines except that one or more types remain unspecified until invocation.
Generic routines participate fully in overload resolution.
Type arguments may be supplied explicitly or inferred by the compiler.
Where sufficient information is available, the compiler shall infer generic type arguments automatically.
Type inference considers:
The inferred types shall produce the same semantics as if they had been written explicitly.
Failure to infer a unique type is a compile-time error.
A programmer may explicitly specify generic type arguments.
Explicit type arguments override inference.
Every supplied type argument shall satisfy the requirements of the corresponding type parameter.
Members declared within a generic type may reference the enclosing type parameters.
A member may also declare additional generic parameters independent of those declared by the enclosing type.
The scopes of enclosing and local type parameters are distinct.
A generic declaration may contain additional generic declarations.
Inner declarations may reference both their own type parameters and those declared by enclosing declarations, subject to the normal visibility rules.
Generic programming preserves the static type guarantees of the language.
Every specialization is type checked according to the same language rules as a non-generic declaration.
No operation permitted within a generic declaration may violate the type safety of a valid specialization.
The language specification defines only the observable behaviour of generic declarations.
A conforming implementation may generate specialized code, shared code, or any other equivalent representation provided the externally observable behaviour remains unchanged.
Generic programming allows algorithms and data structures to be expressed independently of the concrete types upon which they operate.
Generic declarations are fully integrated with the Buoy type system, overload resolution, and compile-time type inference.
A class defines a user-defined reference type that combines data and executable behaviour into a single declaration.
A class may declare:
A class declaration introduces a new named type.
A class declaration specifies:
The class body contains the declarations that define the state and behaviour of instances of the class.
An instance is an object created from a class.
Each instance contains its own copy of the instance state declared by the class.
Member Functions and Subs operate upon a particular instance unless declared otherwise.
The mechanism used to create instances is described in Section 11.7.
Members define the capabilities of a class.
A class may contain:
Each member has its own accessibility, lifetime, and semantics.
The grammar for each member category is described in the corresponding chapter.
Member visibility determines where a member may be referenced.
The language defines accessibility rules that govern the visibility of declarations both within and outside the declaring class.
Accessibility is enforced during compilation.
Member Functions and Subs are invoked through an instance of the class unless declared as type members.
Invocation follows the same overload resolution and parameter matching rules defined for all routines.
The only distinction is that the invocation occurs within the context of a particular object.
An object is created by invoking one of the constructors defined by the class.
Construction allocates storage for the new instance and performs any initialization required by the class.
If multiple constructors are declared, overload resolution determines which constructor is invoked.
The construction process completes before the resulting object becomes available to the caller.
The lifetime of an object begins when construction completes.
The mechanism by which object storage is reclaimed is implementation-defined.
Programs shall not depend upon any particular memory management strategy unless explicitly documented by the implementation.
A class may declare one or more generic type parameters.
Each specialization of a generic class represents a distinct constructed type.
Generic classes participate fully in the generic programming facilities described in Chapter 10.
A class may contain additional class declarations.
Nested classes obey the normal rules governing visibility, accessibility, and generic parameter scope.
Classes define reference types that combine state and behaviour.
They provide the primary mechanism for representing complex program entities whose lifetime extends beyond a single routine invocation.
12.1 General
A Structure defines a user-defined value type.
A Structure groups related values into a single composite type while
retaining value semantics. Assignment of a Structure copies the value
represented by the Structure rather than creating an additional
reference to the same object.
Structures are appropriate for representing values that possess identity
only through their contents.
12.2 Structure Declarations
A Structure declaration introduces a new named value type.
A Structure may declare fields, properties, constants, Functions, Subs,
constructors, nested declarations, and generic parameters.
12.3 Value Semantics
Variables of Structure type contain the Structure value directly.
Assigning one Structure variable to another copies the complete value.
Subsequent modification of either variable does not affect the
other.
Example:
Structure Point
X As Integer
Y As Integer
End Structure
Dim p1 As Point
Dim p2 = p1
After the assignment, p1 and p2 represent independent values.
12.4 Members
A Structure member behaves according to the same language rules
governing members of a Class unless otherwise specified.
12.5 Construction
A Structure instance is created according to the construction rules
defined for Structure types.
12.6 Passing Structures
When a Structure is supplied as an argument, the parameter passing
mechanism determines whether the value is copied or whether the called
routine operates directly upon the supplied value.
12.7 Generic Structures
Structures may declare generic parameters.
12.8 Summary
Structures provide efficient composite value types while preserving the
static type guarantees of the language.
13.1 General
An Interface specifies a contract.
Rather than describing how an operation is performed, an Interface
specifies which operations shall be provided by any type implementing
the Interface.
13.2 Interface Declarations
An Interface may declare Functions, Subs, properties, constants and
nested declarations.
13.3 Implementation
A Class or Structure may implement one or more Interfaces.
A type implementing an Interface shall provide every required member
defined by that Interface.
13.4 Interface Variables
A variable whose type is an Interface may reference any object
implementing that Interface.
13.5 Generic Interfaces
Interfaces may declare generic parameters.
13.6 Interface Compatibility
Compatibility between an Interface and an implementing type is
determined entirely during compilation.
13.7 Summary
Interfaces define behaviour independently of implementation.
14.1 General
An Enumeration defines a distinct type consisting of a finite set of
named values.
14.2 Enumeration Declarations
An Enumeration declaration introduces the Enumeration type and one or
more enumerators.
14.3 Enumerator Values
Each enumerator possesses an associated value.
14.4 Type Safety
An Enumeration is a distinct type.
14.5 Comparison
Enumeration values of the same Enumeration type may be compared.
14.6 Summary
Enumerations define finite sets of named constants and improve both
readability and type safety.
A Delegate defines a type that represents a callable routine.
Unlike a Function or Sub declaration, which defines executable code, a Delegate describes the signature of a routine. A Delegate value may refer to any Function or Sub whose signature is compatible with the Delegate declaration.
Delegates enable routines to be passed as arguments, stored in variables, returned from Functions, and invoked indirectly.
A Delegate declaration introduces a new delegate type.
A Delegate specifies:
A Delegate declaration does not provide an implementation.
A routine is compatible with a Delegate when:
Compatibility is determined during compilation.
A variable of Delegate type contains a reference to a callable routine.
The referenced routine may be invoked through the Delegate exactly as though it had been invoked directly.
The semantics of the invocation remain unchanged.
Invoking a Delegate transfers control to the routine currently represented by the Delegate.
Arguments are evaluated according to the normal routine invocation rules.
For a Function Delegate, invocation produces a value.
For a Sub Delegate, invocation performs an action and produces no value.
A Delegate may declare one or more generic type parameters.
Generic Delegates participate fully in the generic programming facilities of the language.
Type inference may be applied where sufficient information is available.
A Delegate variable may be assigned any compatible routine.
Attempting to assign an incompatible routine is a compile-time error.
A Delegate remains valid for as long as the referenced routine remains valid according to the execution model of the language.
The precise representation of Delegate values is implementation-defined.
Delegates provide type-safe references to executable routines.
They form the foundation for callback-based programming, event handling, and higher-order algorithms.