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