Tya v0.13 Specification

This document is the specification for Tya v0.13 after v0.12 interface inheritance and conflict diagnostics.

Theme

Tya v0.13 is about safer method overrides and constructor chains.

Earlier class versions add inheritance, abstract methods, interfaces, and interface inheritance. v0.13 makes class inheritance safer by letting a method definition explicitly say that it is intended to override inherited behavior, and by checking that subclass constructors initialize their parent class properly.

Goals

Included in v0.13

v0.13 includes all v0.12 class and interface behavior and adds:

Not Included in v0.13

v0.13 does not include:

Instance Method Override

An instance method may use override when it intentionally overrides an inherited instance method.

class User
  label = ->
    "user"

class Admin extends User
  override label = ->
    "admin"

Admin.label is valid because User defines an inherited instance method named label.

The overriding method must use the same arity as the overridden method.

class User
  label = prefix ->
    prefix + " user"

class Admin extends User
  override label = ->
    "admin"

Admin.label is invalid because the parent method expects 1 argument.

Missing Override Target

An override declaration is an error when no inherited method with that name exists.

class User
  label = ->
    "user"

class Admin extends User
  override lable = ->
    "admin"

Admin.lable is invalid because there is no inherited lable method. This helps catch method-name typos.

override checks only parent classes. A method that only satisfies an interface requirement is not an override.

interface Named
  name = ->

class User implements Named
  override name = ->
    "user"

User.name is invalid because Named.name is an interface requirement, not an inherited class method.

Optional Override Annotations

v0.13 does not require override for every overriding method.

class User
  label = ->
    "user"

class Admin extends User
  label = ->
    "admin"

This remains valid. override is an opt-in safety annotation in v0.13.

Abstract Method Override

override may implement an inherited abstract instance method.

abstract class Repository
  abstract find = id ->

class UserRepository extends Repository
  override find = id ->
    "user"

The same arity rule applies. The implementation must match the abstract method arity.

Class Method Override

Class methods may also use override.

class Model
  @@table_name = ->
    "models"

class User extends Model
  override @@table_name = ->
    "users"

override @@table_name is valid because Model defines an inherited class method named @@table_name.

override @@method must override an inherited class method, not an instance method.

class Model
  table_name = ->
    "models"

class User extends Model
  override @@table_name = ->
    "users"

This is invalid because the parent method is an instance method.

override method must override an inherited instance method, not a class method.

Constructor Chaining

If a subclass defines init and its parent class has a public init, the subclass init must call super(...).

class User
  init = name ->
    @name = name

class Admin extends User
  init = name, role ->
    super(name)
    @role = role

This is valid because Admin.init calls User.init.

If the call is missing, the subclass is invalid.

class Admin extends User
  init = name, role ->
    @name = name
    @role = role

Admin.init is invalid because it does not call the parent init.

If the parent class has no init, a subclass init does not need super(...).

class User
  label = ->
    "user"

class Admin extends User
  init = role ->
    @role = role

This is valid.

Constructor super(...) Count

A subclass init may call parent init at most once.

class Admin extends User
  init = name, role ->
    super(name)
    super(name)
    @role = role

This is invalid because super(...) is called twice.

If the parent class has no public init, calling super(...) from init is an error.

class User
  label = ->
    "user"

class Admin extends User
  init = role ->
    super()
    @role = role

This is invalid because User has no init.

Constructor super(...) Placement

Inside a subclass init, super(...) must run before the subclass assigns instance fields.

class Admin extends User
  init = name, role ->
    @role = role
    super(name)

This is invalid because @role is assigned before the parent init runs.

An explicit return before constructor super(...) is also invalid.

class Admin extends User
  init = name, role ->
    return nil
    super(name)

This is invalid because the parent init would never run.

Local variables may be prepared before super(...) if they do not access or assign instance fields.

class Admin extends User
  init = name, role ->
    normalized = string.strip(name)
    super(normalized)
    @role = role

This is valid.

Constructor super(...) Arity

The constructor super(...) call must pass the same number of arguments as the parent init expects.

class User
  init = first, last ->
    @name = first + " " + last

class Admin extends User
  init = name, role ->
    super(name)
    @role = role

This is invalid because User.init expects 2 arguments.

Private Constructors

Private constructors keep the v0.9 rule: a subclass cannot call parent _init with super(...).

class Token
  _init = value ->
    @value = value

class ApiToken extends Token
  init = value ->
    super(value)

ApiToken.init is invalid because parent _init is private.

If a parent class has _init but no public init, the subclass is not required to call super(...), and super(...) remains invalid.

super(...) Outside Constructors

v0.13 keeps the existing method super(...) behavior for normal methods. The constructor chaining rules in this document apply specifically to super(...) inside init.

class User
  label = ->
    "user"

class Admin extends User
  override label = ->
    super() + " admin"

This remains valid when the normal method super() call matches the overridden method.

Modules

override and constructor chaining checks work inside modules.

module accounts
  class User
    init = name ->
      @name = name

    label = ->
      @name

  class Admin extends User
    init = name, role ->
      super(name)
      @role = role

    override label = ->
      @name + " admin"

Introspection

v0.13 keeps the v0.8 introspection surface:

v0.13 does not add introspection for override annotations or constructor chain state.

Diagnostics

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

Diagnostics should mention the relevant class, parent class, method, and constructor names when available.