On this page:
class
interface
class.together
extends
extends
implements
nonfinal
final
final
field
method
property
override
method
property
override
private
private
abstract
abstract
this
super
internal
internal
constructor
expression
binding
annotation
expression
annotation
opaque
prefab
authentic
with
reconstructor
reconstructor_  fields
primitive_  property
primitive_  property
8.12

6.8 Classes and Interfaces🔗ℹ

definition

class id_name(field_spec, ...)

 

definition

class id_name(field_spec, ...):

  class_clause_or_body_or_export

  ...

 

field_spec

 = 

modifiers id maybe_annot maybe_default

 | 

keyword: modifiers id maybe_annot maybe_default

 | 

keyword maybe_default

 

modifiers

 = 

private

 | 

mutable

 | 

private mutable

 | 

ϵ

 

maybe_annot

 = 

:: annot

 | 

:~ annot

 | 

ϵ

 

maybe_default

 = 

= default_expr

 | 

: default_body; ...

 | 

ϵ

 

class_clause_or_body_or_export

 = 

class_clause

 | 

body

 | 

export

 

class_clause

 = 

field id maybe_annot = expr

 | 

field id maybe_annot: body; ...

 | 

private field id maybe_annot = expr

 | 

private field id maybe_annot: body; ...

 | 

method method_impl

 | 

override method_impl

 | 

final method_impl

 | 

private method_impl

 | 

abstract method_decl

 | 

property property_impl

 | 

extends id_name

 | 

implements implements_decl

 | 

private implements implements_decl

 | 

final

 | 

nonfinal

 | 

internal id

 | 

constructor constructor_decl

 | 

expression expression_decl

 | 

binding binding_decl

 | 

annotation annotation_decl

 | 

reconstructor reconstructor_impl

 | 

reconstructor_fields reconstructor_fields_decl

 | 

dot dot_decl

 | 

static_info static_info_decl

 | 

opaque

 | 

prefab

 | 

primitive_property primitive_property_decl

 | 

other_class_clause

Binds id_name as a class name in several spaces:

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 can only be accessed by . within methods and properties of the class or through an identifier bound by an internal form. 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 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 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 priviate 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 must be distinct from all non-private field, method, property, and dot-syntax names (which are automatically exported from the class in its role as a namespace). 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 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, but it cannot refer to this, fields of the class, methods of the class, or properties of the class. All fields added through a field clause are mutable, and they can be updated in a custom constructor (form example) using assignment operators such as :=. The field can appear any number of times as a class_clause, with or without a private prefix.

When a class_clause is a method form, override form, abstract form, method-shaped final or private form, or property 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 makes the interface privately implemented; see interface for information on privately implementing an interface. A class_clause can have any number of implements clauses (with or without private). 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 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 procedure is accessed from a class (as a namespace) via ., the procedure 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.

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):

    nonfinal

> class Posn3D(z):

    extends Posn2D

> Posn3D(1, 2, 3)

Posn3D(1, 2, 3)

> class Rectangle(w, h):

    nonfinal

    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)

definition

interface id_name

 

definition

interface id_name:

  interface_clause_or_body_or_export

  ...

 

interface_clause_or_body_or_export

 = 

interface_clause

 | 

body

 | 

export

 

interface_clause

 = 

method method_impl

 | 

override method_impl

 | 

final method_impl

 | 

private method_impl

 | 

abstract method_decl

 | 

property property_impl

 | 

extends extends_decl

 | 

internal internal_decl

 | 

expression expression_decl

 | 

annotation annotation_decl

 | 

dot dot_decl

 | 

static_info static_info_decl

 | 

primitive_property primitive_property_decl

 | 

other_interface_clause

Similar to class for defining classes, but defines an interface, which has no fields but can have multiple superinterfaces. A method, property, or override clause is allowed to have just a name or omit the body, in which case abstract implicitly prefixes the declaration.

The body of an interface form has interface clauses that are similar to class clauses, but declared separately and sometimes with different syntax. For example, extends as an interface clause supports multiple superinterface names instead of just one, and extends can appear multiple times in an interface body.

