Lyricdev
GitHub

5. Interfaces and conformance

An interface names the methods a type promises; a type conforms by implementing them; a value of the interface's type carries any conforming value and reaches its implementation — the language's one dynamic dispatch (04 D1–D15, 01 V7, 03 T5–T7, T10, T11).

Scope

Declaring an interface and a conformance, the interface value, how a call resolves. Written with M4 S1, S3, S4, S4b, S5a, S5b, S6, S7a and S7b; the chapter grows with the milestone's slices — synthesis (D7). Of the associated types (T6, §8) the value form Iterator<Item = int> is rule 6 there; of the operators (D6, §12) the conversions From/Into stand in 12, their blanket under §13 rule 9. What is not written here yet is decided in the design documents, not here.

Conformance. conformance/cases/05-interfaces/.

1. Interfaces and conformance

  1. interface I { … } declares instance methods: abstract, fn area(): int;, or with a default body. A member carries no pub (LYR-SEM0118): an interface's members are public always (D14).
  2. A struct, a class or an enum conforms in its interface list, struct S :: [I, J] { … }, or through an extend block, extend T :: [I] { … } — one interface also without the brackets, struct S :: I, extend T :: I, the list rule (02); several without them are refused (LYR-PAR0066) —; every entry names an interface (LYR-SEM0078). A conformance implements every abstract member of the interface's chain with an exact signature — arity, parameter types, return type, mut (LYR-SEM0020, LYR-SEM0042); a default may be left as it is, and an own member of the name overrides it. A generic member answers a plain one where its own type parameters bind — its parameter and return types unified with the member's — and their constraints hold for what they bound (design 13 M8c S9): contains<P :: [Pattern]>(p: P): bool answers Contains<char>.contains(x: char) at P = char; where they do not, LYR-SEM0042. A string takes such a witness from its view, as a call on a string reaches the view's members (12); a call through the constraint runs the witness at that binding.
  3. An interface may name parents, interface Labeled :: [Named]: conforming to the child conforms to every parent, a parent's defaults are inherited, a parent constraint is satisfied by the child. The list holds interfaces only and no cycle (LYR-SEM0078), and a name stays one: a child does not redeclare a parent's member, two parents do not bring one name from two declarations (LYR-SEM0079); a name reached twice through one declaration — a diamond — is one member.
  4. A default is generic over Self, the conformer (04 D9): this is the conformer's value, Self its type. Called on a conformer — on a value of the type, through a constraint, through a block (§13) — it runs as that type's own, also on a builtin, extend int :: [Named], and for an interface that is a constraint only (§7 rule 3): a parameter o: Self takes the conformer, Self.zero reads its constant. this passes as the interface and as a parent of it, as a type parameter does. A private helper is the defaults' alone — through a constraint from anywhere else as well (04 §3 rule 8, LYR-RES0009) — and never a conformer's method of its name.
  5. A member may be generic, fn hash<H :: [Hasher]>(&h: H): void (04 D9). Abstract, every conformer writes it with as many type parameters, constrained alike, at the same signature once its parameters are read as the interface's (LYR-SEM0042); a call through a constraint reaches the conformer's own at the call's type arguments — monomorphized, there is no table slot for it. With a default, it is the conformer's where the conformer writes none — the default runs as that type's own, as every default does (rule 4) — and a conformer may write its own in its place, at the default's signature (LYR-SEM0042): that one runs on the type, through a constraint, and where another default of the interface calls the member on this (the review's A8). With a body or without, a generic member makes its interface a constraint only (§7 rule 3). A default of a generic interface is not replaced by a conformer's own yet (LYR-SEM0082).

2. Interface values

  1. A value whose static type is an interface holds any conforming value of a struct, a class or an enum: let d: Damageable = Player.new(100);, or of a built-in type that conforms through std.core's blocks — a number, bool, char, string, a string's view: let d: Display = 5;, f(1, "two") for f(xs: Display...) (design 01 V7) —, or of a shape or a type that conforms through a block on its shape or a blanket block (§13 rules 6, 8). The transition happens at the positions of 03 T8 — a binding with a type, an argument, a return, a field, an array element under a context. (Informative.) It is a fat pointer: the data and the table of the concrete type's implementations, two words, by value (V7).
  2. A class value is shared through the interface value: what one side writes, the other reads. A struct or an enum value is copied at the transition (D12) and lives in the interface value from then on: a mut fn called through it writes that copy, the original stays as it was, and a later change to the original is not seen. A built-in's value is copied as a struct's is; a string's text, which nothing changes, is shared. (Informative.) The copy lies in a box on the heap, a string's reference too.
  3. A call through the interface value runs the conforming type's implementation — the language's only dynamic dispatch. A default called through it runs with this as the interface value — the interface is the conformer there (§1 rule 4) —, so a call on this inside it dispatches again: fn isAlive(): bool { return this.getHp() > 0; } reaches whichever getHp stands behind the value.
  4. An interface value is held in a binding, a field, an array, an optional, passed and returned like any value, and answers every member of the interface's chain. Two interface values are not compared.

3. One method set

  1. A type holds one function per name and count (D2, R4a3): its own members, the methods its inherent extend blocks add, and the implementations of its conformances — an own member, a member of a conformance block, an interface's default, a member delegated to a field (§5). Its own members and its inherent blocks' are one scope (08 §1.2 rule 1): fn get() in the body beside fn get(scale: int) in an extend, or in two of them, is an overload by count. A name two of these would give it at a count they share is refused where the second is declared (LYR-SEM0121): an extension beside an own member, two extensions, and — at any count — an extension beside a default of a conformed interface, a conformance block beside an own member. A blanket block's member is none of these: it stands behind them all (§13 rule 7).
  2. An own member is the implementation of every conformance's member of that name, and overrides a default. Two defaults of one name from two interfaces, which the type does not write itself, are the type's to settle: it writes the member, one function for both (LYR-SEM0043, D3).
  3. Every path reads the same set (R3): a call on the concrete type, a call through an interface value, a call through a constraint reach the one function.
  4. The one exception is scoped (D3): a type may implement one name separately per interface, in separate conformance blocks — extend C :: [I1] { fn greet() … } beside extend C :: [I2] { fn greet() … }. The name then belongs to each block: the unqualified call c.greet() is refused and says how to qualify (LYR-SEM0122); through an interface value of I1 or I2, through a constraint that names one of them, from a default of that interface calling the member on this, and through the qualified call (§4), each is reached — a static member through the type parameter its interface bounds, T.make(s). One block that holds a name at two counts holds an overload set (08 §1.2), not two scopes; blocks that give instances of one generic interface hold one set as well (rule 5).
  5. A type may conform to several instances of one generic interface, struct Label :: [From<int>, From<string>] — in its list, or each in a block of its own. Members of one name and count stand side by side where each answers its own instance, from(v: int) beside from(v: string) (D4 holds per conformance; LYR-SEM0085 where they answer one). A call chooses by its arguments' types: the member whose parameters take them, and of several the one whose parameters are the arguments' own types — Label.from(3) is the From<int> one beside From<float>, a literal being its default type. None or several left is refused (LYR-SEM0086); the argument is written with as, Label.from(3 as int16). Through a constraint, U :: [From<T>], the instance the constraint names chooses; through a value of an interface instance, its table does. There is no qualified form for a static member (§4 rule 2).

4. The qualified call

  1. I.m(x, …) calls the member m of the interface I with x as its receiver — the implementation x's type has for I (D2 R5). The receiver conforms to I (LYR-SEM0125 otherwise) and comes first (LYR-SEM0014). It is the form that names what the unqualified call cannot (§3.4), and it is checked as the member call it is: a mut fn writes x itself — no copy of a struct stands in for it —, so x is a place as the receiver of x.m(…) is (LYR-SEM0019).
  2. A static member is not called through its interface: Make.make<Item>(s) is refused (LYR-SEM0231) — the interface has no value to call it for, and a type argument written after it does not make it one. It is called on a type that conforms, Item.make(s), or through a type parameter the interface bounds, T.make(s) (§7).

5. Delegation

  1. A struct or a class may conform by delegating to one of its fields: class Dog :: [Walker by legs] { var legs: Legs, … } (D1). The field exists and conforms to the interface, or holds a value of it or of a child of it (LYR-SEM0123 otherwise) — a child's value is one of the parent (§11). Every abstract member of the interface's chain that the type does not write itself is forwarded to the field, as if fn walk(d) { this.legs.walk(d); } were written; a generic member too (R4b), as if fn tag<U>(u: U) { return this.inner.tag(u); } were written — one forwarder per use, called on the type or through a constraint. A forwarded mut fn writes the field as that function would: a value in the field — a struct, an enum, a builtin — is written where it lies, so its field is a var (LYR-SEM0019); a class, and a value of the interface, are written through. And what the field's implementation throws leaves the forwarded member as it would leave that function (06).
  2. Own members win. The interface's defaults run on the outer type, as every default does, and reach the forwarded members through this. There is no passthrough of this: the field's implementation runs with the field as its receiver, never the outer value — a call on this inside it reaches the field's own members, which is why delegation has no fragile base.
  3. A var field may be exchanged at run time; the next call forwards to the new value.
  4. Two delegations that would answer one name are refused (LYR-SEM0121); an enum delegates nothing (LYR-PAR0047).

6. Extend blocks

  1. extend T { … } adds methods to a type and extend T :: [I] { … } a conformance, in any module (03 T7 X3): there is no orphan rule — coherence is checked for the whole program. An interface is not extended directly (LYR-SEM0124); the generic form, extend<T :: [I]> T { … }, is the one (D15, with 03 §11).
  2. A block adds constants too: a static let in it is the type's, under the type's name, as one in the type's body is — extend int { static let answer: int = 42; } makes int.answer (10 N1: a constant is a static member). A type holds one member of a name: a block's constant repeating a member of the type or of another block is refused (LYR-SEM0121). A generic block's constant is one per instance of the block's parameters (04 §1 rule 6), read on an instance the block reaches (LYR-SEM0134 where its constraints do not hold); in a conformance block it answers the interface's constant for those instances (§7 rule 2).

7. Self and static members

  1. In an interface, Self is the conforming type (03 T5): fn equals(o: Self): bool. An implementation writes its own type where the interface writes Self (LYR-SEM0042 otherwise); through a constraint T :: [I], Self is T. In a struct, a class or an enum, Self is the type itself — a generic one at its own parameters.
  2. An interface may declare static members — static fn parse(s: StringView): Self; and static let zero: Self; — a constant without a value (LYR-SEM0127), a function with a body or none: every conforming type implements one with a static member of its own, in its body or in its conformance block (LYR-SEM0042; LYR-SEM0020 where it has none). A static let answers with the type the interface writes, Self read as the conformer — static let zero: int = 0; in extend int :: [Num]. The member is reached through a type parameter alone, T.parse(s), T.zero — the conformer's own static, a direct call or a read; of several of the name, the one that answers the member. A static member may be generic (§1 rule 5), static fn exact<T :: [Integer]>(v: T): ?Self;: U.exact(v) is the conformer's own at the call's type arguments. On a value of the parameter it is not reached (LYR-SEM0055). A static function with a body is a default (M9a S11): a conformer without a static of the name takes it, set in place for the conformer as an instance default is (11 §4 rule 5), Self the conformer — E.m(…) and T.m(…) are its instance; the conformer's own static of the name replaces it.
  3. An interface that names Self beyond the receiver, declares a static member or a generic one — abstract or with a default (§1 rule 5) —, is a constraint only (04 D9): no value of it arises — a table would hold no one function for such a member — and the transition says so (LYR-SEM0126). A type written for a value — a binding's, a parameter's, a function's return type, a field's — that is a constraint only, an optional of one, or holds one as an element of an array, a view or a tuple or as a type argument (List<Equatable>), says so where it is written, once, and goes on as no type: its uses, its initializers and the calls of its function say nothing more. An interface with neither is a value as §2 says.

8. Associated types

  1. An interface may declare an associated type, type Item; (03 T6): a type every conforming type names, once. Where a type parameter of the interface is an input — chosen by whoever writes the conformance, Add<int> beside Add<Vec2> — an associated type is an output of the conformance: one answer per type, read off the type wherever it is used. The two combine, interface Index<K> { type Output; fn get(k: K): Self.Output; }.
  2. Inside the interface the associated type is Self.Item. The declaration may carry a default, type Out = Self;, resolved with Self as the conforming type, and a bound, type Iter :: [Iterator]; (design 10 B6) — interfaces, written as a type parameter's constraints are (LYR-SEM0078 otherwise), Self again the conforming type. Every answer conforms to the bound (LYR-SEM0160); an answer carries none of its own (LYR-SEM0161). never answers — as a default, an answer or a fixation — only an associated type bounded by Error, as the empty thrown set (06, design 05 E2 K4); elsewhere it would type a value that cannot exist (LYR-SEM0145).
  3. A conforming type answers in its body or in the conformance block: type Item = int;. A conformer without an answer, where the interface gives no default, is refused (LYR-SEM0128); an answer that no interface of the type asks for is refused (LYR-SEM0129); the answer is what the signatures read — fn first(): Self.Item is implemented by fn first(): int (LYR-SEM0042 otherwise). The answer belongs to the conformance instance: Mul<int> and Mul<float> of one type each answer Out for themselves (§12); a second conformance block for the same instance is refused as a whole, its answers with it (LYR-SEM0133). In a type's own body Self.Item is its answer. A built-in conforms through its block alone and answers there. A blanket or shape block (§13 rules 6–8) answers in its body or by a fixation in its list — extend<I :: [Iterator]> I :: [Iterable<Iter = I>] — for every type it reaches: a type with no answer of its own is answered there, at the parameters it binds, and the block's signatures read the answer.
  4. Through a constraint the associated type is a type path, T.Item: fn firstOf<T :: [Container]>(c: T): T.Item { return c.first(); } — the answer of whatever T becomes, int at firstOf(IntBox { … }). The parameter's constraints declare it (LYR-SEM0128 where none does). An interface is no head for the path: Container.Item is nobody's answer. Through the path the associated type has what its bound provides — the members, a.iter().next(), and the constraints the bound reaches, count(a.iter()) with count<I :: [Iterator]> — and nothing else: without a bound it has no member (LYR-SEM0027) and satisfies no constraint (LYR-SEM0028) until its answer is known. The path goes on through what each step provides: A.Iter.Item is the Item of what A's Iter becomes, through Iter's bound while that is open (LYR-SEM0128 where the bound declares none), the answer on a type — Bag.Iter.Item. A blanket block reaches the associated type as it reaches a type parameter (§13): bounded by Iterator, it is Iterable and has iter().
  5. A constraint may fix an associated type: T :: [Iterator<Item = int>]. Inside the declaration T.Item is int, wherever it stands — a T.Item[] is an int[], a List<T.Item> a List<int>, an instance a constraint asks for, FromIterator<T.Item>, a FromIterator<int> (M8a S15); an argument whose answer is another type does not satisfy the constraint (LYR-SEM0028); a name no associated type of the interface carries is refused (LYR-SEM0128).
  6. The value form (design 03 M4-1, T6): an interface declaring associated types is the type of a value where that type answers every one of them — fixed as rule 5 writes it, Iterator<Item = int>, or by the interface's default, Iterator's Error being never. The members typed by an associated type then have one signature, next(): ?int, and are called through the value (§2). A type stands for such a value where it conforms and answers every associated type as the value does (LYR-SEM0001 otherwise, the answer named): a throwing iterator is no Iterator<Item = int>, and neither is a type parameter whose constraints leave its Error open — T :: [Iterator<Item = int, Error = never>] is one. Where the value's type leaves an associated type unanswered the interface stays a constraint only (§7 rule 3, LYR-SEM0126). A member that names Self itself, equals(o: Self), keeps it one too.
  7. An interface's parent list is a constraint on Self and may fix a parent's associated type the same way: interface Num :: [Add<Out = Self>] { … }. Every conformer answers as fixed — another answer does not conform (LYR-SEM0042) — and through a constraint T :: [Num] the answer is the fixed type: a + b is a T.

9. Any, type tests and type patterns

  1. Any is the empty interface of std.core (03 T10), imported as any other of its public types — the prelude does not pass it on (design 10 B2). Every struct, class and enum value is an Any at the transition, nothing declared, and so is every value of a built-in type an interface value holds (§2 rule 1) — a number, bool, char, string, a string's view: let a: Any = 5;. It is the ordinary transition of §2, a copy into a box for a struct, an enum or a built-in's value. Nothing converts to Any unasked: only a position whose type is Any takes a value that way. Any[] is the heterogeneous array.
  2. x is T (03 T11) asks an interface value what it holds and answers a bool: for a struct, a class, an enum or a built-in type T, whether the value is one — a comparison of the descriptor the value's table begins with, a built-in's box having one of its own. For a built-in type the test is exact: 5 as int8 is no int and nothing widens, an alias is the type it names (int64 is int), and a string is no StringView. For an interface T that is a value type (§7 rule 3), whether the concrete type conforms to it — a search of the conformance list behind that descriptor, which holds what a constraint sees: the type's own list, its blocks, and a blanket's or a shape's block that gives it the interface (§13 rules 6, 8). a is Describe holds for a Cat a block makes a Describe through Named, whether or not the program made one a Describe before. is stands at the comparison level and does not chain.
  3. The operand's static type is an interface. On a known type (c is Circle with c: Circle), on an optional (?Shape: test for null first), against a type parameter (s is T, O2: a dynamic test takes Any) and against a type no interface value holds — void, an array, a tuple, an optional, a function type: d is int[] — the test is refused (LYR-SEM0131); against a constraint-only interface it is refused as a value of it is (LYR-SEM0126).
  4. Narrowing. In the branch x is T guards, the name x is a T — the smart cast, the mechanism of the null test (03 §3.2): if (s is Circle) { s.r }, s is Circle && s.r > 0. The branch's end ends it, as an assignment to the name does; the other branch learns nothing. A negated test turns the two around (03 §3.2 rule 3): behind if (!(s is Circle)) { return; } the name is a Circle — the guard form of a type test. A read of the narrowed name takes the value out of the interface value: the object for a class, a copy for a struct or an enum (04 D12).
  5. Type patterns. c: Circle and _: Circle in a match over an interface value test the same way and bind c as the type (09 §2 rule 12). A match of type patterns over an interface needs _: the set of conformers is open (LYR-SEM0050) — unless the interface is sealed (§10).
  6. There is no as?: as is the bit-near conversion of scalars (03 T1d), and is with its narrowing is the downcast.

10. Sealed interfaces

  1. sealed interface Shape { … } (04 D8) closes the set of its conformers: every struct, class and enum that conforms to it — in its own list or in a conformance block — and every interface that names it as a parent stands in the module that declares it (LYR-SEM0132 elsewhere). sealed is a contextual word and stands before interface only (LYR-PAR0048).
  2. A match of type patterns over a sealed interface value is exhaustive without _ when every conformer has an unguarded arm; the diagnostic names the conformer missing, as the pattern to add (LYR-SEM0050, _: Rect). A conformer added later makes every such match without it an error — the point of sealing. Against an enum: an enum is closed variants with payloads, a sealed interface closed types with members of their own.
  3. (Informative.) The compiler may dispatch such a match as a switch over the descriptors.

11. The child interface value as a parent value

  1. A value of an interface is a value of each parent in its chain (04 D10, §1 rule 4): let d: Display = shape; with Shape :: [Display] is a coercion at the ordinary sites — a binding, an argument, a return — never inside a container (Shape[] is no Display[]). The parent's table is found at the transition through the concrete type's conformance list; the data stays the same object or box.
  2. Only up the chain: an unrelated interface and the way down are refused (LYR-SEM0001); the way down is is (§9).

12. The operator interfaces

  1. The operators on a struct, a class or an enum are method calls through the interfaces of std.core (04 D6), one per operator: a + b is exactly a.add(b) through Add<Rhs = Self> { type Out = Self; fn add(rhs: Rhs): Self.Out; }, and likewise - Sub, * Mul, / Div, % Rem, & BitAnd, | BitOr, ^ BitXor, << Shl<Rhs = int>, >> Shr<Rhs = int>; the unary -x is x.neg() through Neg, ~x is x.bitNot() through BitNot. !x is bool only. The scalars keep their own arithmetic and conform besides, so a constraint T :: [Add] takes them.
  2. One resolution. The conformance is chosen by the static type of the right operand: v * 2 takes Mul<int>, v * 2.5 takes Mul<float>; a literal adapts (03 §9.2), and two conformances a literal fits are an ambiguity to annotate (LYR-SEM0083). The result is the conformance's Out — Self unless the conformer answers otherwise. The homogeneous case is struct Vec2 :: [Add]; a heterogeneous conformance stands in a conformance block of its own with its answer, extend Vec2 :: [Mul<float>] { type Out = float; fn mul(rhs: float): float { … } }, beside the type's own mul (§3: the operand tells the two apart, and the block's stays reachable qualified).
  3. Without the conformance the operator is refused, with the conformance to write (LYR-SEM0003): a method of the name alone is not the operator — nominal, not structural.
  4. a += b and the other compound forms are a = a.add(b) on a var place whose type the answer is; there is no AddAssign. ++ and -- stay the integers' own.
  5. Equality. a == b on a struct, a class or an enum is a.equals(b) through Equatable { fn equals(o: Self): bool; }, != its negation; both sides are the one type, and the scalars compare natively. Without the conformance == is refused (LYR-SEM0059).
  6. Ordering. <, <=, > and >= read a.compare(b) through Ordered :: [Equatable] { fn compare(o: Self): ?Ordering; } with enum Ordering :: [Equatable, Hashable, Debug] { Less, Equal, Greater } (the review's M8a-10: an answer can be compared, keyed and printed): < is .Less, > is .Greater, <= is not .Greater, >= is not .Less; null — the two are not ordered, as float with NaN — makes all four false. TotalOrder :: [Ordered] { fn totalCompare(o: Self): Ordering; } is what sorting and keys ask; float has none. Without Ordered an ordering is refused (LYR-SEM0003).
  7. A generic body sees the answer the constraint fixes: fn sum<T :: [Add<Out = T>]>(a: T, b: T): T adds; under T :: [Add] alone a + b is a T.Out, which is not a T (§8 rule 5).
  8. {x} in an f-string renders through Display { fn show(): string; fn showTo(out: StringBuilder): void; }: the f-string calls showTo, which by default appends show(), so a type that can write itself into the buffer does (design 10 S6); StringBuilder.append(v) calls it too. The builder is a class, written through the reference it is passed as — no & (design 10 S13). The scalars render natively and conform besides.
  9. Indexing. x[k] on a struct, a class or an enum is x.index(k) through Index<K> { type Output; fn index(k: K): Self.Output; }, and x[k] = v is x.setIndex(k, v) through IndexSet<K> :: [Index<K>] { mut fn setIndex(k: K, v: Self.Output): void; } — the containers' [i] and [k] (12). A type read and not written refuses every form of the assignment (LYR-SEM0019). setIndex is a mut fn, so the receiver is a place as for the call written out: a struct bound with let, or one an index handed out, is not written through […] (LYR-SEM0019). A compound assignment x[k] op= v, and x[k]++ and x[k]-- on an integer element, evaluate x and k once and are x.setIndex(k, x.index(k) op v) — read, compute, write, as on an array's element (the review's M8a-1); &&=, ||= and ??= read once and write only when they assign. The order and the value of an assignment are 08 §3's. An element so read is a value, no place: x[k].f = v is refused (03 §2.2). The index of an array, a view and an inline array is built in (03 §5). From the end: on a type with its own Index<int> that conforms to Length { fn length(): int; } of std.core, x[^n] is x.index(x.length() - n) and its writes go through setIndex the same way, x evaluated once for both calls; without Length the type takes the index as written, and ^n there is refused (LYR-SEM0114). List<T> and Deque<T> conform (12).
  10. Membership. x in xs is xs.contains(x) through Contains<T> { fn contains(x: T): bool; } (04 D6, design 10 C12) — a range, an array, a view, a list, a set and a map's keys conform (12) — and x !in xs its negation. The conformance is the right operand's, and x adapts to its T as a literal does: 200 in bytes with bytes: uint8[]. On a string or a StringView the left operand is any Pattern and the text's own contains answers — 'a' in s, "ab" in s. The operands are evaluated left to right, x before xs, though the call is the container's. in and !in bind as the comparisons do; !in is one token, ! written against in (x! in xs unwraps x first). Without the conformance in is refused (LYR-SEM0059), a method contains alone included; [] beside it fixes no element (LYR-SEM0060).

13. Generic extends

  1. An extend block may carry type parameters of its own, extend<T> List<T> { … } (03 T7 X1), with constraints as a function's: extend<T :: [Display]> List<T> { … }. The target is a named type at the block's parameters, or one instance of it, extend Box<int> { … }, or a built-in constructor (X2, rule 6) — a function type among them (M8a S12). In the block, this is the target as written; Self stands for a plain target only.
  2. A member of such a block is reached on every receiver the target matches: the receiver binds the parameters — List<int>.first() is an int — and the block's constraints must hold for what it bound; where they do not, the member is not there (LYR-SEM0134). A block on one instance adds to that instance alone (LYR-SEM0012 elsewhere). A parameter the target does not name is bound where a fixation in another's constraint names it: extend<I :: [Iterator<Item = T>], T :: [Num]> I binds I to the receiver and T to its Item, whose constraints then hold or the member is not there. A fixation may name parameters inside a type: extend<I :: [Iterator<Item = (K, V)>], K :: [Hashable], V> I matches the receiver's Item against (K, V) and binds both — a receiver whose Item is no pair has no such member. A parameter that neither the target, a fixation nor the block's conformance (rule 9) names is bound by nothing: the block reaches no type, and is refused where the parameter stands (LYR-SEM0170).
  3. Conditional conformance. extend<T :: [Display]> Bag<T> :: [Display] { … } gives the conformance to every instance whose arguments satisfy the block's constraints and to no other: Bag<int> is a Display, Bag<Foo> is not (LYR-SEM0001) — the form the library's containers take (12, 10 C9). A block on one instance conforms that instance alone. The signatures are checked at the block's target (Bag<T> with the block's T), as §1 checks them at the type.
  4. Coherence (X3, X4): one conformance per type instance and interface instance in the whole program, from whichever site — the type's own list, a block, a generic block. Two sites that could meet on one instance are refused where the second stands (LYR-SEM0133): extend Bag<int> :: [Display] beside extend<T :: [Display]> Bag<T> :: [Display], a block beside the type's own declaration, two generic blocks over the same target. There is no specialization; two blocks on instances that never meet (Box<int>, Box<string>) stand. There is no orphan rule: a block may stand in any module.
  5. (Informative.) A generic block's member is compiled per receiver instance, as a method of a generic type is; a table row for a conformance a block gives is built for the instances the program uses it on.
  6. The built-in constructors (X2): extend<T> T[] { … }, extend<T> ?T { … }, extend<T> Slice<T> { … }, a tuple of fixed arity extend<A, B> (A, B) { … }, a function type extend<T> fn(T) -> T { … } (design 10 S2; M8a S12: this is the function, this(x) calls it), and the same at fixed types, extend int[] { … }, extend fn(char) -> bool { … }. A function type's shape is its parameters, their places, its return and its set, matched as written. A coroutine's shape is what it yields and what it returns, extend<Y, R> Coroutine<Y, R> { … } (N2d); what its pulls throw is no part of it — the block reaches every coroutine of those, and where a member names the target, it names the receiver. The members are reached on every value of that shape, the shape binding the parameters as rule 2 says, the constraints checked the same way (LYR-SEM0134); this is the shape, so this[0] and this.length() stand in an array's block, this == null and this! in an optional's, let (a, _) = this; in a tuple's. length() stays the primitive (03 §5). An element of a tuple comes first, a block's member after. A shape conforms through a block that names the interface, extend<T :: [Describe]> T[] :: [Describe] { … } — every shape the block's constraints admit (rule 3), with the signatures checked at the shape, Self read as it (LYR-SEM0020, LYR-SEM0042). That is the one way a shape satisfies a constraint (LYR-SEM0028 otherwise), and a member called through the constraint is the block's. Two blocks on overlapping shapes giving one interface — of either's chain — are two conformances (LYR-SEM0133), except a parent another block on the same shape gives under constraints this block's entail: a block giving Hashable to (A, B) of Hashable elements leaves Equatable to the block that gives it to (A, B) of Equatable ones, whose equals answers — each interface under its own condition, as 04 D7 synthesizes them (N2a). A shape that conforms to Equatable so compares with == and != through its block's equals, and one that conforms to Display or Debug renders in an f-string hole, as a named type does — std.core's arrays and views do (10 C9; M8a S14). A value of the interface is made from the shape in a box, as from a built-in (§2): let d: Describe = [1, 2];, its table the block's members and, where the block writes none, the interface's defaults.
  7. Blanket blocks (04 D15): a block whose target is its own parameter, extend<T :: [I]> T { … }, adds its members to every type its constraints admit — the receiver binds T, this is the receiver: x.greeting() on every Named. A member of the type's own, of another block or of a default comes first: a blanket member of a name the type already has is hidden for that type (the review's A5) — a call on the type reaches the type's, while generic code that reaches the name through the block's constraints calls the block's, for that type too. They are two functions of one name, so the type's member is warned about where it stands (LYR-SEM0168; at the type, for a default it inherits or a member it leaves to a field). Two blanket blocks that give a type one name it does not have itself are refused (LYR-SEM0121), and a member the receiver's type does not admit is not there (LYR-SEM0012). In a generic body a type parameter whose constraints imply the block's receives it too (D2 R4). A shape receives a blanket member where it satisfies the block's constraints (rule 6). A member with type parameters of its own, fn mapped<U>(…), is one function per receiver and per its own type arguments.
  8. Blanket conformance (D15): extend<T :: [I]> T :: [J] { … } gives J to every type the constraints admit — the signatures checked at T — so a Named satisfies a constraint on J and a call through it is the block's member; a type it does not reach keeps its own. Such a block implements J's whole chain, so it excludes, by interface: a type's own conformance to one of the chain where it reaches the type (X4); a shape's block giving one where it reaches the shape — the shape, or an overlapping one another block names, meeting its constraints, which a shape does through a block naming them alone (rule 6); and another blanket block giving one, even where no type could meet both (LYR-SEM0133). A value of J is made from a type the block reaches, with the block's members in its table: let d: J = cat;.
  9. A parameter only the conformance names (design 10 B4): in extend<T, U :: [From<T>]> T :: [Into<U>] { … } the target does not name U, nor does a fixation; the block's conformance does. The instance a constraint asks for binds it: Meters satisfies Into<Centimeters> where Centimeters :: [From<Meters>] holds, and a call through the constraint is the block's member with U that instance's. At a call of the block's member on a receiver the call binds it, as it binds the member's own parameters (03 §9.2) — from the arguments, then from the type the position expects: let c: Centimeters = m.into();. Where nothing determines it the call is refused (LYR-SEM0060), and where what binds it fails the block's constraints too (LYR-SEM0028). Such a block reaches a type at the instances bound so and at no other, so it excludes a type's own conformance (rule 8) at those alone, whole-program: Meters :: [Into<int>] stands while int is no From<Meters>, and is refused where it is one (LYR-SEM0133), said at the type's conformance.

14. Conformance synthesis

  1. A conformance to one of the family — Equatable, Hashable, Ordered, TotalOrder, Clone, Default, Display, Debug — written in a struct's, class's or enum's own list without the member is synthesized from the fields (04 D7): struct P :: [Equatable, Hashable] { x: int, y: string } compares and hashes field by field, in declaration order. There is no derive word. A member the type writes itself replaces the synthesis for that member; the others are still synthesized. The list is fixed in 5.0; a synthesis of the program's own interfaces is 11's question. Equatable, Hashable, Ordered, TotalOrder and Clone are std.core's defaults, written once over the conformer's fields and variants and set in place for each conformer (11 §4 rules 5 and 10; 09 A5); Default — no form names a field's type or its written default —, Display, Debug and Identity are the compiler's.
  2. Field by field, declaration order. equals is the conjunction of the fields' equalities; hash writes the fields into the hasher in order (12 §Hashing) — an optional as a mark and its value, a tuple's and an inline array's elements in order; compare and totalCompare are lexicographic, the first field that is not .Equal decides, and a compare that meets null is null; clone copies the value fields and clones the reference fields — an array element by element, an optional's value, a tuple's elements —, and copies a field marked @Shared (11 §1 rule 14) instead, whatever it holds: a parent, a service, a cycle; such a field's type needs no Clone; default takes every field's default(), or the field's own initializer where it has one, [] for an array, null for an optional and a tuple of its elements' defaults; show on request is the debug form; debug renders P { x = 1, y = "a" } with every field's debug(), an array as [1, 2], an optional as its value or null, a tuple as (1, "a"), a string quoted and a char in single quotes. An inline array field is taken element by element.
  3. Enums as structs, variant by variant: two values are equal when they are the same variant with equal payloads; the hash writes the variant's position, then its payload; the order is the variant's position first, then the payload lexicographically; clone rebuilds the variant; debug renders E.B(3), E.R { w = 4 }, E.A. A Default of an enum is refused — no variant is the default (LYR-SEM0135).
  4. Generic types conditionally (04 D7, Rust's derive bound): struct Pair<T> :: [Equatable] is extend<T :: [Equatable]> Pair<T> :: [Equatable], so Pair<int> compares and Pair<NoEq> does not (LYR-SEM0059 at the comparison, §13 rule 3); Display asks Debug of the parameters.
  5. The implied parents come along. A type listing Hashable or Ordered alone gets Equatable synthesized too, TotalOrder brings Ordered and Equatable — unless the type lists them or writes their member. A conformance written in a block of its own beside such a list meets the synthesized one as two sites (§13 rule 4, LYR-SEM0133): list it, and the block is the one site. A parent an interface of the program implies comes along the same way where the compiler writes the member — interface Args :: [Default] gives struct Start :: [Args] its default, through the whole chain of parents, the interface another module's or not —, while the std.core defaults a parent brings are inherited as every default is (§1 rule 3). A type that lists the implied interface itself, writes the member, or gives the interface in a block of its own module has that one (the sweep after M10, W8).
  6. What is refused is said at the declaration. A field whose type lacks the conformance the synthesis needs is the diagnostic of the missing operation, with its own code, at the type's conformance-list entry — 'Equatable' is not synthesized for 'P': … —, the field's round and the default's line as notes (note: in the round for the field 'f' of 'P'); a shape the synthesis has no form for — a Slice<T> or a function to clone, a function to default, an array or a view to hash (10 C8) — is LYR-SEM0135 with the field's type, and the member is written by hand.
  7. Debug is given unasked to every struct, class and enum that writes none — in its declaration or in a block, a block under a constraint included, as std's containers write theirs —, where the fields allow it: a type holding an interface value, a function, or a type that has no debug() of its own gets none, silently — there was nothing asked and nothing to report — and x.debug() on it is LYR-SEM0012. Listing Debug asks, and then rule 6 names the field. Display is never automatic (§12 rule 8). Debug is { fn debug(): string; fn debugTo(out: StringBuilder): void; }, debugTo by default appending debug(), as Display's showTo appends show(); {x:?} calls debugTo.
  8. Identity (02 M10, 04 D7; N2b) is an object's: class Node :: [Identity] is given Equatable and Hashable by the object — equals is same(this, o), hash writes the object's address, which holds while it lives (nothing moves, 01 L1) — so a Map<Node, X> keys by the object and two objects of equal fields are two keys. Listed beside it, Equatable and Hashable are those two; a generic class asks nothing of its parameters. A struct or an enum is a value and has no identity, and an equals or a hash the class writes beside Identity would be a second answer to its question: both are refused (LYR-SEM0135), a conformance a block writes by hand as one a list does. The prelude passes Identity on beside Equatable and Hashable.
  9. (Informative.) The synthesized member stands in an extend block of the type's own module, generated as source and compiled like one written; it is reached, overridden and qualified exactly as a block's member is. Hints about its style are nobody's to act on and are not reported.