6.1 Classes
definition | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
definition | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
In the expr space, id_name a constructor function or form, which by default is a function that takes as many arguments as the supplied non-private/protected field_specs in parentheses, and it returns an instance of the class;
in the annot space, an annotation, which is satisfied by any instance of the class, and by default an annotation constructor id_name.of or id_name.now_of, which default takes as many annotation arguments as supplied non-private/protected field_specs in parentheses; the name is id_name.of if all such field_specs are for immutable fields;
in the bind space, a binding-pattern constructor, which by default takes as many patterns as the supplied non-private/protected field_specs in parentheses and matches an instance of the class where the fields match the corresponding patterns;
in the namespace space, a namespace to access exported bindings as well as (if not replaced by an export) a function id_name.method, a function id_name.property, a syntactic form id_name.dot, and a field accessor id_name.field for each non-private/protected method, property, dot syntax, and field in the class (including inherited methods, properties, dot syntax, and fields), respectively; and
in the class space, a representation of the class for reference as a superclass.
Fields, methods, properties, and dot syntax declared in a class can be accessed from an object (as opposed to just a class) using ., but fields, methods, and properties declared as private or protected can only be accessed by . within methods and properties of the class or through an identifier bound by an internal form. Fields, methods, and properties declared as protected can be accessed in subclass methods, in addition. In static mode (see use_static), a non-property method must be called like a function; in dynamic mode, a method accessed from an object closes over the object. Private fields, methods, and properties can be accessed with . only statically. Syntactic forms bound via dot can be accessed with . only statically, and functional update via with also relies on static access.
A field_spec has an identifier, keyword, or both. A keyword implies that the default constructor expects the corresponding argument as a keyword argument instead of a by-position argument. The default annotation and binding pattern similarly expect a keyword-tagged subform instead of a by-position form for the corresponding fields. The name of the field for access with . is the identifier, if present, otherwise the name is the symbolic form of the keyword. When a field_spec has the private or protected modifier, however, then it is not included as an argument for the default constructor, binding form, or annotation form.
When a default-value expression or block is provided for a field after = or :, then the default constructor evaluates the default_expr or default_bodys to obtain a value for the argument when it is not supplied. If a by-position field has a default-value expression or block, then all later by-position fields must have a default. If the class extends a superclass that has a non-private/protected by-position argument with a default, then all by-position arguments of the subclass must have a default. A default_expr or default_body can refer to earlier field names in the same class to produce a default value. If a private field_spec lacks a = and default-value expression, then a custom constructor must be declared with constructor.
If a block follows a class form’s field_spec sequence, the block contains a mixture of definitions, expressions, exports, and class clauses. A class clause adjusts the class and bindings created by the class form; it be one of the predefined clause forms, or it can be a macro that ultimately expands to a predefined form. Definitions and expressions in a class block are evaluated at when the class form is evaluated, and not when an instance is created. Definitions are scoped to the block for potential use by class clauses, but a class form is analogous to namespace in that local definitions can be exported. Exported names can replace non-private field, method, and property names, which are otherwise exported automatically, but exported names must be distinct from dot-syntax names. Since the definitions and expressions of a class body must be processed to find class clauses in the body, the class is not available for use until after the definitions and expressions, as if the definitions and expressions appeared before the class form.
When a class_clause is a field or immutable form, then an additional field is added to the class, but the additional field is not represented by an arguments to the default constructor, annotation form, or binding-pattern form. Instead, the expr or body block the field gives the added field its initial value; that expression or block is evaluated each time an instance of the class is created, and it can refer to field_spec names and earlier field names, but it cannot refer to this, later fields of the class, methods of the class, or properties of the class. All fields added through a field clause without immutable are mutable, and they can be updated in a custom constructor (form example) using assignment operators such as :=. The field or immutable form can appear any number of times as a class_clause, with or without a private or protected prefix.
When a class_clause is a method form, override form, abstract form, property form, or method- or property-shaped final, private, or protected form, then the clause declares a method or property for the class. These clauses can appear any number of times as a class_clause to add or override any number of methods or properties. See method for more information on methods and properties.
When a class_clause is an extends form, the new class is created as a subclass of the extended class. The extended class must not be final. At most one class_clause can have extends.
When a class_clause is an implements form, the new class is created as an implementation of the named interfaces. Like a superclass, an interface can supply method and property implementations (that can be overridden) and have abstract methods and properties, but an interface does not have fields; see interface for more information. Prefixing implements with private or protected makes the interface privately or protectedly implemented, and the interface’s methods are private or protected; see interface for information on privately or protectedly implementing an interface. A class_clause can have any number of implements clauses (with or without private and protected). Any rule that applies to the superinterface of an interface also applies to the implemented interfaces of class, as well as any superinterface of those interfaces.
Unless some class_clause is nonfinal, then the new class is final, which means that it cannot have subclasses. When a class_clause is nonfinal, then the new class is not final. At most one class_clause can have nonfinal.
When a class_clause is an internal form, then the clause’s id is bound in similar ways as the main class id_name: as a constructor, annotation form, binding pattern form, and namespace. A use of the internal id as a constructor creates an instance of the same class, but the constructor expects arguments for all fields declared with field_specs, including private and protected fields. For more information on internal names, see constructor, since the details of internal names are closely related to constructor, annotation, and binding pattern customization. Any number of internal declarations can appear among the class_clauses, which means that multiple internal aliases may be defined.
The class_clause forms constructor or expression, binding, and annotation replace default meanings of the defined id_name for an expression context, binding context, and annotation context, respectively. The reconstructor form with optional reconstructor_fields replaces the way that with functional update is implemented. The dot form (which must be imported through rhombus/meta) replaces the way that . accesses are resolved for expressions that have the class’s annotation. The static_info form (which must be imported through rhombus/meta) adds static information for the class’s instances. See constructor, expression, binding, annotation, reconstructor, dot, and static_info for more information on those forms.
When a method function is accessed from a class (as a namespace) via ., the function expects an extra by-position argument that must be an instance of the class, and the extra argument is supplied before all other arguments. A field accessor from a class (as a namespace) via . similarly takes an instance of the class, and it accepts a second argument to act as a mutator if the field is mutable. A property accessor from a class (as a namespace) via . takes an instance of the class, and it accepts an additional value to assign to the property (if the property supports assignment). Even when a method is accessed via its class instead of an object, if the method and class are not final, the called method is determined by the object and may be from a subclass that overrides the method; the same is true for properties.
Each field, method, property, and dot-syntax name must be distinct from all other field, method, property, and dot-syntax names, whether from a parenthesized field_spec, from a field clause, or from a method, property, or dot-syntax clause. If an extends or implements clause is present, then each name must also be distinct from any name in the superclass or interface, except that a override clause must name a method or property that is already declared in the superclass. Private superclass fields, methods, and properties are not visible to the subclass, so their names are not required to be distinct from subclass field, method, and property names. When a method or property is overridden via override, the original and overriding versions must be both methods or both properties.
A ~name form as an option is analogous to ~name within a fun definition. It specifies a name used for run-time reporting, such as from a constructor or as the prefix on errors from methods.
See Rules for Static Information for information about static information associated with classes.
> class Posn(x, y)
> Posn(1, 2)
Posn(1, 2)
> Posn.x
#<function:Posn.x>
> Posn.x(Posn(1, 2))
1
> Posn(1, 2).x
1
> class Posn3(z):
extends Posn
class: superclass is final and cannot be extended
> class Posn2D(x, y):
> class Posn3D(z):
extends Posn2D
> Posn3D(1, 2, 3)
Posn3D(1, 2, 3)
> class Rectangle(w, h):
constructor (~width: w, ~height: h):
super(w, h)
> class Square():
extends Rectangle
constructor (~side: s):
super(~width: s, ~height: s)()
> Square(~side: 10)
Square(10, 10)
The class.together form expands to a combination of namespace, annot.delayed_declare, and annot.delayed_complete declarations.
class Even():
class Odd():
class clause | |
| |
| |
class clause | |
|
class clause | |
class clause | |
| |
class clause | |
| |
class clause | |
| |
class clause | |
| |
class clause | |
| |
class clause | |
| |
class clause | |
| |
class clause | |
| |
class clause | |
class clause | ||||||||
| ||||||||
class clause | ||||||||
| ||||||||
class clause | ||||||||
| ||||||||
| ||||||||
|
class clause | |||||||||||||||||||||||||
| |||||||||||||||||||||||||
class clause | |||||||||||||||||||||||||
| |||||||||||||||||||||||||
class clause | |||||||||||||||||||||||||
| |||||||||||||||||||||||||
class clause | |||||||||||||||||||||||||
| |||||||||||||||||||||||||
class clause | |||||||||||||||||||||||||
| |||||||||||||||||||||||||
| |||||||||||||||||||||||||
| |||||||||||||||||||||||||
|
The method form is used in documentation with a different shape than in implementation. See Documenting Methods for more information.
These class clauses are recognized by class to declare methods and properties, along with the method and property forms of final, private, and protected. The combination override followed by method is the same as just override.
A method_impl is either an id followed by an optional result annotation and a block containing an entry point, or it has the same form as a fun definition with a form name like method in place of fun. A maybe_res_annot applies to the immediate method implementation as well as overriding implementations in subclasses; a result annotation within an entry point, in contrast, does not apply to subclasses. A maybe_res_annot can specify a converter annotation only if the method is final or the enclosing class is final; the conversion applies before inherited result annotations for the method are checked.
A property clause declares or overrides a property, which is like a method in that using the property evaluates a block. However, the property is used either as an expression, which is analogous to calling a method with no arguments, or as the left-hand side of an assignment operator like := form, which is analogous to calling a method with the right-hand side of := as the method argument. A property always supports a reference form, but it supports assignment only when the property form includes a := case. In a property’s := case, the part after := is a binding analogous to a function-argument binding, and the subsequent body will normally refer to that binding. Using := with a property always produces #void, but more generally, any value returned by the body of a property definition’s := case is ignored by assignment operators. A maybe_res_annot in a property clause applies to overriding implementations in subclasses, but it imposes no constraints on the right-hand part of := or other assignment operators when assigning to a property.
In the body of a method or property, the special expression form this refers to the object whose method was called. Fields (in the case of a class) and methods can be accessed using this and ., but they can also be used directly. Using a field, method, or property name directly is the same as using this and . in static mode (which implies that a direct reference to a method name must be a call of the method). An argument that has the same name as a field, method, or property shadows the field, method, or property.
class clause | |
| |
| |
class clause | |
| |
| |
class clause | |
| |
class clause | |
| |
class clause | |
| |
class clause | |
| |
class clause | |
| |
class clause | |
| |
class clause | |
| |
class clause | |
| |
class clause | |
Private fields, methods, and properties can be accessed only within the body of the enclosing class or through an identifier declared with internal. When referenced via the . operator, only static references are allowed through the enclosing class’s annotation (not a subclass annotation) or through an internal identifier’s annotation.
class clause | |
| |
class clause | |
| |
| |
class clause | |
| |
class clause | |
| |
class clause | |
| |
class clause | |
| |
class clause | |
| |
class clause | |
class clause | |||||||||||||
| |||||||||||||
class clause | |||||||||||||
| |||||||||||||
class clause | |||||||||||||
| |||||||||||||
class clause | |||||||||||||
| |||||||||||||
class clause | |||||||||||||
| |||||||||||||
class clause | |||||||||||||
| |||||||||||||
class clause | |||||||||||||
| |||||||||||||
class clause | |||||||||||||
| |||||||||||||
| |||||||||||||
| |||||||||||||
|
When a class has an abstract method or property, either declared directly or inherited, the underlying constructor for the class throws an exception. The method or property must be overridden with a override class in a subclass, and then the subclass can be instantiated (as long as it has no other abstract methods). A final class cannot have an abstract method or property.
A method or property can be both abstract and override. In that case, if the overridden method or property is not abstract, then the method or property becomes abstract and most be overridden in a subclass before instantiation. Even if the overridden method or property is already abstract, an abstract override can be useful to impose an additional result annotation.
expression | |
When used as a namespace, id can access the immediate private and protected fields, methods, and properties of the class or interface containing the internal declaration. Along similar lines, id as an annotation associates static information with an expression or binding so that . can be used to access private and protected fields, methods and properties, but only with . as statically resolved.
class clause | ||||||||||||||||
| ||||||||||||||||
class clause | ||||||||||||||||
| ||||||||||||||||
class clause | ||||||||||||||||
| ||||||||||||||||
| ||||||||||||||||
class clause | ||||||||||||||||
| ||||||||||||||||
class clause | ||||||||||||||||
| ||||||||||||||||
class clause | ||||||||||||||||
| ||||||||||||||||
| ||||||||||||||||
class clause | ||||||||||||||||
| ||||||||||||||||
| ||||||||||||||||
class clause | ||||||||||||||||
| ||||||||||||||||
class clause | ||||||||||||||||
| ||||||||||||||||
class clause | ||||||||||||||||
| ||||||||||||||||
class clause | ||||||||||||||||
| ||||||||||||||||
| ||||||||||||||||
class clause | ||||||||||||||||
| ||||||||||||||||
class clause | ||||||||||||||||
| ||||||||||||||||
class clause | ||||||||||||||||
| ||||||||||||||||
| ||||||||||||||||
class clause | ||||||||||||||||
| ||||||||||||||||
| ||||||||||||||||
class clause | ||||||||||||||||
| ||||||||||||||||
| ||||||||||||||||
| ||||||||||||||||
|
When a constructor, expression, binding, or annotation clause has a disable_form containing ~error, then the binding of the class name in the corresponding space reports an error for any use. When a disable_form contains ~none, then the class name is not bound in the corresponding space by the enclosing class form. The constructor and expression forms are equivalent when used with a disable_form.
When a class has a constructor form with an empty maybe_name, then a use of new class’s id_name as a constructor function invokes a function the entry point (typically a fun form) in the block after constructor. That function must return an instance of the new class, typically by calling super:
If the new class does not have a superclass, then super accesses the default constructor, which returns an instance of the class. Note that this instance might be an instance of a subclass if the new class is not final.
If the new class has a superclass, then super accesses a curried function. The function accepts the same arguments as the superclass constructor. Instead of returning an instance of the class, it returns a function that accepts arguments as declared by field_specs in the new subclass, including private/protected clause fields; the result of that function is an instance of the new class. Again, the result instance might be an instance of a subclass if the new class is not final.
If a constructor form has an id for maybe_name that is not the same as the enclosing class’s id_name, then the constructor is bound to id instead of id_name. Typically, naming a constructor is paired with an expression declaration that refers to that constructor.
If a class has an internal clause, then the bound name acts as a constructor like super, except that it always instantiates the class that contains the internal clause (so, the internal name is not a substitute for using super in a custom constructor). If a superclass has a custom constructor, the default constructor of a subclass assumes that the superclass constructor accepts the same argument as the default superclass constructor.
When a class has a expression form, then a use of the new class’s id_name as an expression invokes the entry point (typically a macro form) in the block after expression. The entry_point is a meta-time expression. This macro replaces the default meaning of the id_name as a reference to the constructor. When expression, then the default id_name.of annotation constructor accepts only predicate annotations.
When a class has a binding form, then a use of the new class’s id_name as a binding-pattern constructor invokes the entry point (typically a macro form) in the block after binding. The entry_point is a meta-time expression. There is no super for custom binding patterns; instead, use internal to bind an internal name that acts similar to the class’s default binding form, but with two differences: it does not expect bindings for superclass fields, but it does expect bindings for private and protected fields declared with a field_spec. When a class has a superclass, then a custom binding form is typically implemented using an internal binding form, the superclass’s binding form, and the && binding operator. When a superclass has a custom binding form, then a class must have a custom binding form, too (unlike the case with constructors, where a default constructor still can be generated to call a custom superclass constructor).
When a class has an annotation form, then a use of new class’s id_name in a annotation invokes the entry point (typically a macro form) in the block after annotation. The entry_point is a meta-time expression. Similar to custom binding forms, a custom annotation form normally needs an internal annotation name bound with internal; the of form of that annotation expects annotations for only immediate fields of the class, but including private and protected ones declared with field_specs. Use the && annotation operator to combine the internal annotation with a superclass annotation. When a superclass has a custom annotation form, then a class must have a custom annotation form, too. Typically, an of annotation is also defined and exported from the class’s namespace to go along with the customization of the class name as an annotation.
class clause | |
class clause | |
A prefab class’s fields and methods are accessible from an instance expression followed by . only when the reference is statically resolved. That is, dynamic . lookup will fail to find a field or a method.
If a prefab class has a superclass, the superclass must also be prefab.
A prefab class is implicitly nonfinal, and it cannot be opaque or authentic.
When a field is declared with a field_spec that has a :: annotation, a field value is checked against the annotation only when an instance is created through a constructor from the class declaration, and not when the field is accessed. The field access is effectively treated as having a :~ annotation.
class clause | |
expression | ||
| ||
|
The listed ids must all correspond to fields of the object that would be represented by a default constructor, independent of whether the field is optional or would be supplied with a keyword. The fields are checked dynamically, unless with is static (see use_static), in which case the set of fields must syntactically match the ones expected for the class indicated by static information.
An object is updated by calling its class’s reconstructor. A default reconstructor for a non-abstract class without a custom constructor is implemented by calling the class’s constructor. A class declaration can contain an reconstructor clause to replace the default implementation or supply an implementation when a default is not available.
class Posn(x, y)
class Posn(x, y):
class Posn3D(z):
extends Posn
// same as the default reconstructor:
reconstructor (x, y, z = this.z):
Posn3D(x, y, z)
class PosnX(x, y):
internal _PosnX
reconstructor (x, y):
class PosnD(x, y):
delta: 0
reconstructor (x, y, delta):
class clause | ||||||||
| ||||||||
class clause | ||||||||
| ||||||||
class clause | ||||||||
| ||||||||
| ||||||||
class clause | ||||||||
| ||||||||
|
By default, a class’s reconstructor should expect as many arguments as the class has fields, and it should expect them in the declared order. If the class has a reconstructor_fields declaration, the reconstructor should instead expect those fields in addition to the ones that the superclass (if any) expects for its reconstructor.
A reconstructor should not expect keyword arguments; all fields are supplied by-position. If the class has a superclass, then any fields added by the class should be made optional, because those arguments will not be supplied when an instance of the class is updated based on static information corresponding to the superclass. The optional arguments typically have a default value that is drawn from the corresponding field of this.
See with for examples.
class clause | |||
|
The body sequence for a field is evaluated for a use of with that does not supply the field. The body sequence can refer to this or other bindings that would be available in a 0-argument method of the class.
When a class has a reconstructor_fields declaration, then the class and any subclass that extends it must have a reconstructor declaration, since there is not necessarily any connection between the declared reconstructor fields and the constructor’s arguments.
See with for an example.
class clause | |
|
If the class is not final, then any subclass of the class by default implements the property with the same value, but it can specify the same property to produce a different value that replaces the one produced by body. Implementing an interface with implements, meanwhile, corresponds to writing primitive_property for each primitive property in the interface and its superinterfaces, which means that those properties cannot be immediately overridden with primitive_property.
The expr and body are evaluated in order relative to surrounding expr and defn forms.
class NamedPosn(name, x, y):
primitive_property base.#{prop:object-name}:
> base.#{object-name}(NamedPosn("Rumpelstiltskin", 1, 2))
"Rumpelstiltskin"