Interfaces cannot be instantiated. They are implemented by classes via the implements form. When a class implements an interface (not privately), it has all methods and properties of the interface, and its instances satisfy the interface as an annotation.

Typically, an interface declares methods and properties with abstract to be implemented by classes that implement the interface. However, an interface can define method and property implementations; those implementations are inherited by classes that implement the interface or any subinterface that extends the interface. An interface can also have private helper methods and properties, but they are useful only when an interface also has implemented public methods or properties that refer to them.

When a class implements an interface privately using private implements, its instances do not satisfy the interface as an annotation. If the privately implemented interface has an internal name declared with internal, however, instances satisfy the internal name as an annotation. Methods and properties of a privately implemented instance can be accessed only with static . via the internal-name annotation. As long as a method or property belongs to only privately implemented interfaces, it can be overridden with private override, otherwise it is overidden normally. If a class declares the implementation of a interface both normally and privately, then the interface is implemented normally. Abstract private methods and properties must be implemented immediately in the class that privately implements the associated interface.

When a class or interface extends or implements multiple interfaces that provide a method or property with the same name, the method or property implementation must be the same for all interfaces. That is, the method or property must be abstract, the implementation must reside in a shared superinterface of the interfaces, or the method or property must be overridden in the implementing class. Overriding applies to same-named methods or properties of all interfaces.

definition

class.together:

  class_or_interface

  ...

 

class_or_interface

 = 

class class_decl

 | 

interface interface_decl

Defines the same bindings as the class_or_interfaces, but with an indirection on annotations so that the defined class and interface names can be used as field, method, and property-result annotations and in all of the other class and interface declarations.

The class.together form expands to a combination of namespace, annot.delayed_declare, and annot.delayed_complete declarations.

> class.together:

    class Tree(x :: List.of(Node))

    class Node(val, children :: Tree)

> class.together:

    class Even():

      nonfinal

      abstract method get_next() :: Odd

    class Odd():

      nonfinal

      abstract method get_next() :: Even

class clause

extends id_name

 

class clause

extends: id_name

 

interface clause

extends id_name

 

interface clause

extends: id_name ...; ...

A class clause recognized by class to define a class that is a subclass of the one named by id_name, and an interface clause recognized by interface to define an interface that is a subinterface of the ones named by the id_names.

class clause

implements id_name ...

 

class clause

implements: id_name ...; ...

A class clause recognized by class to define a class that implements subclasses named by id_names. See class and interface.

class clause

nonfinal

As a class clause, nonfinal is recognized by class so that the new class is not final (as it would be by default).

class clause

final method_impl

 

class clause

final method method_impl

 

class clause

final override method_impl

 

class clause

final override method method_impl

 

class clause

final property property_impl

 

class clause

final override property property_impl

 

interface clause

final method_impl

 

interface clause

final method method_impl

 

interface clause

final override method_impl

 

interface clause

final override method method_impl

 

interface clause

final override method method_impl

 

interface clause

final property property_impl

 

interface clause

final override property property_impl

The final form as a class clause or interface clause is followed by a method or property declaration. In that case, the method or property is final, even if the enclosing class is not (and an interface is never final). A final method or property cannot be overridden in subclaseses or subinterfaces. Using final with an immediate declaration is the same as final followed by method. Including override means that the method or proerty must be defined in the superclass or a superinterface, while it must not be defined in the superclass or a superinterface if override is not used.

class clause

field id maybe_annot = expr

 

class clause

field id maybe_annot: body; ...

A class clause recognized by class to add fields to the class. The expr or body block is evaluated each time the class is instantiated, but it cannot refer to this or fields, methods, or properties of an object. See class for more information.

class clause

method method_impl

 

class clause

property property_impl

 

class clause

override method_impl

 

class clause

override method method_impl

 

class clause

override property property_impl

 

interface clause

method method_decl

 

interface clause

method method_impl

 

interface clause

property property_decl

 

interface clause

property property_impl

 

interface clause

override method_impl

 

interface clause

override method_decl

 

interface clause

override method method_impl

 

interface clause

override method method_decl

 

interface clause

override property property_impl

 

interface clause

override property property_decl

 

