Tya v0.12 Specification

This document is the specification for Tya v0.12 after v0.11 explicit interfaces and implements.

Theme

Tya v0.12 is about interface inheritance and precise conflict diagnostics.

v0.11 adds explicit interfaces as standalone contracts. v0.12 lets interfaces extend other interfaces so contracts can be composed without repeating method requirements. Because Tya does not support method overloading, v0.12 also defines how conflicting interface requirements are rejected and reported.

Goals

Included in v0.12

v0.12 includes all v0.11 interface behavior and adds:

Not Included in v0.12

v0.12 does not include:

Interface Inheritance

An interface may extend another interface.

interface Reader
  read = ->

interface SeekableReader extends Reader
  seek = offset ->

SeekableReader requires both read and seek.

A class that implements SeekableReader must implement inherited requirements from Reader as well as requirements declared directly in SeekableReader.

class File implements SeekableReader
  read = ->
    "data"

  seek = offset ->
    nil

This is valid because File implements both required methods.

class BrokenFile implements SeekableReader
  seek = offset ->
    nil

BrokenFile is invalid because the inherited read requirement is missing.

Multiple Interface Inheritance

An interface may extend multiple interfaces.

interface Reader
  read = ->

interface Writer
  write = value ->

interface ReadWriter extends Reader, Writer

ReadWriter requires both read and write.

An interface may add its own requirements while extending other interfaces.

interface Closable
  close = ->

interface FileHandle extends Reader, Writer, Closable
  path = ->

FileHandle requires read, write, close, and path.

Transitive Inheritance

Interface inheritance is transitive.

interface Named
  name = ->

interface Displayable extends Named
  display = ->

interface MenuItem extends Displayable
  select = ->

MenuItem requires name, display, and select.

Compatible Duplicate Requirements

If inherited interfaces require the same method with the same arity, the requirements are compatible.

interface Named
  name = ->

interface Labeled
  name = ->

interface MenuItem extends Named, Labeled

MenuItem has one name requirement.

class Item implements MenuItem
  name = ->
    "File"

One implementation satisfies both inherited requirements.

Conflicting Requirements

If inherited interfaces require the same method name with different arity, the interface declaration is an error.

interface LookupById
  find = id ->

interface LookupByName
  find = first, last ->

interface Searchable extends LookupById, LookupByName

Searchable is invalid because Tya does not support method overloading. A single find method cannot satisfy both arities.

Conflict diagnostics should identify the interface being declared, the method name, both source interfaces, and both arities.

For example:

interface Searchable has conflicting method requirement find:
LookupById.find expects 1 argument, LookupByName.find expects 2 arguments

The exact wording may differ, but the diagnostic must include:

Direct and Inherited Conflicts

A method declared directly in a child interface must also be compatible with inherited requirements.

interface Lookup
  find = id ->

interface BadLookup extends Lookup
  find = first, last ->

BadLookup is invalid because its direct find requirement conflicts with the inherited Lookup.find requirement.

If the direct requirement uses the same arity, it is valid and represents the same requirement.

interface Lookup
  find = id ->

interface CachedLookup extends Lookup
  find = key ->

CachedLookup is valid because both find requirements have arity 1. Parameter names are local to each declaration and are not part of compatibility.

Inheritance Cycles

Interface inheritance cannot form cycles.

interface A extends B

interface B extends A

This is an error.

Longer cycles are also errors.

interface A extends B
interface B extends C
interface C extends A

Cycle diagnostics should mention the interfaces involved in the cycle when available.

Interface and Class Boundaries

Interfaces may extend only interfaces.

class Base
  name = ->
    "base"

interface Named extends Base

Named is invalid because Base is a class.

Classes may extend only classes.

interface Named
  name = ->

class User extends Named

User is invalid because Named is an interface.

Classes continue to use implements for interfaces.

class User implements Named
  name = ->
    "user"

Abstract Classes and Inherited Interfaces

An abstract class may implement an interface that inherits requirements without implementing every requirement.

interface Reader
  read = ->

interface SeekableReader extends Reader
  seek = offset ->

abstract class AbstractFile implements SeekableReader
  seek = offset ->
    nil

AbstractFile is valid because it is abstract. A concrete subclass must still implement read.

class File extends AbstractFile
  read = ->
    "data"

Modules

Interface inheritance works with module-qualified interface names.

module io
  interface Reader
    read = ->

interface FileReader extends io.Reader
  path = ->

A class implementing FileReader must satisfy both io.Reader.read and FileReader.path.

class MemoryFile implements FileReader
  read = ->
    "data"

  path = ->
    "memory"

Introspection

v0.12 keeps the v0.8 introspection surface:

v0.12 does not add interface introspection.

Diagnostics

v0.12 implementations should report source-oriented errors for:

Conflict diagnostics must include the relevant interface names, method name, and arity values when available.