Skip to content

Kinds and nominal conformance

Every E# type has a kind. Kind controls copying, identity, construction, nullability, and CLR interoperability; it is not inferred from how an individual value happens to be allocated.

TypeKind = StructType | ClassType | UnionType | RefUnionType | EnumType
| InterfaceType | DelegateType | PointerType | PrimitiveType | TupleType .
StructType = "struct" TypeName [ TypeParameters ] .
ClassType = [ "open" | "abstract" ] "class" TypeName [ TypeParameters ] .
UnionType = "union" TypeName [ TypeParameters ] .
RefUnionType = "ref" "union" TypeName [ TypeParameters ] .
InterfaceType = "interface" TypeName [ TypeParameters ] .
DelegateType = "delegate" "func" TypeName "(" [ ParameterList ] ")" [ ReturnType ] .
PointerType = "*" Type .
TupleType = "(" TupleElem { "," TupleElem } ")" .
TupleElem = [ identifier ":" ] Type . // (x: int, y: int) , (count: int, int)

struct, union, enum, primitives, and tuples have value discipline: assignment, argument passing, return, and storage copy their value. class and ref union have reference discipline: those operations copy a reference to one object. A pointer is a reference to a value location and is specified separately in Pointers.

A struct is a user-defined value type. It has no object identity and cannot contain itself by value. Its construction forms are composite literals and positional struct construction; an init block is not valid. readonly struct makes its fields immutable. A value union has the same value discipline, but represents exactly one named case plus its case payload. An enum represents an int32-backed named integral value.

struct Point { var x: int, var y: int }
union Parse { number(value: int), missing }
enum Direction { north, east, south, west }

Value equality is supplied by the type’s equality contract (derive equality for a user struct) or the CLR default. It is not reference equality. Passing a value through an interface/object box creates an object that contains a copy; it does not change the source value into a shared object.

A class has object identity, supports constructors and inheritance, and is sealed by default. open makes the class inheritable; abstract makes it non-instantiable. A ref union represents its cases as an abstract reference base and sealed case objects; each constructed case has reference identity. A reference can be absent (nil) and is usable where its nullable form is expected.

An interface is a named contract. A type conforms only by declaring that interface in its base list; matching members without that declaration does not create structural conformance. Member contracts include methods, events, and properties. A property implementer must be declared as let or var and expose at least the required accessor set. A field remains a field and never receives synthesized property accessors through conformance. E# interface declarations conventionally use the .NET I-prefixed PascalCase form (ISized, IMap<K, V>). The prefix is a source convention rather than part of interface identity, so an imported or deliberately non-prefixed interface remains legal.

interface ISized { func size() -> int }
class Buffer : ISized {
var count: int
init(count: int) { self.count = count }
func size() -> int = self.count
}

Conformance is exact in member name, parameter types, return type, and required property accessors. Generic interfaces are reified and conformance is to the closed instantiation named by the type (IBox<int> is not IBox<string>). The pointer method-set wrapper may implement an interface where only *T has the required methods; see Pointers → method sets.

Generic types and generic functions are reified. Option<int> and Option<string> are distinct CLR closed types with their actual arguments, not erased object containers. Declaration identity is the pair of name and arity, so Result<T, E> can coexist with a non-generic static Result factory facet. The generic rules, constraints, and inference algorithm are specified in Generics.