method_impl

 = 

id maybe_res_annot: entry_point

 | 

id case_maybe_kw_opt

 | 

 | id case_maybe_kw

 | ...

 

method_decl

 = 

id maybe_res_annot

 | 

id (bind_maybe_kw_opt, ..., rest, ...) maybe_res_annot

 | 

 | id (bind_maybe_kw_opt, ..., rest, ...) maybe_res_annot

 | ...

 

property_impl

 = 

id maybe_res_annot: body; ...

 | 

 | id maybe_res_annot: body; ...

 | 

 | id maybe_res_annot: body; ...

 | id := binding: body; ...

 

property_decl

 = 

id maybe_res_annot

 | 

 | id maybe_res_annot

These class clauses and interface clauses are recognized by class and interface to declare methods and properties, along with the method and property forms of final and private. 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.

In an interface, a method, override, or property declation can be just an identifier, or it can omit a body block. In that case, method, override, or property is treated as if abstract is added before. If arguments are declared for an abstract method, they determine the method’s expectations for static argument-count checking (see use_static), but they do not impose constraints on overriding implementations. When a property_decl uses the single-case | form, it declares the property as not supporting assignment; that declaration is not enforced on implementations of the property, but it affects static resolution of a property assignment.

class clause

private implements id_name ...

 

class clause

private implements: id_name ...; ...

 

class clause

private field field_decl

 

class clause

private method_impl

 

class clause

private method method_impl

 

class clause

private property property_impl

 

class clause

private override method_impl

 

class clause

private override method method_impl

 

class clause

private override property property_impl

 

interface clause

private method_impl

 

interface clause

private method method_impl

 

interface clause

private property property_impl

 

interface clause

private override method_impl

 

interface clause

private override method method_impl

 

interface clause

private override property property_impl

A class clause that declares interfaces that are privately implemented (see interface), a class clause that declares a private field, or a class clause or interface clause that declares a private method or property. See class, interface, and method for more information on field, method, and property declarations. A private without implements, field, method, override, or property is equivalent to private followed by method.

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 identfier’s annotation.

class clause

abstract method_decl

 

class clause

abstract method method_decl

 

class clause

abstract override method_decl

 

class clause

abstract property property_decl

 

class clause

abstract override property property_decl

 

interface clause

abstract method_decl

 

interface clause

abstract method method_decl

 

interface clause

abstract override method_decl

 

interface clause

abstract property property_decl

 

interface clause

abstract override property property_decl

A class clause or interface clause that declares a method or property without an implementation. See method for the shape of method_decl, and see property for the shape of property_decl.

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 overidden method or property is already abstract, an abstract override can be useful to impose an additional result annotation.

expression

this

The this form can only be used within a method or property. See method for more information.

expression

super . id(arg_expr, ...)

 

expression

super

The super form can only be used in two places: within a method property call to invoke another method or property id that is statically known to be implemented in a superclass or superinterface of the enclosing class; or within a custom constructor to as a reference to an underlying constructor. In the case of a method call or property use, the super call invokes the superclass’s or superinterface’s implementation, even if a method or property named id is overridden in a class, interface, or a subclass.

class clause

internal id

 

class clause

internal: id

 

interface clause

internal id

 

interface clause

internal: id

A class clause or interface clause recognized by class and interface to bind id to the class or interface’s representation. See class, interface, and constructor for more information.

When used as a namespace, id can access the immediate private 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 fields, methods and properties, but only with . as statically resolved.

class clause

constructor maybe_name: entry_point

 

class clause

constructor maybe_name case_maybe_kw_opt

 

class clause

constructor

| maybe_name case_maybe_kw

| ...

 

class clause

expression: entry_point

 

class clause

expression 'id pattern ...': 'template ...'

 

class clause

expression

| 'id pattern ...': 'template ...'

| ...

 

class clause

binding: entry_point

 

class clause

binding 'id pattern ...': 'template ...'

 

class clause

binding

| 'id pattern ...': 'template ...'

| ...

 

class clause

annotation: entry_point

 

class clause

annotation 'id pattern ...': 'template ...'

 

class clause

annotation

