Skip to content
Topaz6.0 · Before release
Download

Search documentation

Specification 6.0 · Not released · Tested product 5.21.3

Use arrow keys to choose a result. Press Enter to open.

Type an API name, diagnostic number, rule, or project topic.

    Language · chapter 08 · 43 rules

    Protocols and implementations

    Declare protocol requirements, provide implementations, and understand conformance, receiver methods, and resource bindings.

    Scope and ownership

    Link

    Use this page to declare protocol requirements, implement them, and add receiver methods.

    This chapter specifies Topaz 6.0. Source references identify the inspected SELF revision.

    This page owns protocol declarations, implementation conformance, protocol resolution, receiver self, and using. Records, enums and newtypes owns nominal declarations and derives placement. Functions and generics owns ordinary function signatures, calls, and bound syntax. Standard library entries own the behavior of built-in methods.

    Declaration forms

    Link
    EBNF
    1. ProtocolDeclaration =
    2. "export"? "protocol" Identifier
    3. "{" ProtocolSignature* "}" ;
    4. ProtocolSignature =
    5. "function" Identifier FunctionTypeParameters?
    6. "(" FunctionParameters? ")" ("->" Type)? ;
    7. Implementation =
    8. "impl" FunctionTypeParameters? NamedType
    9. "{" ImplementationMethod* "}" ;
    10. ImplementationMethod =
    11. "export"? NamedFunction ;
    12. ProtocolReference = Identifier ("." Identifier)? ;
    13. UsingStatement =
    14. "using" Identifier "=" ControlOperand Block ;
    15. ConditionalDerivesClause =
    16. "derives" FunctionTypeParameters ProtocolReference "<" Type ">"
    17. ("," ProtocolReference "<" Type ">")* ;

    An export prefix publishes a module-level protocol declaration. Protocol references in bounds and conformance clauses use P or ns.P through the ordinary import boundary. One implementation head uses the ordinary named-type and type-argument grammar. Name resolution distinguishes a protocol application, which takes one conforming type, from a nominal receiver. Explicit binders precede either head. Protocol signatures use ordinary annotated parameters; their result annotation is required during checking. A conditional derives clause appears before the nominal body or newtype base.

    Source evidence
    Ruletopaz.rule.structures.protocols.declaration.body

    Protocol bodies

    A protocol body contains function signatures.

    Why

    A protocol states required operations independently of their implementations. Signatures give callers a contract without choosing the code that provides it.

    Source evidence

    Protocol implementation form

    impl Protocol<Type> supplies definitions for one protocol and a complete conforming type. An explicit binder list precedes the protocol name for a conditional implementation. The protocol application has exactly one conforming type argument. Type aliases are expanded, and generic heads match the resolved static type through type equality, not value subtyping. A protocol call chooses the conformance before execution.

    Why

    Naming the resolved protocol and target type identifies one contract. Using the ordinary type grammar admits built-in, structural, and nominal targets without a new implementation notation.

    Source evidence
    Ruletopaz.rule.structures.protocols.receiver.form

    Receiver implementation form

    impl Type declares receiver methods for a nominal type owned by the current module. For a generic receiver, write explicit binders before its complete target, as in impl<T> Box<T>. Those binders enter method signatures and bodies; a method may introduce its own nested binders. The first bare self retains the instantiated receiver type even if a method binder shadows an outer name. The existing receiver name, field-collision, and export rules apply to every instantiation.

    Why

    Receiver methods already identify their type through the receiver expression. Explicit enclosing binders let generic receivers use the same method declarations and calls as concrete receivers.

    Source evidence

    Protocol method requirements

    Link

    Protocol declarations use Self

    A protocol declaration has no explicit type parameter list. Use Self for its conforming type. An explicit receiver binder is rejected during parsing with TPZ2001.

    Why

    An explicit receiver binder and Self both name the type that implements the protocol. The binder renames that role rather than introducing a different type relationship. Self preserves the same receiver intent and matches built-in protocol signatures.

    Source evidence

    The conforming type in signatures

    Within a protocol method signature, Self denotes the conforming type.

    Why

    The conforming type must be substituted consistently throughout a protocol signature. This keeps its receiver and related arguments tied to the same implementation target.

    Source evidence

    Selecting the conforming type

    A protocol method has an implicit conforming type parameter named Self. It may occur in any parameter or result type. Methods may have no value parameters. A call infers Self from its arguments and expected result through the ordinary generic-call rules. An explicit call type-argument list supplies Self first, followed by the method parameters. Dispatch requires one resolved conforming type; a call without enough inference evidence must supply that complete list.

    Why

    A constructor or decoder has no existing receiver value. Selecting the conforming type with the ordinary generic-call rules makes the same protocol operation usable before and after a value exists. No position in a value-parameter list owns dispatch.

    Source evidence

    Protocol return annotation

    Every protocol method requires an explicit return annotation, including -> () for unit. An omitted annotation produces TPZ5022.

    Why

    A protocol signature has no body from which to infer a result. An explicit result type gives callers and implementations the same complete contract.

    Source evidence

    Ordinary function parameters in protocols

    Protocol signatures use the type parameter, bound, annotated parameter, variadic, and default-expression rules of named functions. Method binders are distinct from implicit Self and are in scope throughout that method signature, including bounds and result types. They do not enter another method. Parameters remain immutable. Required fixed parameters precede the defaulted suffix; one final variadic parameter has no default and binds an array. The protocol declaration owns each default. Its names resolve in the protocol declaration environment, where value parameters are unavailable. Supplied arguments and omitted defaults follow ordinary call evaluation order.

    The method type parameters follow implicit Self in the generic call signature. Implementation signature matching compares their resolved types and bounds after renaming corresponding binders. Protocol declarations have no body, so the result annotation remains required.

    Why

    A protocol promises callable operations. Giving those operations the same generic, variadic, and omission rules as named functions avoids a parallel helper-function interface. Keeping defaults in that promise gives every implementation and caller one omission behavior.

    Source evidence

    Unique protocol names

    Redeclaring a user protocol name in the same module produces TPZ3008 during resolution.

    Why

    A protocol name must identify one contract in its module. Reusing the name would make conformance declarations ambiguous.

    Source evidence

    Unique protocol method names

    Declaring the same method name twice in a protocol produces TPZ5008.

    Why

    A method name must identify one operation within a protocol. Duplicate names would make implementation coverage and calls ambiguous.

    Source evidence

    Built-in protocol names

    Redeclaring the built-in protocols Eq, Order, Show, or JSON produces TPZ5008.

    Why

    Built-in operations and bounds use these protocol names. Redeclaring them could change the meaning of existing equality, ordering, display, or serialization code.

    Source evidence

    Protocol and type name collisions

    Reusing a declared type name for a protocol in the same module produces TPZ3008 during resolution.

    Why

    Protocol and type names occur in implementation heads. Distinct names keep an implementation target from being mistaken for its protocol.

    Source evidence

    Module-level declarations

    A protocol or implementation declaration inside a block produces TPZ5022.

    Why

    Conformance is checked for the program before execution. Module declarations keep the available implementation from depending on whether a block has executed.

    Source evidence

    Implementation conformance

    Link

    Implementation ownership

    The implementing module must define either the resolved protocol or the target type's outermost nominal declaration. A module may therefore define its own protocol for int, string, a structural type, or a generic type pattern. A foreign protocol requires an own-module record, enum, or newtype as the target's outermost constructor. A local type argument inside a foreign Array or Map does not transfer ownership. Transparent aliases preserve the underlying type and its owner. Implementing a foreign protocol for a foreign type produces TPZ5520. Every admitted implementation also passes the complete dependency graph's uniqueness check.

    Why

    The protocol owner can give an existing type a new operation without inventing a wrapper or a separate helper-function interface. The type owner can adopt an existing operation. Requiring one of those owners and rejecting graph-wide overlap gives each protocol and target pair one meaning.

    Source evidence

    Required implementation methods

    An implementation supplies every declared protocol method and no undeclared method. A missing or undeclared method produces TPZ5022.

    Why

    Every operation promised by a protocol must be callable. Rejecting extra methods also catches a misspelled implementation name instead of leaving the intended operation missing.

    Source evidence

    Unique implementation methods

    Implementing the same method twice in a protocol implementation produces TPZ5008.

    Why

    One implementation must supply one definition for each operation. Repeated definitions would make dispatch depend on declaration order.

    Source evidence

    Matching the protocol signature

    Substitute the complete target for Self, keep implementation binders rigid, and pair method binders by position. Method binder counts, their sets of resolved protocol bounds, fixed parameter types, variadic position and element type, and result type must match. Type aliases and binder names do not create differences; bound order and parameter names do not affect a positional call. Repeated bounds still follow the ordinary duplicate-bound rule. The implementation may neither strengthen nor weaken a method's bound set. An explicit result annotation must equal the protocol result. When it is omitted, use the protocol result as the ordinary expected result while checking the body. Each return and tail must be assignable to that result; omission does not narrow the public signature. A signature mismatch produces TPZ5022.

    Why

    Callers have one complete signature before they see an implementation. Alpha-equivalence allows harmless renaming, while exact callable structure and bound identity preserve the same obligations. The protocol already supplies the result context, so repeating its annotation cannot be mandatory.

    Source evidence

    Implementation methods use ordinary functions

    An implementation method is checked as an ordinary function against the protocol signature. Matching method generics and a matching variadic tail are admitted, and the result annotation may be omitted. Method-level export and parameter-default initializers produce TPZ5022. Conformance visibility already exposes the promised operations, and the protocol declaration already supplies omitted arguments. An implementation cannot create a second export or default contract, even by repeating the same default expression.

    The implementation binder list encloses every method. Each method binder list is a nested lexical scope. A shadowed name denotes the inner binder; it is not silently identified with the outer binder during signature matching. Generic conformance defines the same scope for receiver implementations and conditional derives.

    Why

    Generic methods, variadic tails, and contextual results have the same function meaning inside an implementation. Visibility and defaults each already have one owner, so repeating either on a method would create competing descriptions of the same operation.

    Source evidence

    One conformance across the dependency graph

    At most one conformance may apply to a resolved protocol and normalized target type throughout the complete resolved dependency graph. Expand aliases, retain defining-module nominal identities, and rename each declaration's binders before comparing target heads. Heads overlap when some consistent substitution makes their full target types equal. Repeated variables must keep the same substitution. Different positive bound sets cannot establish disjointness, even when no current type satisfies both. Reject every overlap with TPZ5521 before module initialization, whether it is manual/manual, manual/derive, or derive/derive. A concrete implementation has no priority over a generic one. Declaration order, import order, aliases, and method bodies cannot choose a winner. Distinct heads that cannot unify remain independent. Overlap diagnostics use the target of the later manual declaration in deterministic dependency-first module and source order. A conflict involving derive points to the derived protocol head; two derives use the later clause by the same order.

    For example, Eq<Box<T>> overlaps Eq<Box<int>>. Mark<Box<int>> and Mark<Box<string>> are disjoint. Mark<Box<T>> and Mark<Box<U>> overlap even if their binders carry different positive bounds. Derive-generated conformances enter this same comparison. Normalization uses static type equality, so the rule does not add value-based or subtype-based implementation selection.

    For two conflicting manual declarations, report the target of the later declaration in deterministic dependency-first module order, using source order within a module. If a derive clause participates, report its protocol head; for two derive clauses, choose the later clause by that same order. A secondary location may identify the other declaration. This reporting order never selects an implementation.

    Why

    Dispatch must identify one operation from its protocol and static type. Rejecting overlap makes that meaning independent of import and declaration order, and prevents later conformances from changing an earlier specialization choice. A stable primary location lets a caller fix the conflicting declaration without making import order affect which source is reported.

    Source evidence

    Generic binders and conditional conformance

    An implementation places its type binders before its head, as in impl<T: Eq> Eq<Box<T>>. A receiver implementation uses the same placement, impl<T> Box<T>. Resolve the head to distinguish a protocol application from a nominal receiver; spelling, capitalization, and whitespace do not choose the meaning. Implementation binders scope over their bounds, the complete target type, and every method signature and body. Every implementation binder must occur in the target, so the selected target determines its arguments. Method binders have ordinary nested lexical scope and may shadow outer binders; matching uses resolved binder identities, never name strings. Each binder list has distinct names. A protocol method already has implicit Self in its binder scope. Bounds must have independent conformance evidence; an active obligation cannot prove itself. A generic nominal writes an explicit conditional derive binder list even when it has no bounds. Those binders scope only over that clause, correspond positionally to the nominal parameters, and may be renamed. Each derive target is the declaring nominal applied to all of those binders in declaration order. Fields and enum payloads keep the nominal declaration binders. Concrete and conditional manual and derived conformances use the same uniqueness rule.

    Write record Box<T> derives<U: Eq> Eq<Box<U>> { value: T } to rename the clause-local binder. Its positional correspondence is with the nominal parameter T; it does not rebind T inside the fields. A concrete impl Eq<Box<int>> is a concrete head, while impl<T: Eq> Eq<Box<T>> covers every target satisfying the bound. They overlap and cannot coexist. A blanket implementation such as impl<T> Local<T> is permitted in the module defining Local and excludes every overlapping Local implementation. A type alias is expanded before ownership, matching, or overlap is decided.

    Why

    Explicit lexical binders make a generic conformance one reusable definition. Resolving types before comparing signatures prevents renaming or shadowing from capturing a different type variable. One conformance lookup for every instantiated target avoids overload or specialization priorities.

    Source evidence

    Implement equality for a generic wrapper

    Link
    example.tpzImplement equality for a generic wrapperOpen in Playground
    1. record Box<T> { value: T }
    2. impl<T: Eq> Eq<Box<T>> {
    3. function equals(a: Box<T>, b: Box<T>) -> bool {
    4. Eq.equals(a.value, b.value)
    5. }
    6. }
    7. let left: Box<int> = Box { value: 3 }
    8. let right: Box<int> = Box { value: 3 }
    9. let same: bool = Eq.equals(left, right)
    Expected outputexit 0
    No stdout.
    $ topaz check example.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Source evidence

    Protocol calls and resolution

    Link
    Ruletopaz.rule.structures.protocols.call.target

    Protocol-qualified calls

    Resolve the protocol and method, then apply the ordinary generic-function call rules. The call type-parameter list is Self followed by the method binders in declaration order. Infer the complete list, or write it after the method name as Protocol.method<Receiver, MethodType>(arguments). Implementation binders are determined by the selected conforming type and are not extra call arguments. Parentheses around the callee preserve the same declaration signature. No separate Protocol<Type>.method call form is introduced.

    Why

    A protocol-qualified operation is still one generic callable. One inference and explicit-specialization rule keeps ordinary functions, callbacks, and protocol calls compatible.

    Source evidence
    Ruletopaz.rule.structures.protocols.call.conformance

    Protocol call conformance

    Check conformance for the resolved Self type, independently of whether a value of that type occurs first, later, or only in the result. A generic body may use its declared protocol bound as conformance evidence. A concrete type without the required conformance produces TPZ5522. Dispatch uses that one conformance and does not select an implementation from the runtime value or from argument order.

    Why

    Arguments and results may establish the same conforming type. Looking up that type and the resolved protocol identity gives the operation one behavior without requiring a dummy receiver value.

    Source evidence

    Protocol argument forms

    Protocol calls supply positional values and may spread an array into a declared variadic tail. Fixed omissions use the protocol defaults. The callee and supplied arguments are evaluated once from left to right; missing defaults follow in parameter order. Spread cannot fill an unsatisfied fixed slot. A named argument is rejected during parsing with TPZ2001, as in every other call.

    Why

    One argument-binding and evaluation rule lets an operation move between an ordinary function and a protocol without changing its call behavior.

    Source evidence

    Protocol bounds

    A function protocol bound checks the inferred concrete type argument through conformance lookup. An unsatisfied bound produces TPZ5522.

    Why

    A generic body may use every operation promised by its bounds. Checking conformance when binding a concrete type keeps those operations available.

    Source evidence

    Conformance across modules

    Manual and derived conformances are available in the defining module and in modules that import the nominal type. Import aliases preserve that conformance.

    Why

    A conformance belongs to the type and protocol identities. Importing that type must preserve its operations whether the methods are written or derived.

    Source evidence

    Conformance follows nominal origin across imports

    Resolve a conformance by the protocol declaration identity and the normalized receiver type. User protocols and nominal types retain their defining module through selected or namespace imports and aliases. Same-spelled declarations in different modules remain distinct. Manual and derived conformances in the resolved dependency graph participate in the same coherence check. Import paths, aliases, and item order do not create or select another conformance. A manual call uses the implementation from the conformance-defining module. A receiver without an applicable conformance is rejected with TPZ5522. Conformance ownership and overlap follow the implementation rules.

    Why

    A call must keep its meaning when an import is renamed or moved. Resolve both the protocol and receiver type to their defining identities, and check one coherent set of conformances for the dependency graph. The defining module of a conformance determines its implementation, so aliases and import order cannot select a different body.

    Source evidence

    Protocol heads respect local values and declaration identity

    A protocol declaration creates a non-value protocol binding in the same lexical and module name table as other declarations. It is private unless prefixed with export. Selected imports, selected aliases, and namespace imports resolve that binding by its defining module and declaration. Use P or ns.P in protocol bounds and conformance heads, and P.method or ns.P.method for calls. Aliases preserve identity; equal spellings in different modules remain different protocols. A same-scope name collision follows the module duplicate-binding rule. A nearer value or namespace owns an expression member access and is not reinterpreted as a protocol. Eq, Order, Show, and JSON remain predeclared protocol identities. Protocol names are not runtime values or nominal value types. A public callable may expose a user-protocol bound when that protocol is exported; a private bound is rejected with TPZ5524. Exported protocol method signatures obey the ordinary public-type visibility check. Importing a public type does not implicitly export the private protocols in its defining module. Derive eligibility is unchanged by importing a protocol name.

    Why

    Moving a generic API into a module must preserve its protocol contract. Protocols therefore use the ordinary explicit export and import boundary. One lexical binding table prevents a spelling from silently denoting separate declarations in the same scope. Defining identities survive aliases and keep independent modules from accidentally sharing conformances. Public signatures expose public contracts; private implementation details remain private.

    Source evidence

    A selected protocol alias preserves a public conformance

    Link
    example.tpzA selected protocol alias preserves a public conformanceOpen in Playground
    1. import std.io { writeLine }
    2. import contracts { Labelled as Described }
    3. import item { Item }
    4. function describe<T: Described>(value: T) -> string { Described.label(value) }
    5. writeLine(describe(Item { text: "selected" }))
    Expected outputexit 0
    selected
    
    $ topaz run example.tpz · target product 5.21.3
    contracts.tpzA selected protocol alias preserves a public conformanceOpen in Playground
    1. export protocol Labelled { function label(value: Self) -> string }
    item.tpzA selected protocol alias preserves a public conformanceOpen in Playground
    1. import contracts { Labelled }
    2. export record Item { text: string }
    3. impl Labelled<Item> {
    4. function label(value: Item) -> string { value.text }
    5. }
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. selected
    Source evidence

    A namespace-qualified protocol resolves in bounds, impl heads, and calls

    Link
    example.tpzA namespace-qualified protocol resolves in bounds, impl heads, and callsOpen in Playground
    1. import std.io { writeLine }
    2. import contracts as api
    3. import item { Item }
    4. function describe<T: api.Labelled>(value: T) -> string { api.Labelled.label(value) }
    5. writeLine(describe(Item { text: "qualified" }))
    Expected outputexit 0
    qualified
    
    $ topaz run example.tpz · target product 5.21.3
    contracts.tpzA namespace-qualified protocol resolves in bounds, impl heads, and callsOpen in Playground
    1. export protocol Labelled { function label(value: Self) -> string }
    item.tpzA namespace-qualified protocol resolves in bounds, impl heads, and callsOpen in Playground
    1. import contracts as api
    2. export record Item { text: string }
    3. impl api.Labelled<Item> {
    4. function label(value: Item) -> string { value.text }
    5. }
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. qualified
    Source evidence

    Equal protocol spellings in different modules keep different conformances

    Link
    example.tpzEqual protocol spellings in different modules keep different conformancesOpen in Playground
    1. import std.io { writeLine }
    2. import first { Marked as First }
    3. import second { Marked as Second }
    4. import value { Item }
    5. function printBoth<T: First + Second>(value: T) -> () {
    6. writeLine(First.mark(value))
    7. writeLine(Second.mark(value))
    8. }
    9. printBoth(Item { text: "value" })
    Expected outputexit 0
    first value
    second value
    
    $ topaz run example.tpz · target product 5.21.3
    value.tpzEqual protocol spellings in different modules keep different conformancesOpen in Playground
    1. export record Item { text: string }
    first.tpzEqual protocol spellings in different modules keep different conformancesOpen in Playground
    1. import value { Item }
    2. export protocol Marked { function mark(value: Self) -> string }
    3. impl Marked<Item> { function mark(value: Item) -> string { "first {value.text}" } }
    second.tpzEqual protocol spellings in different modules keep different conformancesOpen in Playground
    1. import value { Item }
    2. export protocol Marked { function mark(value: Self) -> string }
    3. impl Marked<Item> { function mark(value: Item) -> string { "second {value.text}" } }
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. first value
    2. second value
    Source evidence

    An exported function may expose an exported user protocol bound

    Link
    example.tpzAn exported function may expose an exported user protocol boundOpen in Playground
    1. export protocol Labelled { function label(value: Self) -> string }
    2. export function describe<T: Labelled>(value: T) -> string { Labelled.label(value) }
    Expected outputexit 0
    No stdout.
    $ topaz check example.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Source evidence

    Reject a public callable that exposes a private protocol bound

    Link
    example.tpzReject a public callable that exposes a private protocol boundOpen in Playground
    1. protocol Hidden { function label(value: Self) -> string }
    2. export function describe<T: Hidden>(value: T) -> string { Hidden.label(value) }
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5524 protocol-private-public-bound at example.tpz:2:29
    Source evidence

    A private protocol is not selected through import

    Link
    example.tpzA private protocol is not selected through importOpen in Playground
    1. import contracts { Hidden }
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --format json · target product 5.21.3
    contracts.tpzA private protocol is not selected through importOpen in Playground
    1. protocol Hidden { function label(value: Self) -> string }
    2. export let marker = 0
    Expected result

    Expected result

    Rejection during resolve. Nonzero exit.

    • TPZ3009 protocol-name-not-exported at example.tpz:1:20
    Source evidence

    A protocol and a value cannot bind the same module-level name

    Link
    example.tpzA protocol and a value cannot bind the same module-level nameOpen in Playground
    1. protocol Labelled { function label(value: Self) -> string }
    2. let Labelled = 0
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during resolve. Nonzero exit.

    • TPZ3008 protocol-binding-name-collision at example.tpz:2:5
    Source evidence

    An exported protocol cannot expose a private nominal parameter type

    Link
    example.tpzAn exported protocol cannot expose a private nominal parameter typeOpen in Playground
    1. record Hidden { n: int }
    2. export protocol Render { function render(value: Self, input: Hidden) -> string }
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during resolve. Nonzero exit.

    • TPZ3014 modules-public-private-nominal at example.tpz:2:62
    Source evidence

    An exported protocol signature may use an exported nominal type

    Link
    example.tpzAn exported protocol signature may use an exported nominal typeOpen in Playground
    1. export record Input { n: int }
    2. export protocol Render { function render(value: Self, input: Input) -> string }
    Expected outputexit 0
    No stdout.
    $ topaz check example.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Source evidence

    Namespace qualification whitespace cannot repeat one protocol bound

    Link
    example.tpzNamespace qualification whitespace cannot repeat one protocol boundOpen in Playground
    1. import contracts as api
    2. function describe<T: api.Labelled + api . Labelled>(value: T) -> string { api.Labelled.label(value) }
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --format json · target product 5.21.3
    contracts.tpzNamespace qualification whitespace cannot repeat one protocol boundOpen in Playground
    1. export protocol Labelled { function label(value: Self) -> string }
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5523 protocol-repeated-resolved-bound at example.tpz:2:37
    Source evidence

    Derived conformance

    Link

    For declaration grammar, see Records, enums and newtypes.

    Ruletopaz.rule.structures.protocols.derive.protocols

    Derivable protocols

    For a nongeneric record, enum, or newtype, derives requests Eq, Order, Show, or JSON conformance. Another protocol name produces TPZ5530.

    Why

    Generated operations need a defined derivation for each field, variant payload, or newtype base. The built-in protocols supply those rules for all three nominal declaration forms.

    Source evidence
    Ruletopaz.rule.structures.protocols.derive.show

    Show derivation

    Show derivation for a nongeneric record, enum, or newtype requires no member capability test. A newtype is displayed by its type name followed by its rendered base value in parentheses.

    Why

    Derived Show uses the shared value renderer, which handles every runtime value, including opaque values and functions. Its fields need no separate Show conformance.

    Source evidence

    Derivation field and payload requirements

    Eq, Order, and JSON derivation check every record field, enum payload, or newtype base for equality, total ordering, or both JSON conversion operations. Each operation uses the capability domain described below. JSON decoding follows the declared normalization rules, including Option null conversion. A member that lacks the required capability produces TPZ5530.

    Why

    A generated operation visits the constituent fields or payloads. Each constituent must support the corresponding equality, ordering, or JSON conversion operation.

    Source evidence

    JSON conformance

    JSON is an ordinary conformance with encode(value: Self) -> Result<JSONValue, string> and decode(value: JSONValue) -> Result<Self, string>. An implementation supplies both methods. An empty marker produces the missing-method error TPZ5022. The ordinary protocol call rules infer Self or select it through the first explicit call type argument, as in JSON.decode<Token>(tree). std.json.stringify calls encode and then the canonical JSONValue writer. std.json.parseAs<T> parses the text and calls the same decode implementation selected by JSON.decode<T>. A codec returns an Err result for invalid input or a value it cannot encode. These failures do not become checker diagnostics. The manual decoder owns the accepted JSONValue shape. Derived record decoders ignore unknown object fields and use declared field defaults when a field is missing. A missing required field, wrong kind, unknown enum tag, or wrong payload arity returns an Err result. Malformed text fails before the decoder is called. JSON promises both conversion operations, with the representation and normalization defined for the type. Conversion may normalize distinct source values to the same decoded value. Existing Option conversion maps None() to null and Some(v) to the representation of v, so Some(None()) and Some(()) normalize to None() when decoded through an enclosing Option. Decode a JSONValue with JSON.decode<T>(tree). The receiver member tree.decode<T>() is removed and produces the ordinary missing-member diagnostic TPZ5006. Parsing text and writing text remain distinct operations through parseAs and stringify.

    Why

    A public codec should customize the same operation that library callers use. Supplying encode and decode through ordinary protocol methods lets a nominal type define its external representation without a second ad hoc serialization API. JSONValue separates typed conversion from parsing and canonical text output. The Result channel reports data failures in the same way for manual and derived implementations. Existing record default and Option normalization rules remain explicit conversion semantics. Decoding an existing tree has one public spelling, the ordinary protocol-qualified call. A receiver alias would repeat the same operation with identical input and output.

    Source evidence

    Built-in protocol signatures

    The built-in method signatures are Show.show(value: Self) -> string, Eq.equals(a: Self, b: Self) -> bool, Order.compare(a: Self, b: Self) -> int, JSON.encode(value: Self) -> Result<JSONValue, string>, and JSON.decode(value: JSONValue) -> Result<Self, string>. JSONValue is the parsed JSON tree type exported by std.json. JSON decode uses the ordinary protocol rule for selecting Self when there is no conforming input value.

    Why

    Shared signatures let user types participate in the same display, equality, and ordering operations. Callers do not need separate APIs for each type.

    Source evidence

    Nested and recursive derivation requirements

    Deriving Eq, Order, or JSON composes the same operation on every record field, enum payload, or newtype base. A nested nominal uses its applicable manual or derived conformance after type arguments are substituted in its defining module. A manual method replaces structural inspection of that nominal; an outer derive must not bypass it or reject it because of private representation fields. A nested nominal with no required conformance produces TPZ5530. Built-in component families use the corresponding operation domain below.

    Declared recursive and mutually recursive conformances may refer to one another. A recursive edge closes a declared conformance reference; it does not excuse a missing component capability elsewhere in the cycle. Establish each conditional instance using independent generic bound evidence before linking its recursive descriptors. An active generic bound cannot prove itself; a back-edge between already applicable declared derives is a descriptor reference, not evidence for that bound. A descriptor is identified by defining module, nominal declaration, and normalized type arguments. Keep references and captured substitutions and instantiate them as values are traversed, rather than eagerly expanding a schema tree. Imported aliases keep the same descriptor identity. Conditional generic instances follow the ordinary binder and coherence rules.

    A derived JSON decoder follows the finite input tree. Each recursive aggregate step consumes a child JSON value; a descriptor loop that revisits the same input without structural progress returns Err. JSON starts at depth zero, permits depths through 128, and increments depth when descending to a record field, array element, map value, or enum payload. Descriptor references, Option wrapping, and newtype wrapping do not add a level. Data values follow value semantics. A depth above 128 returns an Err string beginning JSON_LIMIT:. The same traversal context covers nested derived dispatch. Descriptor expansion does not reset it. Manual method bodies retain ordinary function evaluation and resource behavior.

    Derived equality and ordering use the same nested conformance as direct protocol calls. A declared nominal Eq or Order implementation also supplies the corresponding comparison operators, so moving a comparison inside a wrapper does not change its meaning. A structural comparison that exhausts its budget raises the existing TPZ5007 guard. Show retains its total debug rendering operation and does not introduce an Eq, Order, or JSON member requirement.

    Why

    A nominal conformance is the type author’s chosen operation, so composition calls it. Recursive declarations describe finite data trees under value semantics. Data values cannot contain cycles. Lazy descriptor references and bounded traversal support recursive declarations and deep values.

    Source evidence

    Field capability domains for derivation

    Derivation follows the operation it generates. Eq asks whether two values are equal. Order supplies a total comparison. JSON supplies conversion to and from a JSON tree under the type's documented representation. These are different obligations, so admission is determined by the operation rather than a common whitelist. A nested nominal always uses the conformance described above. This table defines the listed component families; it does not assign capabilities to other built-ins. Duration capabilities follow its operations rule.

    ComponentEqOrderJSON
    int, string, boolValue equalityNumeric, Unicode scalar, or false-before-true orderJSON number, string, or boolean
    unit and nullEquality within the singleton typeZero within the same singleton typeJSON null, recovered through the requested target type
    floatIEEE numerical equalityNo total Order because NaN has no place in the existing numerical relationFinite numbers convert; NaN and infinities return Err
    ArraySame length and elementwise equalityLexicographic elements; a prefix precedes its extensionJSON array with recursive conversion
    OptionSame constructor and payload equalityNone() precedes Some(value). Compare present payloadsNone() encodes as null. Some(value) unwraps its payload. Null payloads normalize on decoding
    ResultSame constructor and payload equalityErr before Ok; compare payloads within the same constructorThe ordinary enum object with tag Ok or Err and one values entry
    MapSame lookup key identities and equal values, independent of insertion orderRequires an application-selected orderingString keys form a JSON object; other key types need an explicit schema
    SetSame membership key identities, independent of insertion orderRequires an application-selected orderingRequires an explicit sequence representation and duplicate policy
    Structural recordSame field set and fieldwise equalityCompare fields in Unicode scalar order of field namesString field names form a JSON object
    UnionCompare active member identities, then values within a memberAdmitted when every member widens to the same ordered scalar domainUntagged overlapping representations need an explicit nominal schema

    Map and Set equality reuse the key identity already used by lookup and membership. A custom Eq method does not replace stored-key equality. Map values use their Eq operation, including a manual nominal implementation. Insertion order does not affect either equality result. Functions and resources have no data-value equality, and regex denotation has no general equality operation. JSONValue and TOMLValue preserve representation information and require decoding or an explicit projection into the intended equality domain. Templates, ranges, mutable ByteBuffer values, and namespaces likewise have no automatic Eq conformance. Bytes compare bytes; Path and URL compare their canonical representations; Date, BigInt, and Decimal compare their values; Match and RoundingMode retain their declared value equality. IEEE equality is not an equivalence relation for NaN-containing values: NaN differs from itself and positive and negative zero compare equal. Eq does not promise otherwise.

    For Order, Bytes compare bytewise; Path and URL compare canonical strings; Date compares calendar days; BigInt and Decimal compare numbers. A record uses sorted field names, including a nominal record, so rearranging declarations cannot change priority. A derived enum compares variant names in Unicode scalar order and then positional payloads. A newtype compares its base. Each component must itself have Order. Map and Set describe membership rather than a selected key order; a heterogeneous union has no declared cross-type priority. A nominal manual implementation can state those application-specific choices. Float keeps its ordinary IEEE relational predicates, which may all be false for NaN, and therefore does not satisfy total Order. Opaque families without a declared total order do not gain one through derivation.

    JSON uses the existing record object, enum tag/values object, and transparent newtype representations. Result uses the enum representation rather than treating an Err payload as codec failure. Option converts through null and its payload values. Some(None()) and Some(()) can decode as None(). This normalization can map distinct source values to the same decoded value. JSONValue conversion preserves its tree, including stored number lexemes. Semantic scalar types such as Bytes, Path, URL, Date, BigInt, Decimal, Match, and RoundingMode need an explicitly chosen external schema through a nominal manual JSON implementation. Conversion does not silently pick a binary encoding, path base, unit, precision loss, or tag convention. Functions, resources, and templates cannot be reconstructed as data values. Derived conversion failure is TPZ5530 when a component type has no JSON capability. Malformed data and value-level encoding failures return an Err result during conversion.

    Finite float encoding chooses the shortest decimal significand that recovers the identical binary64 value under nearest, ties-to-even decoding. If shortest candidates differ, choose the decimal closest to the exact binary value, then the candidate with an even final significand digit. Render fixed notation when the normalized decimal exponent is at least -6 and below 21; otherwise use lowercase e, with no plus sign or redundant exponent zeroes. Omit redundant fractional zeroes. Positive zero is 0 and negative zero is -0. A nonfinite input returns Err without substituting null or a string. Decoding accepts any JSON number, including integer lexemes, rounds once to binary64 with nearest, ties-to-even, preserves a negative zero sign, and rejects overflow to a nonfinite result. Underflow rounds to a finite subnormal or signed zero. The shortest-number rule applies when encoding a float; parsing and re-serializing an existing JSONValue preserves its accepted number lexeme, and numberText/asInt retain their existing semantics. Recursive conversion follows the depth rule above and value semantics. Show remains the total debug-render operation and has no member capability gate.

    Duration has Eq and Order under its operations rule. Duration has no JSON capability in 6.0.

    Why

    Equal membership, lexicographic sequence order, total comparison, and JSON conversion answer different questions. Their requirements follow those questions. Map and Set equality should agree with lookup and membership instead of requiring an arbitrary insertion order. A total Order cannot inherit IEEE NaN comparison. A finite float can use the JSON number domain while the existing Result channel handles nonfinite values. JSON schemas that require an application choice stay explicit, and declaration layout does not choose a comparison priority.

    Source evidence

    Derived and manual conformance must be unique

    Why

    Generated implementations must participate in the same dispatch model as handwritten implementations.

    Source evidence

    Derive equality under an element bound

    Link
    example.tpzDerive equality under an element boundOpen in Playground
    1. record Box<T> derives<T: Eq> Eq<Box<T>> { value: T }
    2. let left: Box<int> = Box { value: 3 }
    3. let right: Box<int> = Box { value: 3 }
    4. let same: bool = Eq.equals(left, right)
    Expected outputexit 0
    No stdout.
    $ topaz check example.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Source evidence

    Derive the existing protocols for a newtype

    Link
    example.tpzDerive the existing protocols for a newtypeOpen in Playground
    1. newtype Item derives Eq, Order, Show, JSON = int
    2. function jsonIdentity<T: JSON>(value: T) -> T { value }
    3. let value = Item(7)
    4. let same: bool = Eq.equals(value, value)
    5. let order: int = Order.compare(value, Item(8))
    6. let shown: string = Show.show(value)
    7. let retained: Item = jsonIdentity(value)
    Expected outputexit 0
    No stdout.
    $ topaz check example.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Source evidence

    Call derived Eq and Show on a newtype

    Link
    example.tpzCall derived Eq and Show on a newtype
    1. import std.io { writeLine }
    2. newtype Item derives Eq, Show = int
    3. let value = Item(7)
    4. writeLine("{Eq.equals(value, Item(7))}")
    5. writeLine("{Eq.equals(value, Item(8))}")
    6. writeLine(Show.show(value))
    Expected outputexit 0
    true
    false
    Item(7)
    
    $ topaz run example.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. true
    2. false
    3. Item(7)
    Source evidence

    Use the conditional derive binder on a newtype

    Link
    example.tpzUse the conditional derive binder on a newtypeOpen in Playground
    1. newtype Box<T> derives<T: Eq> Eq<Box<T>> = T
    2. let left: Box<int> = Box(3)
    3. let right: Box<int> = Box(3)
    4. let same: bool = Eq.equals(left, right)
    Expected outputexit 0
    No stdout.
    $ topaz check example.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Source evidence

    Require an explicit newtype conformance

    Link
    example.tpzRequire an explicit newtype conformanceOpen in Playground
    1. newtype Item = int
    2. let value = Item(7)
    3. let same = Eq.equals(value, value)
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz · target product 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5522 structures-no-visible-eq-conformance at example.tpz:3:22
    Source evidence

    A manual JSON implementation supplies both conversions

    Link
    example.tpzA manual JSON implementation supplies both conversionsOpen in Playground
    1. import std.json { JSONValue }
    2. record Token { text: string }
    3. impl JSON<Token> {
    4. function encode(value: Token) -> Result<JSONValue, string> {
    5. JSON.encode(value.text)
    6. }
    7. function decode(value: JSONValue) -> Result<Token, string> {
    8. match (value.asString()) {
    9. case Some(text) => Ok(Token { text: text })
    10. case None() => Err("expected token string")
    11. }
    12. }
    13. }
    Expected outputexit 0
    No stdout.
    $ topaz check example.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Source evidence

    Text and tree conversion use the same manual JSON implementation

    Link
    example.tpzText and tree conversion use the same manual JSON implementation
    1. import std.io { writeLine }
    2. import std.json { JSONValue, parse, parseAs, stringify }
    3. record Token { text: string }
    4. impl JSON<Token> {
    5. function encode(value: Token) -> Result<JSONValue, string> {
    6. JSON.encode(value.text)
    7. }
    8. function decode(value: JSONValue) -> Result<Token, string> {
    9. match (value.asString()) {
    10. case Some(text) => Ok(Token { text: text })
    11. case None() => Err("expected token string")
    12. }
    13. }
    14. }
    15. writeLine(match (stringify(Token { text: "paper" })) {
    16. case Ok(text) => text
    17. case Err(message) => message
    18. })
    19. writeLine(match (parseAs<Token>("\"paper\"")) {
    20. case Ok(value) => value.text
    21. case Err(message) => message
    22. })
    23. writeLine(match (parseAs<Token>("17")) {
    24. case Ok(value) => value.text
    25. case Err(message) => message
    26. })
    27. writeLine(match (parse("\"ink\"")) {
    28. case Ok(tree) => match (JSON.decode<Token>(tree)) {
    29. case Ok(value) => value.text
    30. case Err(message) => message
    31. }
    32. case Err(_) => "parse failed"
    33. })
    Expected outputexit 0
    "paper"
    paper
    expected token string
    ink
    
    $ topaz run example.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. "paper"
    2. paper
    3. expected token string
    4. ink
    Source evidence

    An empty JSON marker omits the required conversion methods

    Link
    example.tpzAn empty JSON marker omits the required conversion methodsOpen in Playground
    1. record Token { text: string }
    2. impl JSON<Token> {}
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz · target product 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5022 missing-protocol-method at example.tpz:2:6
    Source evidence

    Derived JSON keeps record defaults and ignores unknown object fields

    Link
    example.tpzDerived JSON keeps record defaults and ignores unknown object fields
    1. import std.io { writeLine }
    2. import std.json { parseAs, stringify }
    3. record Item derives JSON { name: string, count: int = 7 }
    4. let text = match (stringify({ name: "ink", extra: true })) {
    5. case Ok(value) => value
    6. case Err(message) => message
    7. }
    8. writeLine(match (parseAs<Item>(text)) {
    9. case Ok(value) => "{value.name}:{value.count}"
    10. case Err(message) => message
    11. })
    12. writeLine(match (parseAs<Item>("17")) {
    13. case Ok(_) => "unexpected value"
    14. case Err(_) => "wrong shape"
    15. })
    16. writeLine(match (parseAs<Item>("[")) {
    17. case Ok(_) => "unexpected value"
    18. case Err(_) => "malformed text"
    19. })
    Expected outputexit 0
    ink:7
    wrong shape
    malformed text
    
    $ topaz run example.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. ink:7
    2. wrong shape
    3. malformed text
    Source evidence

    JSONValue decoding uses the ordinary protocol call

    Link
    example.tpzJSONValue decoding uses the ordinary protocol callOpen in Playground
    1. import std.json { JSONValue }
    2. function read(tree: JSONValue) -> Result<int, string> {
    3. tree.decode<int>()
    4. }
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5006 stdlib-removed-member at example.tpz:3:8
    Source evidence

    Decode a JSONValue with one protocol-qualified form

    Link
    example.tpzDecode a JSONValue with one protocol-qualified formOpen in Playground
    1. import std.json { JSONValue }
    2. function read(tree: JSONValue) -> Result<int, string> {
    3. JSON.decode<int>(tree)
    4. }
    Expected outputexit 0
    No stdout.
    $ topaz check example.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Source evidence

    Derived equality and operators use the inner manual equality

    Link
    example.tpzDerived equality and operators use the inner manual equality
    1. import std.io { writeLine }
    2. record Token { key: int, noise: int }
    3. impl Eq<Token> {
    4. function equals(a: Token, b: Token) -> bool { a.key == b.key }
    5. }
    6. record Box derives Eq { item: Token }
    7. let a = Box { item: Token { key: 1, noise: 10 } }
    8. let b = Box { item: Token { key: 1, noise: 20 } }
    9. let c = Box { item: Token { key: 2, noise: 10 } }
    10. writeLine("{Eq.equals(a, b)}")
    11. writeLine("{a == b}")
    12. writeLine("{Eq.equals(a, c)}")
    13. writeLine("{a != c}")
    Expected outputexit 0
    true
    true
    false
    true
    
    $ topaz run example.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. true
    2. true
    3. false
    4. true
    Source evidence

    A manual equality hides an opaque field from outer operators

    Link
    example.tpzA manual equality hides an opaque field from outer operators
    1. import std.io { writeLine }
    2. record Callback { key: int, run: () -> int }
    3. impl Eq<Callback> {
    4. function equals(a: Callback, b: Callback) -> bool { a.key == b.key }
    5. }
    6. record Box derives Eq { item: Callback }
    7. let a = Box { item: Callback { key: 1, run: () => 7 } }
    8. let b = Box { item: Callback { key: 1, run: () => 9 } }
    9. writeLine("{a == b}")
    10. writeLine("{Eq.equals(a, b)}")
    Expected outputexit 0
    true
    true
    
    $ topaz run example.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. true
    2. true
    Source evidence

    Derived ordering and operators use the inner manual ordering

    Link
    example.tpzDerived ordering and operators use the inner manual ordering
    1. import std.io { writeLine }
    2. record Rank { score: int }
    3. impl Order<Rank> {
    4. function compare(a: Rank, b: Rank) -> int {
    5. if (a.score > b.score) { -1 } else if (a.score < b.score) { 1 } else { 0 }
    6. }
    7. }
    8. record Box derives Order { item: Rank }
    9. let a = Box { item: Rank { score: 9 } }
    10. let b = Box { item: Rank { score: 3 } }
    11. writeLine("{Order.compare(a, b)}")
    12. writeLine("{a < b}")
    13. writeLine("{Order.compare(b, a)}")
    14. writeLine("{b > a}")
    Expected outputexit 0
    -1
    true
    1
    true
    
    $ topaz run example.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. -1
    2. true
    3. 1
    4. true
    Source evidence

    Derived JSON uses the nested manual codec in both directions

    Link
    example.tpzDerived JSON uses the nested manual codec in both directions
    1. import std.io { writeLine }
    2. import std.json { JSONValue, parseAs, stringify }
    3. record Token { text: string }
    4. impl JSON<Token> {
    5. function encode(value: Token) -> Result<JSONValue, string> {
    6. JSON.encode(value.text)
    7. }
    8. function decode(value: JSONValue) -> Result<Token, string> {
    9. match (value.asString()) {
    10. case Some(text) => Ok(Token { text: text })
    11. case None() => Err("expected token string")
    12. }
    13. }
    14. }
    15. record Envelope derives JSON { token: Token }
    16. let text = match (stringify(Envelope { token: Token { text: "paper" } })) {
    17. case Ok(value) => value
    18. case Err(message) => message
    19. }
    20. writeLine(text)
    21. writeLine(match (parseAs<Envelope>(text)) {
    22. case Ok(value) => value.token.text
    23. case Err(message) => message
    24. })
    Expected outputexit 0
    {"token":"paper"}
    paper
    
    $ topaz run example.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. {"token":"paper"}
    2. paper
    Source evidence

    A finite recursive enum value encodes and decodes

    Link
    example.tpzA finite recursive enum value encodes and decodes
    1. import std.io { writeLine }
    2. import std.json { parseAs, stringify }
    3. enum Chain derives Eq, JSON { End, Next(int, Chain) }
    4. let original = Chain.Next(7, Chain.End())
    5. let text = match (stringify(original)) {
    6. case Ok(value) => value
    7. case Err(message) => message
    8. }
    9. writeLine(text)
    10. writeLine(match (parseAs<Chain>(text)) {
    11. case Ok(value) => "{Eq.equals(value, original)}"
    12. case Err(message) => message
    13. })
    Expected outputexit 0
    {"tag":"Next","values":[7,{"tag":"End"}]}
    true
    
    $ topaz run example.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. {"tag":"Next","values":[7,{"tag":"End"}]}
    2. true
    Source evidence

    Mutually recursive declarations share conformance references

    Link
    example.tpzMutually recursive declarations share conformance referencesOpen in Playground
    1. record Folder derives Eq, JSON { entries: Array<Entry> }
    2. enum Entry derives Eq, JSON { File(string), Directory(Folder) }
    Expected outputexit 0
    No stdout.
    $ topaz check example.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Source evidence

    Inserted values keep recursive JSON finite

    Link
    example.tpzInserted values keep recursive JSON finite
    1. import std.io { writeLine }
    2. import std.json { stringify }
    3. record Node derives JSON { children: Array<Node> }
    4. let mut children: Array<Node> = []
    5. let root = Node { children: children }
    6. children.push(root)
    7. writeLine("{root.children.length}:{children.length}")
    8. writeLine(match (stringify(root)) {
    9. case Ok(value) => "Ok {value}"
    10. case Err(_) => "unexpected error"
    11. })
    Expected outputexit 0
    0:1
    Ok {"children":[]}
    
    $ topaz run example.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. 0:1
    2. Ok {"children":[]}

    The record retains its empty children value after children.push(root). Insertion follows value semantics and cannot create a cycle.

    Source evidence

    Inserted values keep recursive comparison finite

    Link
    example.tpzInserted values keep recursive comparison finite
    1. import std.io { writeLine }
    2. record Node derives Eq { children: Array<Node> }
    3. let mut children: Array<Node> = []
    4. let root = Node { children: children }
    5. let peer = Node { children: children }
    6. children.push(root)
    7. writeLine("{Eq.equals(root, peer)}:{root.children.length}:{children.length}")
    Expected outputexit 0
    true:0:1
    
    $ topaz run example.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. true:0:1

    The record retains its empty children value after children.push(root). Insertion follows value semantics and cannot create a cycle.

    Source evidence

    Outer derivation does not invent an inner nominal conformance

    Link
    example.tpzOuter derivation does not invent an inner nominal conformanceOpen in Playground
    1. record Inner { value: int }
    2. record Outer derives Eq { item: Inner }
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5530 structures-derive-eq-field at example.tpz:2:22
    Source evidence

    Nested dispatch preserves the imported nominal identity

    Link
    example.tpzNested dispatch preserves the imported nominal identityOpen in Playground
    1. import std.io { writeLine }
    2. import tokens { Token as Ink }
    3. record Box derives Eq { value: Ink }
    4. let a = Box { value: Ink { key: 4, label: "first" } }
    5. let b = Box { value: Ink { key: 4, label: "second" } }
    6. writeLine("{Eq.equals(a, b)}")
    Expected outputexit 0
    true
    
    $ topaz run example.tpz · target product 5.21.3
    tokens.tpzNested dispatch preserves the imported nominal identityOpen in Playground
    1. export record Token { key: int, label: string }
    2. impl Eq<Token> {
    3. function equals(a: Token, b: Token) -> bool { a.key == b.key }
    4. }
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. true
    Source evidence

    Map and Set equality follows membership while Map values use Eq

    Link
    example.tpzMap and Set equality follows membership while Map values use Eq
    1. import std.io { writeLine }
    2. record Token { key: int, label: string }
    3. impl Eq<Token> {
    4. function equals(a: Token, b: Token) -> bool { a.key == b.key }
    5. }
    6. record Index derives Eq { values: Map<string, Token>, keys: Set<int> }
    7. let a = Index {
    8. values: map { "a": Token { key: 1, label: "first" }, "b": Token { key: 2, label: "second" } },
    9. keys: set { 1, 2 },
    10. }
    11. let b = Index {
    12. values: map { "b": Token { key: 2, label: "changed" }, "a": Token { key: 1, label: "changed" } },
    13. keys: set { 2, 1 },
    14. }
    15. let c = Index {
    16. values: map { "a": Token { key: 9, label: "first" }, "b": Token { key: 2, label: "second" } },
    17. keys: set { 1, 2 },
    18. }
    19. writeLine("{Eq.equals(a, b)}")
    20. writeLine("{a == b}")
    21. writeLine("{Eq.equals(a, c)}")
    Expected outputexit 0
    true
    true
    false
    
    $ topaz run example.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. true
    2. true
    3. false
    Source evidence

    Boolean, singleton, sequence, sum, and structural record orders compose

    Link
    example.tpzBoolean, singleton, sequence, sum, and structural record orders compose
    1. import std.io { writeLine }
    2. record Flag derives Order { value: bool }
    3. record UnitBox derives Order { value: () }
    4. record NullBox derives Order { value: null }
    5. record Sequence derives Order { value: Array<int> }
    6. record Optional derives Order { value: Option<int> }
    7. record Outcome derives Order { value: Result<int, int> }
    8. record Fields derives Order { value: { z: int, a: int } }
    9. writeLine("{Order.compare(Flag { value: false }, Flag { value: true })}")
    10. writeLine("{Order.compare(UnitBox { value: () }, UnitBox { value: () })}")
    11. writeLine("{Order.compare(NullBox { value: null }, NullBox { value: null })}")
    12. writeLine("{Order.compare(Sequence { value: [1, 2] }, Sequence { value: [1, 3] })}")
    13. writeLine("{Order.compare(Sequence { value: [1] }, Sequence { value: [1, 0] })}")
    14. writeLine("{Order.compare(Optional { value: None() }, Optional { value: Some(0) })}")
    15. writeLine("{Order.compare(Outcome { value: Err(7) }, Outcome { value: Ok(0) })}")
    16. writeLine("{Order.compare(Fields { value: { z: 0, a: 2 } }, Fields { value: { z: 100, a: 1 } })}")
    Expected outputexit 0
    -1
    0
    0
    -1
    -1
    -1
    -1
    1
    
    $ topaz run example.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. -1
    2. 0
    3. 0
    4. -1
    5. -1
    6. -1
    7. -1
    8. 1
    Source evidence

    Reordering record fields or enum declarations does not change derived order

    Link
    example.tpzReordering record fields or enum declarations does not change derived order
    1. import std.io { writeLine }
    2. record First derives Order { z: int, a: int }
    3. record Second derives Order { a: int, z: int }
    4. enum Forward derives Order { Zed, Alpha }
    5. enum Reordered derives Order { Alpha, Zed }
    6. writeLine("{Order.compare(First { z: 0, a: 2 }, First { z: 100, a: 1 })}")
    7. writeLine("{Order.compare(Second { z: 0, a: 2 }, Second { z: 100, a: 1 })}")
    8. writeLine("{Order.compare(Forward.Alpha(), Forward.Zed())}")
    9. writeLine("{Order.compare(Reordered.Alpha(), Reordered.Zed())}")
    Expected outputexit 0
    1
    1
    -1
    -1
    
    $ topaz run example.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. 1
    2. 1
    3. -1
    4. -1
    Source evidence

    A union within one ordered scalar domain keeps that order

    Link
    example.tpzA union within one ordered scalar domain keeps that order
    1. import std.io { writeLine }
    2. record Choice derives Order { value: 1 | 2 }
    3. writeLine("{Order.compare(Choice { value: 1 }, Choice { value: 2 })}")
    Expected outputexit 0
    -1
    
    $ topaz run example.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. -1
    Source evidence

    Map membership has no implicit total ordering

    Link
    example.tpzMap membership has no implicit total orderingOpen in Playground
    1. record Lookup derives Order { values: Map<string, int> }
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz · target product 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5530 structures-derive-order-field at example.tpz:1:23
    Source evidence

    Set membership has no implicit total ordering

    Link
    example.tpzSet membership has no implicit total orderingOpen in Playground
    1. record Membership derives Order { values: Set<int> }
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz · target product 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5530 structures-derive-order-field at example.tpz:1:27
    Source evidence

    Different ordered scalar domains do not acquire a cross-type order

    Link
    example.tpzDifferent ordered scalar domains do not acquire a cross-type orderOpen in Playground
    1. record Mixed derives Order { value: int | string }
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz · target product 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5530 structures-derive-order-field at example.tpz:1:22
    Source evidence

    JSON object keys require strings without an implicit key conversion

    Link
    example.tpzJSON object keys require strings without an implicit key conversionOpen in Playground
    1. record NumericKeys derives JSON { values: Map<int, string> }
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz · target product 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5530 structures-derive-json-field at example.tpz:1:28
    Source evidence

    A Set needs an explicitly chosen JSON representation

    Link
    example.tpzA Set needs an explicitly chosen JSON representationOpen in Playground
    1. record Membership derives JSON { values: Set<int> }
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz · target product 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5530 structures-derive-json-field at example.tpz:1:27
    Source evidence

    Eq uses IEEE float equality and does not claim NaN reflexivity

    Link
    example.tpzEq uses IEEE float equality and does not claim NaN reflexivity
    1. import std.io { writeLine }
    2. record Reading derives Eq { value: float }
    3. let sample = Reading { value: 0.0 / 0.0 }
    4. writeLine("{Eq.equals(sample, sample)}")
    5. writeLine("{sample == sample}")
    Expected outputexit 0
    false
    false
    
    $ topaz run example.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. false
    2. false
    Source evidence

    Finite floats have one JSON number conversion and preserve negative zero

    Link
    example.tpzFinite floats have one JSON number conversion and preserve negative zero
    1. import std.io { writeLine }
    2. import std.json { parseAs, stringify }
    3. record Reading derives JSON { value: float }
    4. writeLine(match (stringify(Reading { value: 1.5 })) {
    5. case Ok(text) => text
    6. case Err(message) => message
    7. })
    8. writeLine(match (stringify(-0.0)) {
    9. case Ok(text) => text
    10. case Err(message) => message
    11. })
    12. writeLine(match (stringify(1000.0)) {
    13. case Ok(text) => text
    14. case Err(message) => message
    15. })
    16. writeLine(match (parseAs<float>("-0")) {
    17. case Ok(value) => "{(1.0 / value) < 0.0}"
    18. case Err(message) => message
    19. })
    20. writeLine(match (parseAs<float>("1")) {
    21. case Ok(value) => "{value == 1.0}"
    22. case Err(message) => message
    23. })
    24. writeLine(match (stringify(0.0 / 0.0)) {
    25. case Ok(_) => "unexpected value"
    26. case Err(_) => "nonfinite rejected"
    27. })
    28. writeLine(match (parseAs<float>("1e9999")) {
    29. case Ok(_) => "unexpected value"
    30. case Err(_) => "overflow rejected"
    31. })
    Expected outputexit 0
    {"value":1.5}
    -0
    1000
    true
    true
    nonfinite rejected
    overflow rejected
    
    $ topaz run example.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. {"value":1.5}
    2. -0
    3. 1000
    4. true
    5. true
    6. nonfinite rejected
    7. overflow rejected
    Source evidence

    Option JSON conversion explicitly normalizes null payloads

    Link
    example.tpzOption JSON conversion explicitly normalizes null payloads
    1. import std.io { writeLine }
    2. import std.json { parseAs, stringify }
    3. let original: Option<Option<int>> = Some(None())
    4. writeLine(match (stringify(original)) {
    5. case Ok(text) => text
    6. case Err(message) => message
    7. })
    8. writeLine(match (parseAs<Option<Option<int>>>("null")) {
    9. case Ok(value) => match (value) {
    10. case None() => "none"
    11. case Some(_) => "some"
    12. }
    13. case Err(message) => message
    14. })
    Expected outputexit 0
    null
    none
    
    $ topaz run example.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. null
    2. none
    Source evidence

    Result data uses the same tagged representation as enum data

    Link
    example.tpzResult data uses the same tagged representation as enum data
    1. import std.io { writeLine }
    2. import std.json { parseAs, stringify }
    3. record Outcome derives JSON { value: Result<int, string> }
    4. let value = Outcome { value: Err("paper") }
    5. let text = match (stringify(value)) {
    6. case Ok(encoded) => encoded
    7. case Err(message) => message
    8. }
    9. writeLine(text)
    10. writeLine(match (parseAs<Outcome>(text)) {
    11. case Ok(decoded) => match (decoded.value) {
    12. case Ok(number) => "{number}"
    13. case Err(message) => message
    14. }
    15. case Err(message) => message
    16. })
    Expected outputexit 0
    {"value":{"tag":"Err","values":["paper"]}}
    paper
    
    $ topaz run example.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. {"value":{"tag":"Err","values":["paper"]}}
    2. paper
    Source evidence

    Receiver methods and self

    Link

    Receiver self parameter

    A receiver method starts with bare self, with no annotation, default, or variadic marker. Its type is the implementation target, and a missing or differently written receiver produces TPZ5022.

    Why

    The implementation target already determines the receiver type. A bare self avoids repeating that type in every method and gives receiver binding one form.

    Source evidence

    Unique receiver method names

    Repeating a receiver method name for the same type produces TPZ5008.

    Why

    Member lookup needs one method for a given type and name. Duplicate definitions would make a call depend on declaration order.

    Source evidence

    Receiver member collisions

    A receiver method name that collides with a record field or reserved built-in member produces TPZ5022.

    Why

    Fields, built-in members, and receiver methods share member access syntax. Distinct names prevent the same access from denoting different operations.

    Source evidence
    Ruletopaz.rule.structures.protocols.receiver.export

    Exporting receiver methods

    export may precede an individual receiver method declaration.

    Why

    Exporting individual methods lets a type expose useful operations while keeping helpers private. It uses the same declaration prefix as other module exports.

    Source evidence

    Using a resource

    Link

    Scope cleanup runs on normal completion and structured exits. The File resource contract defines repeated close and host close failures. Ordinary runtime faults follow the fault cleanup rule.

    Ruletopaz.rule.structures.protocols.using.file-type

    File resource bindings

    using name = (expression) { ... } checks the resource expression against File and gives its block binding type File.

    Why

    The cleanup path invokes the File close operation. Requiring a File ensures that this operation is defined for the acquired resource.

    Source evidence

    Immutable resource bindings

    A using resource binding is immutable.

    Why

    Cleanup is registered for the acquired resource. Preventing reassignment keeps the visible binding tied to the resource that will be closed.

    Source evidence
    Ruletopaz.rule.structures.source.using-cleanup-order

    Using acquisition and cleanup order

    A using initializer is evaluated once. After successful acquisition, its automatic close is registered before the body. An initializer that propagates an Err registers no close for that resource. Body defers run before its close under the scope cleanup rule, including ? propagation. Capturing the File does not keep it open beyond the using scope. Ordinary runtime faults follow the fault cleanup rule.

    Ordinary runtime faults follow the fault cleanup rule.

    Why

    Body defers may still need the resource. Running them before close and leaving nested scopes from inside to outside keeps those dependencies alive.

    Source evidence

    Explicit close and failure boundaries

    using keeps automatic File.close registered after an explicit close.

    File.close follows the File resource contract. That contract defines repeated close, shared closed state and close faults.

    The scope cleanup rule defines cleanup order, structured exits and retained body values. Ordinary runtime faults follow the fault cleanup rule.

    Deferred runtime and host errors follow the run exit contract and test result contract. These contracts determine process failure and test output independently of the stored body value.

    At the cited native-host source revision, close sets the shared closed flag. Repeating that assignment preserves the closed state. Later reads and writes report a closed file. The File resource contract defines the required behavior across hosts.

    Why

    Cleanup registration belongs to the scope, so an explicit close does not cancel it. The File resource contract owns the result of closing. Command contracts own exit status and test output.

    Source evidence

    Complete examples

    Link

    Implement and call a protocol

    Link
    example.tpzImplement and call a protocol
    1. protocol Labelled {
    2. function label(value: Self) -> string
    3. }
    4. record Item { name: string }
    5. impl Labelled<Item> {
    6. function label(value: Item) -> string { value.name }
    7. }
    8. let text: string = Labelled.label(Item { name: "Moss" })
    Recorded outputexit 0
    example.tpz: types-ok (1 module)
    example.tpz: resolve-ok (1 module)
    
    $ topaz check example.tpz · recorded with topaz 5.21.3
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Expected stdout
    1. example.tpz: types-ok (1 module)
    2. example.tpz: resolve-ok (1 module)
    Observed assertions · passed · 5.21.3

    Exit code 0.

    Observed stdout
    1. example.tpz: types-ok (1 module)
    2. example.tpz: resolve-ok (1 module)

    No stderr.

    • Passed · expected 0; observed 0
    • Passed · stdout matches the authored expectation
    • Passed · stderr matches the authored expectation
    Source evidence

    Reject an explicit protocol receiver binder

    Link
    example.tpzReject an explicit protocol receiver binderOpen in Playground
    1. protocol Labelled<T> {
    2. function label(value: T) -> string
    3. }
    4. record Item { name: string }
    5. impl Labelled<Item> {
    6. function label(value: Item) -> string { value.name }
    7. }
    8. let text: string = Labelled.label(Item { name: "Moss" })
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --compiler self --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during parse. Nonzero exit.

    • TPZ2001 protocol-explicit-receiver-binder at example.tpz:1:18
    Source evidence

    Use Self for the conforming type

    Link
    example.tpzUse Self for the conforming typeOpen in Playground
    1. protocol Labelled {
    2. function label(value: Self) -> string
    3. }
    4. record Item { name: string }
    5. impl Labelled<Item> {
    6. function label(value: Item) -> string { value.name }
    7. }
    8. let text: string = Labelled.label(Item { name: "Moss" })
    Expected outputexit 0
    No stdout.
    $ topaz check example.tpz --compiler self --format json · target product 5.21.3
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Source evidence

    Declare a receiver method

    Link
    example.tpzDeclare a receiver method
    1. record Item { name: string }
    2. impl Item {
    3. function label(self) -> string { self.name }
    4. }
    5. let item = Item { name: "Moss" }
    6. let text: string = item.label()
    Recorded outputexit 0
    example.tpz: types-ok (1 module)
    example.tpz: resolve-ok (1 module)
    
    $ topaz check example.tpz · recorded with topaz 5.21.3
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Expected stdout
    1. example.tpz: types-ok (1 module)
    2. example.tpz: resolve-ok (1 module)
    Observed assertions · passed · 5.21.3

    Exit code 0.

    Observed stdout
    1. example.tpz: types-ok (1 module)
    2. example.tpz: resolve-ok (1 module)

    No stderr.

    • Passed · expected 0; observed 0
    • Passed · stdout matches the authored expectation
    • Passed · stderr matches the authored expectation
    Source evidence

    Derive built-in protocols

    Link
    example.tpzDerive built-in protocols
    1. record Pair derives Eq, Order, Show, JSON {
    2. left: int,
    3. right: int,
    4. }
    5. let pair = Pair { left: 1, right: 2 }
    6. let same: bool = Eq.equals(pair, pair)
    Recorded outputexit 0
    example.tpz: types-ok (1 module)
    example.tpz: resolve-ok (1 module)
    
    $ topaz check example.tpz · recorded with topaz 5.21.3
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Expected stdout
    1. example.tpz: types-ok (1 module)
    2. example.tpz: resolve-ok (1 module)
    Observed assertions · passed · 5.21.3

    Exit code 0.

    Observed stdout
    1. example.tpz: types-ok (1 module)
    2. example.tpz: resolve-ok (1 module)

    No stderr.

    • Passed · expected 0; observed 0
    • Passed · stdout matches the authored expectation
    • Passed · stderr matches the authored expectation
    Source evidence

    Check a File resource binding

    Link
    example.tpzCheck a File resource binding
    1. function manage(file: File) -> () {
    2. using resource = (file) {
    3. let handle: File = resource
    4. }
    5. }
    Recorded outputexit 0
    example.tpz: types-ok (1 module)
    example.tpz: resolve-ok (1 module)
    
    $ topaz check example.tpz · recorded with topaz 5.21.3
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Expected stdout
    1. example.tpz: types-ok (1 module)
    2. example.tpz: resolve-ok (1 module)
    Observed assertions · passed · 5.21.3

    Exit code 0.

    Observed stdout
    1. example.tpz: types-ok (1 module)
    2. example.tpz: resolve-ok (1 module)

    No stderr.

    • Passed · expected 0; observed 0
    • Passed · stdout matches the authored expectation
    • Passed · stderr matches the authored expectation
    Source evidence

    Reject an incomplete implementation

    Link
    example.tpzReject an incomplete implementationOpen in Playground
    1. protocol Labelled {
    2. function label(value: Self) -> string
    3. }
    4. record Item { name: string }
    5. impl Labelled<Item> {}
    Recorded outputexit 1
    No stdout.
    error[TPZ5022]: `impl Labelled<Item>` is missing method `label`
     --> example.tpz:7:6
      |
    7 | impl Labelled<Item> {}
      |      ^^^^^^^^
    
    example.tpz: 1 type diagnostic
    
    $ topaz check example.tpz · recorded with topaz 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5022 missing-protocol-method at example.tpz:7:6
    Observed assertions · passed · 5.21.3

    Exit code 1.

    No stdout.

    Observed stderr
    1. error[TPZ5022]: `impl Labelled<Item>` is missing method `label`
    2. --> example.tpz:7:6
    3. |
    4. 7 | impl Labelled<Item> {}
    5. | ^^^^^^^^
    6. example.tpz: 1 type diagnostic
    • Passed · expected nonzero; observed 1
    • Passed · stdout matches the authored expectation
    • Passed · checking TPZ5022; cause missing-protocol-method; primary location asserted; additional diagnostics allow
    Source evidence

    A foreign protocol on a foreign type violates ownership

    Link
    example.tpzA foreign protocol on a foreign type violates ownershipOpen in Playground
    1. impl Show<int> {
    2. function show(value: int) -> string { "number" }
    3. }
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5520 protocol-orphan-implementation at example.tpz:1:11

    The preserved receipt belongs to an earlier revision that rejected a local protocol on int. This revision checks the orphan case where both Show and int belong to another module. The 6.0 contract permits the local-protocol case shown separately.

    Source evidence

    Reject a repeated conformance

    Link
    example.tpzReject a repeated conformanceOpen in Playground
    1. protocol Labelled {
    2. function label(value: Self) -> string
    3. }
    4. record Item { name: string }
    5. impl Labelled<Item> {
    6. function label(value: Item) -> string { value.name }
    7. }
    8. impl Labelled<Item> {
    9. function label(value: Item) -> string { value.name }
    10. }
    Recorded outputexit 1
    No stdout.
    error[TPZ5521]: `Item` already conforms to `Labelled` by a previous `impl`; a conformance must be unique
      --> example.tpz:11:15
       |
    11 | impl Labelled<Item> {
       |               ^^^^
    
    example.tpz: 1 type diagnostic
    
    $ topaz check example.tpz · recorded with topaz 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5521 duplicate-protocol-conformance at example.tpz:11:15
    Observed assertions · passed · 5.21.3

    Exit code 1.

    No stdout.

    Observed stderr
    1. error[TPZ5521]: `Item` already conforms to `Labelled` by a previous `impl`; a conformance must be unique
    2. --> example.tpz:11:15
    3. |
    4. 11 | impl Labelled<Item> {
    5. | ^^^^
    6. example.tpz: 1 type diagnostic
    • Passed · expected nonzero; observed 1
    • Passed · stdout matches the authored expectation
    • Passed · checking TPZ5521; cause duplicate-protocol-conformance; primary location asserted; additional diagnostics allow
    Source evidence

    Reject a call without conformance

    Link
    example.tpzReject a call without conformanceOpen in Playground
    1. protocol Labelled {
    2. function label(value: Self) -> string
    3. }
    4. record Item { name: string }
    5. let text: string = Labelled.label(Item { name: "Moss" })
    Recorded outputexit 1
    No stdout.
    error[TPZ5522]: `Item` does not conform to `Labelled`
     --> example.tpz:7:35
      |
    7 | let text: string = Labelled.label(Item { name: "Moss" })
      |                                   ^^^^^^^^^^^^^^^^^^^^^
    
    example.tpz: 1 type diagnostic
    
    $ topaz check example.tpz · recorded with topaz 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5522 protocol-call-argument-does-not-conform at example.tpz:7:35
    Observed assertions · passed · 5.21.3

    Exit code 1.

    No stdout.

    Observed stderr
    1. error[TPZ5522]: `Item` does not conform to `Labelled`
    2. --> example.tpz:7:35
    3. |
    4. 7 | let text: string = Labelled.label(Item { name: "Moss" })
    5. | ^^^^^^^^^^^^^^^^^^^^^
    6. example.tpz: 1 type diagnostic
    • Passed · expected nonzero; observed 1
    • Passed · stdout matches the authored expectation
    • Passed · checking TPZ5522; cause protocol-call-argument-does-not-conform; primary location asserted; additional diagnostics allow
    Source evidence

    Reject a user-defined protocol in derives

    Link
    example.tpzReject a user-defined protocol in derivesOpen in Playground
    1. protocol Labelled {
    2. function label(value: Self) -> string
    3. }
    4. record Item derives Labelled { name: string }
    Recorded outputexit 1
    No stdout.
    error[TPZ5530]: unknown protocol `Labelled` in `derives` for `Item`; derivable protocols are `Eq`, `Order`, `Show`, `JSON`
     --> example.tpz:5:21
      |
    5 | record Item derives Labelled { name: string }
      |                     ^^^^^^^^
    
    example.tpz: 1 type diagnostic
    
    $ topaz check example.tpz · recorded with topaz 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5530 protocol-is-not-derivable at example.tpz:5:21
    Observed assertions · passed · 5.21.3

    Exit code 1.

    No stdout.

    Observed stderr
    1. error[TPZ5530]: unknown protocol `Labelled` in `derives` for `Item`; derivable protocols are `Eq`, `Order`, `Show`, `JSON`
    2. --> example.tpz:5:21
    3. |
    4. 5 | record Item derives Labelled { name: string }
    5. | ^^^^^^^^
    6. example.tpz: 1 type diagnostic
    • Passed · expected nonzero; observed 1
    • Passed · stdout matches the authored expectation
    • Passed · checking TPZ5530; cause protocol-is-not-derivable; primary location asserted; additional diagnostics allow
    Source evidence

    Acquire once, run body defers, then close

    Link
    example.tpzAcquire once, run body defers, then closeOpen in Playground
    1. import std.io
    2. let mut acquisitions = 0
    3. let mut later: () -> string = () => "unset"
    4. function acquire() -> Result<File, string> {
    5. acquisitions += 1
    6. io.open("input.txt")
    7. }
    8. function scenario() -> Result<int, string> {
    9. using file = (acquire()?) {
    10. later = () => match (file.read()) {
    11. case Ok(text) => text
    12. case Err(e) => e
    13. }
    14. defer { io.writeLine(match (file.read()) {
    15. case Ok(text) => "defer:{text}"
    16. case Err(e) => "defer:{e}"
    17. }) }
    18. io.writeLine("body")
    19. 99
    20. }
    21. Ok(7)
    22. }
    23. let ignored = scenario()
    24. io.writeLine("acquisitions:{acquisitions}")
    25. io.writeLine(later())
    Expected outputexit 0
    body
    defer:payload
    acquisitions:1
    file is closed
    
    $ topaz run example.tpz · target product 5.21.3
    input.txtAcquire once, run body defers, then closeOpen in Playground
    1. payload
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. body
    2. defer:payload
    3. acquisitions:1
    4. file is closed
    Source evidence

    Close on return, propagation, break, and continue

    Link
    example.tpzClose on return, propagation, break, and continueOpen in Playground
    1. import std.io
    2. let mut later: () -> string = () => "unset"
    3. function fail() -> Result<int, string> { Err("stop") }
    4. function onReturn() -> Result<int, string> {
    5. using file = (io.open("input.txt")?) {
    6. later = () => match (file.read()) {
    7. case Ok(text) => text
    8. case Err(e) => e
    9. }
    10. return Ok(7)
    11. }
    12. Ok(0)
    13. }
    14. let ignoredonReturn = onReturn()
    15. io.writeLine("onReturn:{later()}")
    16. function onQuestion() -> Result<int, string> {
    17. using file = (io.open("input.txt")?) {
    18. later = () => match (file.read()) {
    19. case Ok(text) => text
    20. case Err(e) => e
    21. }
    22. fail()?
    23. }
    24. Ok(0)
    25. }
    26. let ignoredonQuestion = onQuestion()
    27. io.writeLine("onQuestion:{later()}")
    28. function onBreak() -> Result<int, string> {
    29. let mut i = 0
    30. while (i < 1) {
    31. using file = (io.open("input.txt")?) {
    32. later = () => match (file.read()) {
    33. case Ok(text) => text
    34. case Err(e) => e
    35. }
    36. break
    37. }
    38. }
    39. Ok(0)
    40. }
    41. let ignoredonBreak = onBreak()
    42. io.writeLine("onBreak:{later()}")
    43. function onContinue() -> Result<int, string> {
    44. let mut i = 0
    45. while (i < 1) {
    46. using file = (io.open("input.txt")?) {
    47. later = () => match (file.read()) {
    48. case Ok(text) => text
    49. case Err(e) => e
    50. }
    51. i += 1
    52. continue
    53. }
    54. }
    55. Ok(0)
    56. }
    57. let ignoredonContinue = onContinue()
    58. io.writeLine("onContinue:{later()}")
    Expected outputexit 0
    onReturn:file is closed
    onQuestion:file is closed
    onBreak:file is closed
    onContinue:file is closed
    
    $ topaz run example.tpz · target product 5.21.3
    input.txtClose on return, propagation, break, and continueOpen in Playground
    1. payload
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. onReturn:file is closed
    2. onQuestion:file is closed
    3. onBreak:file is closed
    4. onContinue:file is closed
    Source evidence

    Explicit and automatic close on the installed native host

    Link
    example.tpzExplicit and automatic close on the installed native hostOpen in Playground
    1. import std.io
    2. let mut later: () -> string = () => "unset"
    3. function scenario() -> Result<int, string> {
    4. using file = (io.open("input.txt")?) {
    5. later = () => match (file.read()) {
    6. case Ok(text) => text
    7. case Err(e) => e
    8. }
    9. file.close()
    10. file.close()
    11. io.writeLine(later())
    12. }
    13. Ok(0)
    14. }
    15. let ignored = scenario()
    16. io.writeLine(later())
    Expected outputexit 0
    file is closed
    file is closed
    
    $ topaz run example.tpz · target product 5.21.3
    input.txtExplicit and automatic close on the installed native hostOpen in Playground
    1. payload
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. file is closed
    2. file is closed

    This example tests repeated close on the installed native host. The File resource contract requires the same close result across hosts.

    Source evidence

    Reject a non-File resource initializer

    Link
    example.tpzReject a non-File resource initializerOpen in Playground
    1. using resource = (1) {}
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz · target product 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5001 structures-using-requires-file at example.tpz:1:19
    Source evidence

    Use a File resource initializer

    Link
    example.tpzUse a File resource initializer
    1. function manage(file: File) -> () {
    2. using resource = (file) {}
    3. }
    Expected outputexit 0
    example.tpz: types-ok (1 module)
    example.tpz: resolve-ok (1 module)
    
    $ topaz check example.tpz · target product 5.21.3
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Expected stdout
    1. example.tpz: types-ok (1 module)
    2. example.tpz: resolve-ok (1 module)
    Source evidence

    Check nested concrete members and recursive Eq or Order

    Link
    example.tpzCheck nested concrete members and recursive Eq or Order
    1. record Leaf derives Eq, Order, JSON { value: int }
    2. record Box derives Eq, Order, JSON { value: Leaf }
    3. record Nested derives Eq, Order, Show, JSON { value: Box }
    4. enum Tree derives Eq, Show { Leaf(int), Branch(Array<Tree>) }
    5. enum Chain derives Eq, Order { End, Next(int, Chain) }
    6. record Callable derives Show { run: () -> int }
    7. record Document derives Eq, JSON { items: Array<Option<Leaf>> }
    Expected outputexit 0
    example.tpz: types-ok (1 module)
    example.tpz: resolve-ok (1 module)
    
    $ topaz check example.tpz · target product 5.21.3
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Expected stdout
    1. example.tpz: types-ok (1 module)
    2. example.tpz: resolve-ok (1 module)
    Source evidence

    Map value equality satisfies Eq derivation

    Link
    example.tpzMap value equality satisfies Eq derivationOpen in Playground
    1. record Index derives Eq { values: Map<string, int> }
    Expected outputexit 0
    No stdout.
    $ topaz check example.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Source evidence

    Use comparable array elements

    Link
    example.tpzUse comparable array elements
    1. record Comparable derives Eq { values: Array<int> }
    Expected outputexit 0
    example.tpz: types-ok (1 module)
    example.tpz: resolve-ok (1 module)
    
    $ topaz check example.tpz · target product 5.21.3
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Expected stdout
    1. example.tpz: types-ok (1 module)
    2. example.tpz: resolve-ok (1 module)
    Source evidence

    Reject a float field when deriving total Order

    Link
    example.tpzReject a float field when deriving total OrderOpen in Playground
    1. record NonTotal derives Order { value: float }
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz · target product 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5530 structures-derive-order-field at example.tpz:1:25
    Source evidence

    Use a totally orderable field

    Link
    example.tpzUse a totally orderable field
    1. record Total derives Order { value: int }
    Expected outputexit 0
    example.tpz: types-ok (1 module)
    example.tpz: resolve-ok (1 module)
    
    $ topaz check example.tpz · target product 5.21.3
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Expected stdout
    1. example.tpz: types-ok (1 module)
    2. example.tpz: resolve-ok (1 module)
    Source evidence

    A recursive declaration has a reusable JSON conformance

    Link
    example.tpzA recursive declaration has a reusable JSON conformanceOpen in Playground
    1. enum Recursive derives JSON { End, Next(Recursive) }
    Expected outputexit 0
    No stdout.
    $ topaz check example.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Source evidence

    Use finite JSON payloads

    Link
    example.tpzUse finite JSON payloads
    1. enum Finite derives JSON { End, Value(int) }
    Expected outputexit 0
    example.tpz: types-ok (1 module)
    example.tpz: resolve-ok (1 module)
    
    $ topaz check example.tpz · target product 5.21.3
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Expected stdout
    1. example.tpz: types-ok (1 module)
    2. example.tpz: resolve-ok (1 module)
    Source evidence

    A nested manual Eq implementation satisfies outer derivation

    Link
    example.tpzA nested manual Eq implementation satisfies outer derivationOpen in Playground
    1. record Callback { run: () -> int }
    2. impl Eq<Callback> {
    3. function equals(a: Callback, b: Callback) -> bool { true }
    4. }
    5. record Outer derives Eq { item: Callback }
    Expected outputexit 0
    No stdout.
    $ topaz check example.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Source evidence

    Show does not require nested comparability

    Link
    example.tpzShow does not require nested comparability
    1. record Callback { run: () -> int }
    2. impl Eq<Callback> {
    3. function equals(a: Callback, b: Callback) -> bool { true }
    4. }
    5. record Outer derives Show { item: Callback }
    Expected outputexit 0
    example.tpz: types-ok (1 module)
    example.tpz: resolve-ok (1 module)
    
    $ topaz check example.tpz · target product 5.21.3
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Expected stdout
    1. example.tpz: types-ok (1 module)
    2. example.tpz: resolve-ok (1 module)
    Source evidence

    Reject duplicate manual and derived Eq

    Link
    example.tpzReject duplicate manual and derived EqOpen in Playground
    1. record Item derives Eq { value: int }
    2. impl Eq<Item> {
    3. function equals(a: Item, b: Item) -> bool { a.value == b.value }
    4. }
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz · target product 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5521 structures-duplicate-conformance at example.tpz:1:21
    Source evidence

    Keep one conformance per protocol

    Link
    example.tpzKeep one conformance per protocol
    1. record Item derives Show { value: int }
    2. impl Eq<Item> {
    3. function equals(a: Item, b: Item) -> bool { a.value == b.value }
    4. }
    Expected outputexit 0
    example.tpz: types-ok (1 module)
    example.tpz: resolve-ok (1 module)
    
    $ topaz check example.tpz · target product 5.21.3
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Expected stdout
    1. example.tpz: types-ok (1 module)
    2. example.tpz: resolve-ok (1 module)
    Source evidence

    Null supports Eq and JSON derivation

    Link
    example.tpzNull supports Eq and JSON derivation
    1. record Nil derives Eq, JSON { value: null }
    Expected outputexit 0
    example.tpz: types-ok (1 module)
    example.tpz: resolve-ok (1 module)
    
    $ topaz check example.tpz · target product 5.21.3
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Expected stdout
    1. example.tpz: types-ok (1 module)
    2. example.tpz: resolve-ok (1 module)
    Source evidence

    An imported nominal alias keeps its derived conformance

    Link
    example.tpzAn imported nominal alias keeps its derived conformanceOpen in Playground
    1. import remote { Item as RemoteItem }
    2. record Item { n: int }
    3. let imported = RemoteItem { n: 1 }
    4. let same: bool = Eq.equals(imported, imported)
    Expected outputexit 0
    example.tpz: types-ok (2 modules)
    example.tpz: resolve-ok (2 modules)
    
    $ topaz check example.tpz · target product 5.21.3
    remote.tpzAn imported nominal alias keeps its derived conformanceOpen in Playground
    1. export record Item derives Eq { n: int }
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Expected stdout
    1. example.tpz: types-ok (2 modules)
    2. example.tpz: resolve-ok (2 modules)
    Source evidence

    A local type does not inherit the imported namesake conformance

    Link
    example.tpzA local type does not inherit the imported namesake conformanceOpen in Playground
    1. import remote { Item as RemoteItem }
    2. record Item { n: int }
    3. let localValue = Item { n: 1 }
    4. let same = Eq.equals(localValue, localValue)
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz · target product 5.21.3
    remote.tpzA local type does not inherit the imported namesake conformanceOpen in Playground
    1. export record Item derives Eq { n: int }
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5522 structures-no-visible-eq-conformance at example.tpz:4:22
    Source evidence

    Give the local type its own conformance

    Link
    example.tpzGive the local type its own conformanceOpen in Playground
    1. import remote { Item as RemoteItem }
    2. record Item derives Eq { n: int }
    3. let localValue = Item { n: 1 }
    4. let same = Eq.equals(localValue, localValue)
    Expected outputexit 0
    example.tpz: types-ok (2 modules)
    example.tpz: resolve-ok (2 modules)
    
    $ topaz check example.tpz · target product 5.21.3
    remote.tpzGive the local type its own conformanceOpen in Playground
    1. export record Item derives Eq { n: int }
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Expected stdout
    1. example.tpz: types-ok (2 modules)
    2. example.tpz: resolve-ok (2 modules)
    Source evidence

    A manual conformance is visible through a nominal import

    Link
    example.tpzA manual conformance is visible through a nominal importOpen in Playground
    1. import remote { Item as RemoteItem }
    2. let imported = RemoteItem { n: 1 }
    3. let same = Eq.equals(imported, imported)
    Expected outputexit 0
    No stdout.
    $ topaz check example.tpz --format json · target product 5.21.3
    remote.tpzA manual conformance is visible through a nominal importOpen in Playground
    1. export record Item { n: int }
    2. impl Eq<Item> {
    3. function equals(a: Item, b: Item) -> bool { a.n == b.n }
    4. }
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Source evidence

    A derived conformance is visible through a nominal import

    Link
    example.tpzA derived conformance is visible through a nominal importOpen in Playground
    1. import remote { Item as RemoteItem }
    2. let imported = RemoteItem { n: 1 }
    3. let same = Eq.equals(imported, imported)
    Expected outputexit 0
    No stdout.
    $ topaz check example.tpz --format json · target product 5.21.3
    remote.tpzA derived conformance is visible through a nominal importOpen in Playground
    1. export record Item derives Eq { n: int }
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Source evidence

    A local value owns the member call despite a protocol namesake

    Link
    example.tpzA local value owns the member call despite a protocol namesake
    1. import std.io
    2. protocol Labelled { function label(value: Self) -> string }
    3. record Item { name: string }
    4. impl Labelled<Item> {
    5. function label(value: Item) -> string { value.name }
    6. }
    7. function localLabel() -> string {
    8. let Labelled = { label: (value: Item) => "local" }
    9. Labelled.label(Item { name: "protocol" })
    10. }
    11. io.writeLine(localLabel())
    Expected outputexit 0
    local
    
    $ topaz run example.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. local
    Source evidence

    Reject a user protocol with a built-in protocol name

    Link
    example.tpzReject a user protocol with a built-in protocol nameOpen in Playground
    1. protocol Eq { function custom(value: Self) -> int }
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz · target product 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5008 structures-builtin-protocol-redeclaration at example.tpz:1:10
    Source evidence

    Choose a distinct user protocol name

    Link
    example.tpzChoose a distinct user protocol name
    1. protocol Custom { function custom(value: Self) -> int }
    Expected outputexit 0
    example.tpz: types-ok (1 module)
    example.tpz: resolve-ok (1 module)
    
    $ topaz check example.tpz · target product 5.21.3
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Expected stdout
    1. example.tpz: types-ok (1 module)
    2. example.tpz: resolve-ok (1 module)
    Source evidence

    Evaluate the return value, then close nested resources

    Link
    example.tpzEvaluate the return value, then close nested resourcesOpen in Playground
    1. import std.io
    2. let mut outerClosed: () -> bool = () => false
    3. let mut innerClosed: () -> bool = () => false
    4. function isClosed(file: File) -> bool {
    5. match (file.read()) {
    6. case Ok(text) => false
    7. case Err(error) => true
    8. }
    9. }
    10. function makeResult() -> Result<int, string> {
    11. io.writeLine("return-value")
    12. Ok(7)
    13. }
    14. function scenario() -> Result<int, string> {
    15. using outer = (io.open("input.txt")?) {
    16. outerClosed = () => isClosed(outer)
    17. defer { io.writeLine("outer-defer:inner-closed={innerClosed()}") }
    18. using inner = (io.open("input.txt")?) {
    19. innerClosed = () => isClosed(inner)
    20. defer { io.writeLine("inner-defer") }
    21. return makeResult()
    22. }
    23. }
    24. Ok(0)
    25. }
    26. let outcome = scenario()
    27. io.writeLine(match (outcome) {
    28. case Ok(value) => "result:{value}"
    29. case Err(error) => "open:{error}"
    30. })
    31. io.writeLine("outer-closed:{outerClosed()}")
    32. io.writeLine("inner-closed:{innerClosed()}")
    Expected outputexit 0
    return-value
    inner-defer
    outer-defer:inner-closed=true
    result:7
    outer-closed:true
    inner-closed:true
    
    $ topaz run example.tpz · target product 5.21.3
    input.txtEvaluate the return value, then close nested resourcesOpen in Playground
    1. payload
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. return-value
    2. inner-defer
    3. outer-defer:inner-closed=true
    4. result:7
    5. outer-closed:true
    6. inner-closed:true

    The native File read result makes closing observable. The outer body defer sees the inner resource closed before the outer resource closes.

    Source evidence

    An ordinary fault skips remaining cleanup

    Link
    example.tpzAn ordinary fault skips remaining cleanupOpen in Playground
    1. import std.io
    2. function divide(divisor: int) -> int {
    3. 42 / divisor
    4. }
    5. function scenario() -> Result<int, string> {
    6. using file = (io.open("input.txt")?) {
    7. defer { io.writeLine("cleanup-observed") }
    8. divide(0)
    9. }
    10. Ok(0)
    11. }
    12. let result = scenario()
    Expected outputexit 1
    No stdout.
    $ topaz run example.tpz · target product 5.21.3
    input.txtAn ordinary fault skips remaining cleanupOpen in Playground
    1. payload
    Expected result

    Expected result

    Rejection during run. Exit code 1.

    • TPZ4002 diagnostic-tpz4002 at example.tpz:3:5

    The division fault stops execution. The remaining defer and using cleanup are skipped under the fault cleanup rule. Standard output contains no cleanup message.

    Source evidence

    An imported protocol call executes its manual method

    Link
    example.tpzAn imported protocol call executes its manual methodOpen in Playground
    1. import std.io { writeLine }
    2. import remote { Item as RemoteItem }
    3. let left = RemoteItem { n: 1 }
    4. let right = RemoteItem { n: 11 }
    5. writeLine("{Eq.equals(left, right)}")
    Expected outputexit 0
    true
    
    $ topaz run example.tpz · target product 5.21.3
    remote.tpzAn imported protocol call executes its manual methodOpen in Playground
    1. export record Item { n: int }
    2. impl Eq<Item> {
    3. function equals(a: Item, b: Item) -> bool { a.n % 10 == b.n % 10 }
    4. }
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. true
    Source evidence

    A protocol method has ordinary generic bounds

    Link
    example.tpzA protocol method has ordinary generic boundsOpen in Playground
    1. protocol Echo {
    2. function echo<T: Show>(value: Self, item: T) -> T
    3. }
    Expected outputexit 0
    No stdout.
    $ topaz check example.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Source evidence

    Fixed defaults precede the variadic protocol tail

    Link
    example.tpzFixed defaults precede the variadic protocol tailOpen in Playground
    1. protocol Sum {
    2. function sum(value: Self, start: int = 0, ...items: int) -> int
    3. }
    Expected outputexit 0
    No stdout.
    $ topaz check example.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Source evidence

    Every caller uses the protocol-owned default

    Link
    example.tpzEvery caller uses the protocol-owned default
    1. import std.io { writeLine }
    2. const ending = "!"
    3. protocol Labelled {
    4. function label(value: Self, suffix: string = ending) -> string
    5. }
    6. record Item { name: string }
    7. impl Labelled<Item> {
    8. function label(value: Item, suffix: string) -> string { value.name + suffix }
    9. }
    10. let item = Item { name: "Moss" }
    11. writeLine(Labelled.label(item))
    12. writeLine(Labelled.label(item, "?"))
    Expected outputexit 0
    Moss!
    Moss?
    
    $ topaz run example.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. Moss!
    2. Moss?
    Source evidence

    A protocol constructs a value without a receiver argument

    Link
    example.tpzA protocol constructs a value without a receiver argument
    1. import std.io { writeLine }
    2. protocol Factory {
    3. function make(value: int) -> Self
    4. function zero() -> Self
    5. }
    6. record Item { value: int }
    7. impl Factory<Item> {
    8. function make(value: int) -> Item { Item { value: value } }
    9. function zero() -> Item { Item { value: 0 } }
    10. }
    11. let explicit = Factory.make<Item>(9)
    12. let inferred: Item = Factory.zero()
    13. writeLine("{explicit.value}")
    14. writeLine("{inferred.value}")
    Expected outputexit 0
    9
    0
    
    $ topaz run example.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. 9
    2. 0
    Source evidence

    Protocol calls reject named arguments during parsing

    Link
    example.tpzProtocol calls reject named arguments during parsingOpen in Playground
    1. protocol Labelled {
    2. function label(value: Self) -> string
    3. }
    4. record Item { name: string }
    5. impl Labelled<Item> {
    6. function label(value: Item) -> string { value.name }
    7. }
    8. let item = Item { name: "Moss" }
    9. Labelled.label(value: item)
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during parse. Nonzero exit.

    • TPZ2001 function-named-call-syntax at example.tpz:9:21
    Source evidence

    A required protocol parameter cannot follow a default

    Link
    example.tpzA required protocol parameter cannot follow a defaultOpen in Playground
    1. protocol Sum {
    2. function sum(value: Self, first: int = 1, second: int) -> int
    3. }
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during parse. Nonzero exit.

    • TPZ2001 function-required-after-default at example.tpz:2:58
    Source evidence

    A bodyless protocol signature still declares its result

    Link
    example.tpzA bodyless protocol signature still declares its resultOpen in Playground
    1. protocol Labelled {
    2. function label(value: Self)
    3. }
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5022 protocol-signature-return-required at example.tpz:2:14
    Source evidence

    Implementation binders and renamed method binders remain distinct

    Link
    example.tpzImplementation binders and renamed method binders remain distinct
    1. import std.io { writeLine }
    2. protocol Echo {
    3. function echo<U: Show>(value: Self, item: U) -> U
    4. }
    5. record Box<T> { value: T }
    6. impl<T> Echo<Box<T>> {
    7. function echo<V: Show>(value: Box<T>, item: V) { item }
    8. }
    9. let box: Box<int> = Box { value: 3 }
    10. writeLine(Echo.echo<Box<int>, string>(box, "ok"))
    11. let inferred: int = Echo.echo(box, 9)
    12. writeLine("{inferred}")
    Expected outputexit 0
    ok
    9
    
    $ topaz run example.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. ok
    2. 9
    Source evidence

    Protocol defaults and a variadic implementation share one call plan

    Link
    example.tpzProtocol defaults and a variadic implementation share one call plan
    1. import std.io { writeLine }
    2. protocol Sum {
    3. function sum(value: Self, start: int = 10, ...items: int) -> int
    4. }
    5. record Item { offset: int }
    6. impl Sum<Item> {
    7. function sum(value: Item, start: int, ...items: int) {
    8. let mut total = value.offset + start
    9. for item in (items) { total = total + item }
    10. total
    11. }
    12. }
    13. let item = Item { offset: 1 }
    14. writeLine("{Sum.sum(item)}")
    15. writeLine("{Sum.sum(item, 2, ...[3, 4], 5)}")
    16. writeLine("{Sum.sum(item, ...[2, 3])}")
    Expected outputexit 0
    11
    15
    16
    
    $ topaz run example.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. 11
    2. 15
    3. 16
    Source evidence

    An omitted implementation result uses the protocol result context

    Link
    example.tpzAn omitted implementation result uses the protocol result contextOpen in Playground
    1. protocol Choose {
    2. function choose(value: Self) -> int | string
    3. }
    4. record Item {}
    5. impl Choose<Item> {
    6. function choose(value: Item) { 7 }
    7. }
    8. let chosen: int | string = Choose.choose(Item {})
    Expected outputexit 0
    No stdout.
    $ topaz check example.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Source evidence

    A generic receiver keeps its outer binder and local method binders

    Link
    example.tpzA generic receiver keeps its outer binder and local method binders
    1. import std.io { writeLine }
    2. record Box<T> { value: T }
    3. impl<T> Box<T> {
    4. function readValue(self) -> T { self.value }
    5. function echo<U>(self, item: U) -> U { item }
    6. }
    7. let box: Box<int> = Box { value: 3 }
    8. writeLine("{box.readValue()}")
    9. writeLine(box.echo<string>("ok"))
    Expected outputexit 0
    3
    ok
    
    $ topaz run example.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. 3
    2. ok
    Source evidence

    A protocol owner can give int its declared operation

    Link
    example.tpzA protocol owner can give int its declared operation
    1. import std.io { writeLine }
    2. protocol Labelled {
    3. function label(value: Self) -> string
    4. }
    5. impl Labelled<int> {
    6. function label(value: int) -> string { "number" }
    7. }
    8. writeLine(Labelled.label(7))
    Expected outputexit 0
    number
    
    $ topaz run example.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. number
    Source evidence

    Different concrete type arguments select distinct conformances

    Link
    example.tpzDifferent concrete type arguments select distinct conformances
    1. import std.io { writeLine }
    2. protocol Mark {
    3. function mark(value: Self) -> string
    4. }
    5. record Box<T> { value: T }
    6. impl Mark<Box<int>> {
    7. function mark(value: Box<int>) -> string { "integer" }
    8. }
    9. impl Mark<Box<string>> {
    10. function mark(value: Box<string>) -> string { "text" }
    11. }
    12. let number: Box<int> = Box { value: 3 }
    13. let text: Box<string> = Box { value: "three" }
    14. writeLine(Mark.mark(number))
    15. writeLine(Mark.mark(text))
    Expected outputexit 0
    integer
    text
    
    $ topaz run example.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. integer
    2. text
    Source evidence

    Conditional derive binders correspond by position without capturing fields

    Link
    example.tpzConditional derive binders correspond by position without capturing fieldsOpen in Playground
    1. record Box<T> derives<U: Eq> Eq<Box<U>> { value: T }
    2. let first: Box<int> = Box { value: 3 }
    3. let second: Box<int> = Box { value: 3 }
    4. let same: bool = Eq.equals(first, second)
    Expected outputexit 0
    No stdout.
    $ topaz check example.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Source evidence

    Conformance visibility makes a separate method export invalid

    Link
    example.tpzConformance visibility makes a separate method export invalidOpen in Playground
    1. protocol Labelled {
    2. function label(value: Self) -> string
    3. }
    4. record Item {}
    5. impl Labelled<Item> {
    6. export function label(value: Item) -> string { "item" }
    7. }
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5022 protocol-method-export at example.tpz:6:5
    Source evidence

    An implementation cannot introduce its own omitted argument

    Link
    example.tpzAn implementation cannot introduce its own omitted argumentOpen in Playground
    1. protocol Labelled {
    2. function label(value: Self, suffix: string) -> string
    3. }
    4. record Item {}
    5. impl Labelled<Item> {
    6. function label(value: Item, suffix: string = "!") -> string { suffix }
    7. }
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5022 protocol-method-default at example.tpz:6:33
    Source evidence

    Implementation method bounds must match the declared operation

    Link
    example.tpzImplementation method bounds must match the declared operationOpen in Playground
    1. protocol Echo {
    2. function echo<U: Show>(value: Self, item: U) -> U
    3. }
    4. record Item {}
    5. impl Echo<Item> {
    6. function echo<V: Eq>(value: Item, item: V) -> V { item }
    7. }
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5022 protocol-method-signature-mismatch at example.tpz:6:14
    Source evidence

    An array parameter does not replace a variadic tail

    Link
    example.tpzAn array parameter does not replace a variadic tailOpen in Playground
    1. protocol Count {
    2. function count(value: Self, ...items: int) -> int
    3. }
    4. record Item {}
    5. impl Count<Item> {
    6. function count(value: Item, items: Array<int>) -> int { items.length }
    7. }
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5022 protocol-method-signature-mismatch at example.tpz:6:14
    Source evidence

    An inner binder cannot be mistaken for the receiver binder

    Link
    example.tpzAn inner binder cannot be mistaken for the receiver binderOpen in Playground
    1. protocol Echo {
    2. function echo<U>(value: Self, item: U) -> U
    3. }
    4. record Box<T> { value: T }
    5. impl<T> Echo<Box<T>> {
    6. function echo<T>(value: Box<T>, item: T) -> T { item }
    7. }
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5022 protocol-method-signature-mismatch at example.tpz:6:14
    Source evidence

    A concrete conformance cannot specialize an overlapping generic head

    Link
    example.tpzA concrete conformance cannot specialize an overlapping generic headOpen in Playground
    1. record Box<T> { value: T }
    2. impl<T: Eq> Eq<Box<T>> {
    3. function equals(a: Box<T>, b: Box<T>) -> bool { Eq.equals(a.value, b.value) }
    4. }
    5. impl Eq<Box<int>> {
    6. function equals(a: Box<int>, b: Box<int>) -> bool { a.value == b.value }
    7. }
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5521 duplicate-protocol-conformance at example.tpz:5:9
    Source evidence

    Different positive bounds do not prove two heads disjoint

    Link
    example.tpzDifferent positive bounds do not prove two heads disjointOpen in Playground
    1. protocol Mark {
    2. function mark(value: Self) -> int
    3. }
    4. record Box<T> { value: T }
    5. impl<T: Eq> Mark<Box<T>> {
    6. function mark(value: Box<T>) -> int { 1 }
    7. }
    8. impl<U: Show> Mark<Box<U>> {
    9. function mark(value: Box<U>) -> int { 2 }
    10. }
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5521 duplicate-protocol-conformance at example.tpz:8:20
    Source evidence

    A conditional derive and a manual specialization share the overlap check

    Link
    example.tpzA conditional derive and a manual specialization share the overlap checkOpen in Playground
    1. record Box<T> derives<T: Eq> Eq<Box<T>> { value: T }
    2. impl Eq<Box<int>> {
    3. function equals(a: Box<int>, b: Box<int>) -> bool { a.value == b.value }
    4. }
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5521 duplicate-protocol-conformance at example.tpz:1:30
    Source evidence

    A transparent alias cannot create a second conformance

    Link
    example.tpzA transparent alias cannot create a second conformanceOpen in Playground
    1. record Item {}
    2. type Alias = Item
    3. impl Eq<Item> {
    4. function equals(a: Item, b: Item) -> bool { true }
    5. }
    6. impl Eq<Alias> {
    7. function equals(a: Alias, b: Alias) -> bool { true }
    8. }
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5521 duplicate-protocol-conformance at example.tpz:6:9
    Source evidence

    The dependency graph rejects protocol-owner and type-owner overlap

    Link
    example.tpzThe dependency graph rejects protocol-owner and type-owner overlapOpen in Playground
    1. import rules
    2. import kinds
    3. let value = 0
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --format json · target product 5.21.3
    rules.tpzThe dependency graph rejects protocol-owner and type-owner overlapOpen in Playground
    1. export protocol Mark { function mark(value: Self) -> int }
    2. impl<T> Mark<T> { function mark(value: T) -> int { 1 } }
    kinds.tpzThe dependency graph rejects protocol-owner and type-owner overlapOpen in Playground
    1. import rules { Mark }
    2. export record Item {}
    3. impl Mark<Item> { function mark(value: Item) -> int { 2 } }
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5521 duplicate-protocol-conformance at kinds.tpz:3:11
    Source evidence

    Changing import order cannot choose a conformance winner

    Link
    example.tpzChanging import order cannot choose a conformance winnerOpen in Playground
    1. import kinds
    2. import rules
    3. let value = 0
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --format json · target product 5.21.3
    rules.tpzChanging import order cannot choose a conformance winnerOpen in Playground
    1. export protocol Mark { function mark(value: Self) -> int }
    2. impl<T> Mark<T> { function mark(value: T) -> int { 1 } }
    kinds.tpzChanging import order cannot choose a conformance winnerOpen in Playground
    1. import rules { Mark }
    2. export record Item {}
    3. impl Mark<Item> { function mark(value: Item) -> int { 2 } }
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5521 duplicate-protocol-conformance at kinds.tpz:3:11
    Source evidence

    An implementation cannot assume its own conditional bound

    Link
    example.tpzAn implementation cannot assume its own conditional boundOpen in Playground
    1. protocol Required { function required(value: Self) -> bool }
    2. protocol Mark { function mark(value: Self) -> int }
    3. record Box<T> { value: T }
    4. impl<T: Required> Mark<Box<T>> {
    5. function mark(value: Box<T>) -> int { 1 }
    6. }
    7. let box: Box<int> = Box { value: 3 }
    8. Mark.mark(box)
    Expected outputNonzero exit
    No stdout.
    $ topaz check example.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5522 protocol-call-argument-does-not-conform at example.tpz:8:11
    Source evidence

    Pending contracts

    Link

    Related definitions and diagnostics

    Link

    See Functions and generics for function and bound syntax, and Records, enums and newtypes for nominal construction and declaration syntax.

    Protocol implementation violates the orphan rule

    Duplicate protocol implementation

    Missing protocol conformance

    Protocol cannot be derived for this type

    TPZ5022

    Redeclaration

    Wrong argument count

    TPZ5001

    Division or remainder by zero

    Unexpected token

    Variadic parameter is not final

    Wrong number of explicit type arguments

    TPZ5523

    Module namespace collision

    Name is not exported

    TPZ5524

    Private type in exported surface

    Unknown member or field

    Value is not comparable or keyable

    Runtime comparison guard

    Menu

    Download