Skip to content

The book · 11

Interfaces

book.md · 62 lines · 2 min read

Goal: understand structural satisfaction (no implements), vtable dispatch, and the small interfaces that stitch the stdlib together.

An interface is a decl with signatures (chapter 03). Implementing the methods already satisfies it: there is no implements declaration:

decl Writeable {
fn write(data: []byte) -> error
}
decl Log { ... }
fn (l: *Log) write(data: []byte) -> error { ... } // Log now satisfies Writeable, without saying anything

An interface value carries a pair (a pointer to the data, a pointer to the concrete type’s vtable). The vtable is built by the compiler per type, not per instance: the struct never carries function pointers inline. It is the cost of the pair at the point of use, zero overhead per object:

fn log_to(w: Writeable, msg: string) -> error { // 'w' is an interface value (vtable)
return w.write(msg.bytes)
}

When the result depends on the concrete type (map[A,B](xs: C[A]) -> C[B]), then it is a monomorphized generic, not a vtable (chapter 10). The rule: an action that does not return the receiver’s type can use a vtable; a transformation that returns it needs monomorphization.

Makoto adopts “small composable interfaces”: few, of one or two methods, that everything reuses:

  • Readable (io): fn read(buf: mut []byte) -> Result[usize, error], Ok(0) = EOF.
  • Writeable (io): fn write(data: []byte) -> error.
  • Display (fmt): fn to_string() -> string; it is what the {{ }} interpolation uses.
  • Iterator[T] (io): fn next() -> Optional[T]; it is what loop x in it consumes.
  • Serializable (encoding): becomes bytes and back; auto-derived by reflect (chapter 09/10).

Because they are small, they compose: any Readable can be buffered, copied (io.copy), scanned line by line; any Display can be interpolated; any Iterator can be map/filter/ collect. You implement one method and gain the ecosystem.

sc := io.scanner(io.stdin())
loop linha in sc.lines() { process(linha) } // stdin is Readable → scanner → Iterator[string]

Hash/Ord/Clone/Drop/From do not exist as interfaces. Hashability and order are derived (by reflect) or functions (fn(T,T) -> Order); copy is @copy-free default and @promote; cleanup is defer. The comparison/copy axis does not become a family of traits.

Next: 12 · Modules and ecosystem