| 'id pattern ...': 'template ...'

| ...

 

maybe_name

 = 

id

 | 

ϵ

These class clauses are recognized by class to replace the default constructor, expression form, binding form, or annotation form. For constructor, the second two forms are shorthand for using a fun entry point. For each of expression, binding, and annotation, the pattern and template shorthands are similar to using a macro entry point, but an id must be presented instead of () for the pattern, and id must match the name of the class being defined. When annotation is not present, in addition to the class name being bound as a default annotation, an of annotation constructor is exported as a field of the class.

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 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 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 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.

interface clause

expression: expresssion_decl

 

interface clause

annotation: annotation_point

These interface clause forms have the same syntax and analogous meaning as the expression and annotation class clauses.

There is no constructor for interfaces, since interfaces cannot be instantiated directly, but an expression clause can make an interface identifier behave like a constructor, perhaps instantiating some default class. There is no binding for interfaces, because interface does not otherwise define an interfeace name for binding, and so bind.macro can be used alongside interface with the same interface name.

class clause

opaque

When a class clause is opaque, then the default printed form of a class instance does not show fields, and instead prints ... in parentheses after the class name.

class clause

prefab

When a class clause is prefab, then the representation of an instance depends only on the class name, the number of fields in the class, and the mutability of each field. When two class desclarations have prefab, the same field count, and the same mutability of each field, then instances from one class declaration count as instances of the other class declaration, even if they have different annotations on the fields, different methods, and so on. That sharing implies a number of constraints:

class clause

authentic

When a class clause is authentic, then the new class cannot be chaperoned or impersonated. At most one class clause in a class form can be authentic.

expression

obj with (id = expr, ...)

Performs a functional update of the object produced by obj by creating a new instance of the same class, using the values of obj’s fields for the new object except as replaced by fields.

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 syntacticaly 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)

> def p = Posn(1, 2)

> p with (x = 10)

Posn(10, 2)

> p

Posn(1, 2)

class Posn(x, y):

  nonfinal

class Posn3D(z):

  extends Posn

  // same as the default reconstructor:

  reconstructor (x, y, z = this.z):

    Posn3D(x, y, z)

> def p = Posn3D(1, 2, 3)

> (p :: Posn) with (x = 10)

Posn3D(10, 2, 3)

class PosnX(x, y):

  internal _PosnX

  expression 'PosnX< $x ... || $y ... >':

    '_PosnX($x ..., $y ...) :~ PosnX'

  reconstructor (x, y):

    PosnX< x || y >

> def px = PosnX< 1 || 2 >

> px with (y = 20)

PosnX(1, 20)

> px

PosnX(1, 2)

class PosnD(x, y):

  reconstructor_fields:

    x: this.x

    y: this.y

    delta: 0

  reconstructor (x, y, delta):

    PosnD(x+delta, y+delta)

> PosnD(1, 2) with (delta = 10)

PosnD(11, 12)

class clause

reconstructor: entry_point

 

class clause

reconstructor case_maybe_kw_opt

 

class clause

reconstructor

| case_maybe_kw

| ...

A form for class to provide an implementation of a functional-update reconstructor via with. The implementation is similar to a method, in that this is bound to the object being updated (i.e., the object whose fields are being used to create a new instance of the class).

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 reconstuctor.

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

reconstructor_fields:

  field_id: body; ...

  ...

Declares fields available to be supplied to a with functional-update operation for instances of a class, instead of fields declared in the enclosing class, but in addition to the fields that are allowed for the class’s superclass (if any).

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

primitive_property expr: body; ...

 

interface clause

primitive_property expr: body; ...

A class clause or interface clause that bridges Rhombus and Racket protocols. When used as a class clause, the enclosing class implements the primitive, Racket-level property produced by expr with the value produced by body. When used as an interface clause, any class that implements the interface will implement the primitive property with the body value.

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 inthe 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.

import lib("racket/base.rkt")

class NamedPosn(name, x, y):

  primitive_property base.#{prop:object-name}:

    fun (self): self.name

> base.#{object-name}(NamedPosn("Rumpelstiltskin", 1, 2))

"Rumpelstiltskin"