An interface makes a small promise

A function would like something that can glow. It has not asked for the moon, although the moon is welcome to apply.

Ask for the capability

An interface describes the methods a value promises to provide:

interface Glowing {
  glow() -> String
}

class Firefly with Glowing {
  new() {}

  glow() -> String {
    "blink"
  }
}

shine(light: Glowing) -> String {
  light.glow()
}

main() {
  assert shine(Firefly()) == "blink"
}

shine depends on the interface. It does not need to inspect the firefly’s representation or know how its answer is produced.

A familiar shape is not a declaration

Pooka requires the class to say with Glowing. A coincidentally named glow method is not the same agreement.

interface contract: glow() -> String
class declaration:  with Glowing
implementation:     glow returns String

All three pieces matter. The checker must verify the method’s signature, including any generic types involved; matching the method’s name alone would be a rather loose interpretation of a promise.

An actor can implement an interface too. The concrete implementation may have a process boundary, while the calling code still names the capability it needs. That does not make the timing or failure behaviour of a request disappear.

Leave the family tree out of it

An interface and a base class solve different problems. A closed class family gives pattern matching a known set of related variants. An interface lets independently declared implementations offer the same operations.

It would be a mistake to infer that every possible implementor is known merely because the interface declaration is visible in one module.

This is why I prefer the small question at the call site: what work does this function need the value to do?

Sometimes the answer is one method. A whole inheritance tree would be rather large luggage for a firefly.

— Peregrine Finch

· pooka, interfaces, types