# Chapter 10: Interfaces

An interface is a contract: a list of methods that any type claiming to implement it must provide. Code written against the interface works with every implementation, so you can render a list of different shapes, validate a form field with a list of different rules, or swap an in-memory store for a real one in tests.

Loup interfaces are **nominal**: a class or struct implements an interface only when it says `implements Name`. Having methods with the right names is not enough. That makes them closer to Java and C# interfaces than to TypeScript's structural types or Go's implicit interfaces. Rust programmers can read `interface` as "trait" and `implements` as an `impl … for` that lives on the type's declaration. The normative text is in the Language Reference §1-11.

## Declaring and implementing an interface

```loup
@mode(web.compiled)

interface Shape {
    func area(): float;
    func name(): string;
}

class Circle implements Shape {
    pub let radius: float;
    new(radius: float) { this.radius = radius; }

    pub func area(): float { return 3.14159 * this.radius * this.radius; }
    pub func name(): string { return "circle"; }
}

class Rect implements Shape {
    pub let w: float;
    pub let h: float;
    new(w: float, h: float) { this.w = w; this.h = h; }

    pub func area(): float { return this.w * this.h; }
    pub func name(): string { return "rect"; }
}

func totalArea(shapes: list[Shape]): float {
    var total = 0.0;
    for (s in shapes) {
        total = total + s.area();
    }
    return total;
}

func main(): void {
    let shapes: list[Shape] = [Circle(1.0), Rect(2.0, 3.0)];
    for (s in shapes) {
        println(i"{$ s.name() $}: {$ s.area() $}");
    }
    println(totalArea(shapes));
}
```

*Listing 10-1: An interface, two implementations, and a function that works with both*

```text
circle: 3.14159
rect: 6
9.14159
```

The interface body lists method signatures ending in `;`. Interface methods are always public, so `pub` is optional there; the implementing class still writes `pub func`. A class extends at most one parent class but can implement any number of interfaces: `class Badge : Widget implements Shape, Named`.

`list[Shape]` holds objects of different classes. Each call to `s.area()` dispatches to the method of the object's own class.

### Forgetting a method

Every method without a body in the interface must be implemented. Leave one out and the class is rejected where it is declared:

```loup,does_not_compile
@mode(web.compiled)

interface Shape {
    func area(): float;
    func name(): string;
}

class Square implements Shape {
    pub let side: float;
    new(side: float) { this.side = side; }

    pub func area(): float { return this.side * this.side; }
}

func main(): void {
    let s: Shape = Square(2.0);
    println(s.area());
}
```

```text
main.lpweb:8:1: error[E0001]: class 'Square' does not implement all methods of interface 'Shape'; missing: name(): string
```

The message lists the missing signature; add `pub func name(): string { return "square"; }`.

### Getting a signature slightly wrong

The implementation must match the interface's parameter and return types exactly. Returning `int` where the interface says `float` is a different method, not an implementation of this one:

```loup,does_not_compile
@mode(web.compiled)

interface Shape {
    func area(): float;
}

class Square implements Shape {
    pub let side: int;
    new(side: int) { this.side = side; }

    pub func area(): int { return this.side * this.side; }
}

func main(): void {
    let s: Shape = Square(3);
    println(s.area());
}
```

```text
main.lpweb:7:1: error[E0001]: method 'area' in class 'Square' has return type 'int', incompatible with interface 'Shape' return type 'float'
```

Loup never converts between `int` and `float` for you (Chapter 3). Either store `side` as a `float`, or convert in the method: `return (this.side * this.side) as float;`.

### Matching methods are not enough

If you come from TypeScript or Go, you might expect a class with an `area(): float` method to count as a `Shape` automatically. It does not:

```loup,does_not_compile
@mode(web.compiled)

interface Shape {
    func area(): float;
}

class Square {
    pub let side: float;
    new(side: float) { this.side = side; }

    pub func area(): float { return this.side * this.side; }
}

func printArea(s: Shape): void {
    println(s.area());
}

func main(): void {
    printArea(Square(3.0));
}
```

```text
main.lpweb:19:15: error[E0001]: argument 1 of 'printArea': expected 'Shape', found 'Square'
```

Declare the relationship: `class Square implements Shape { … }`. Nominal typing means that a type implements a contract only when its author says so, and the compiler then checks the whole contract at that one place.

## Generic interfaces, and structs that implement interfaces

An interface can take type parameters, and a struct can implement an interface just as a class can. Here a form validator is any type that can check a value of some type `T` and maybe return an error message:

```loup
@mode(web.compiled)

interface Validator[T] {
    func check(value: T): Option[string];
}

class NotBlank implements Validator[string] {
    pub func check(value: string): Option[string] {
        if (value.trim() == "") {
            return Some("must not be blank");
        }
        return None;
    }
}

class MinLength implements Validator[string] {
    pub let min: int;
    new(min: int) { this.min = min; }

    pub func check(value: string): Option[string] {
        if (value.len() < this.min) {
            return Some(i"must be at least {$ this.min $} characters");
        }
        return None;
    }
}

struct MaxTemperature implements Validator[float] {
    limit: float;

    func check(value: float): Option[string] {
        if (value > this.limit) {
            return Some(i"must be at most {$ this.limit $}");
        }
        return None;
    }
}

func errors(value: string, rules: list[Validator[string]]): list[string] {
    var found: list[string] = [];
    for (rule in rules) {
        match rule.check(value) {
            Some(msg) => found.push(msg),
            None => {},
        }
    }
    return found;
}

func main(): void {
    let password: list[Validator[string]] = [NotBlank(), MinLength(8)];
    println(errors("   ", password));
    println(errors("hunter2", password));
    println(errors("correct horse", password));

    let oven = MaxTemperature(limit = 250.0);
    println(oven.check(275.0).unwrap_or("ok"));
}
```

*Listing 10-2: A generic `Validator[T]` interface with class and struct implementations*

```text
["must not be blank", "must be at least 8 characters"]
["must be at least 8 characters"]
[]
must be at most 250
```

`Validator[string]` and `Validator[float]` are different interfaces: a `list[Validator[string]]` cannot hold a `MaxTemperature`. Chapter 11 covers type parameters in general.

## Default methods and interface inheritance

An interface method can have a body. Implementers inherit it and may replace it. An interface can also extend other interfaces with `:`, and an implementer must then provide the methods of all of them (Language Reference §1-11.1):

```loup
@mode(web.compiled)

interface Named {
    func name(): string;
}

interface Priced : Named {
    func cents(): int;

    func label(): string {                       # default method
        return i"{$ this.name() $} ({$ this.cents() $} cents)";
    }
}

class Item implements Priced {
    pub let title: string;
    pub let price: int;
    new(title: string, price: int) { this.title = title; this.price = price; }

    pub func name(): string { return this.title; }
    pub func cents(): int { return this.price; }
}

class GiftCard implements Priced {
    pub let amount: int;
    new(amount: int) { this.amount = amount; }

    pub func name(): string { return "gift card"; }
    pub func cents(): int { return this.amount; }
    pub func label(): string {                   # replaces the default
        return i"{$ this.amount // 100 $} dollar gift card";
    }
}

func main(): void {
    let cart: list[Priced] = [Item("notebook", 450), GiftCard(2500)];
    for (p in cart) {
        println(p.label());
    }
}
```

*Listing 10-3: A default method, an interface that extends another, and an override of the default*

```text
notebook (450 cents)
25 dollar gift card
```

Default methods let an interface grow without breaking existing implementers: add a method with a sensible body and every implementation still compiles.

> **Spec and compiler disagree.** The Language Reference (§1-8.3.1) says a method that replaces an interface's *default* method must be marked `override`. Stage0 1.25.1 rejects `override` there with `E_OVERRIDE_NO_TARGET` ("class `GiftCard` has no supertype"), so Listing 10-3 leaves it off. `override` is still required, and accepted, when a class replaces a method inherited from a parent *class* (Chapter 9).

## Checking which interface a value implements

`x is I` tests whether `x`'s runtime type implements interface `I`. As with classes (Chapter 9), Loup 1.25 does not narrow the variable's type after the test, so use `as?` to get a value of the narrower type:

```loup
@mode(web.compiled)

interface Named {
    func name(): string;
}

interface Priced : Named {
    func cents(): int;
}

class Item implements Priced {
    pub let title: string;
    pub let price: int;
    new(title: string, price: int) { this.title = title; this.price = price; }
    pub func name(): string { return this.title; }
    pub func cents(): int { return this.price; }
}

class Customer implements Named {
    pub let email: string;
    new(email: string) { this.email = email; }
    pub func name(): string { return this.email; }
}

func describe(n: Named): string {
    match n as? Priced {
        Some(p) => { return i"{$ p.name() $} costs {$ p.cents() $} cents"; },
        None => { return i"{$ n.name() $} has no price"; },
    }
}

func main(): void {
    let pen = Item("pen", 250);
    println(describe(pen));
    println(describe(Customer("ann@example.com")));
    println(pen is Named);
}
```

*Listing 10-4: Testing for an interface with `is` and converting with `as?`*

```text
pen costs 250 cents
ann@example.com has no price
true
```

If you find yourself writing many of these checks, consider adding a method to the interface instead, so each type answers the question itself.

## Standard interfaces

The Language Reference defines a set of interfaces that the language itself uses (§1-11.2). These are the ones you will meet first, with what Loup 1.25 actually does with them.

### `Iterable[T]` and `Iterator[T]`

A type that implements `Iterable[T]` works in a `for` loop. `iter()` returns an `Iterator[T]`, whose `next()` returns `Some(value)` until it is exhausted and then `None` (Language Reference §1-11.3):

```loup
@mode(web.compiled)

class Countdown implements Iterable[int] {
    pub let start: int;
    new(start: int) { this.start = start; }

    pub func iter(): Iterator[int] {
        return CountdownIter(this.start);
    }
}

class CountdownIter implements Iterator[int] {
    var current: int;
    new(start: int) { this.current = start; }

    pub modifier func next(): Option[int] {
        if (this.current <= 0) {
            return None;
        }
        let value = this.current;
        this.current = this.current - 1;
        return Some(value);
    }
}

func main(): void {
    for (n in Countdown(3)) {
        println(n);
    }
}
```

*Listing 10-5: Implementing the iteration protocol by hand*

```text
3
2
1
```

The iterator's `next()` changes its own state, so it is a `modifier`. The interface declares a plain `next()`; implementing it with a `modifier` method is allowed, because the implementation only adds a contract.

Most of the time you do not need two classes. A generator function (`gen func`, whose body uses `yield`) produces an `Iterable[T]` for you, and a class that wraps a list can hand out the list's own iterator:

```loup
@mode(web.compiled)

gen func countdown(start: int): Iterable[int] {
    var n = start;
    while (n > 0) {
        yield n;
        n = n - 1;
    }
}

class Playlist implements Iterable[string] {
    var songs: list[string] = [];
    new(songs: list[string]) { this.songs = songs; }

    pub func iter(): Iterator[string] {
        return this.songs.iter();
    }
}

func main(): void {
    for (n in countdown(3)) {
        println(n);
    }
    for (song in Playlist(["intro", "verse", "chorus"])) {
        println(song);
    }
}
```

```text
3
2
1
intro
verse
chorus
```

### `Comparable[T]` and `==`

`Comparable[T]` has one method, `compareTo(other: T): int`, which returns a negative number, zero or a positive number. Implementing it makes `<`, `>`, `<=` and `>=` work on your type (Language Reference §1-5.1):

```loup
@mode(web.compiled)

class Version implements Comparable[Version] {
    pub let major: int;
    pub let minor: int;
    new(major: int, minor: int) { this.major = major; this.minor = minor; }

    pub func compareTo(other: Version): int {
        if (this.major != other.major) {
            return this.major - other.major;
        }
        return this.minor - other.minor;
    }
}

func newest(versions: list[Version]): Version {
    var best = versions[0];
    for (v in versions) {
        if (v > best) {
            best = v;
        }
    }
    return best;
}

func main(): void {
    let releases = [Version(1, 4), Version(2, 0), Version(1, 10)];
    let n = newest(releases);
    println(i"newest is {$ n.major $}.{$ n.minor $}");
    println(Version(1, 9) < Version(1, 10));
}
```

*Listing 10-6: `compareTo` gives a class the ordering operators*

```text
newest is 2.0
true
```

The method is named `compareTo`; a method called `compare` does not satisfy the interface. Generic code bounded by `T: Comparable[T]` cannot yet call `compareTo` or `<` on a `T` in Stage0 1.25.1 ("type 'T0' has no method 'compareTo'"), which is why `newest` takes a `list[Version]` rather than a `list[T]`.

The Language Reference says `Comparable` also supplies `==` in terms of `compareTo`. Stage0 does not supply it yet, and `==` on two class instances compares identity, so this check gives the wrong answer:

```loup,not_desired_behavior
@mode(web.compiled)

class Version implements Comparable[Version] {
    pub let major: int;
    pub let minor: int;
    new(major: int, minor: int) { this.major = major; this.minor = minor; }

    pub func compareTo(other: Version): int {
        if (this.major != other.major) {
            return this.major - other.major;
        }
        return this.minor - other.minor;
    }
}

func main(): void {
    let installed = Version(1, 2);
    let required = Version(1, 2);
    println(installed == required);
}
```

```text
false
```

Two separately constructed objects are two objects, even with equal fields. Define `==` yourself with `operator func ==`; `Comparable[T]` extends `Equatable[T]`, which is what allows the operator:

```loup
@mode(web.compiled)

class Version implements Comparable[Version] {
    pub let major: int;
    pub let minor: int;
    new(major: int, minor: int) { this.major = major; this.minor = minor; }

    pub func compareTo(other: Version): int {
        if (this.major != other.major) {
            return this.major - other.major;
        }
        return this.minor - other.minor;
    }

    pub operator func ==(other: Version): bool {
        return this.compareTo(other) == 0;
    }
}

func main(): void {
    let installed = Version(1, 2);
    let required = Version(1, 2);
    println(installed == required);
}
```

```text
true
```

A class that needs `==` but no ordering declares `implements Equatable[T]` and the same `operator func ==`. Without one of those interfaces, `operator func ==` is `E_OPERATOR_NO_INTERFACE`.

> ⚠ **Planned.** `Hashable` (`func hashCode(): int`) is specified to let a class be used as a `dict` key or `set` element by value. In Loup 1.25 a class instance used as a key is looked up by identity, even when it implements `Hashable` and `Equatable`. Key dictionaries by a string or integer field instead.

### Rendering an object as text

`println(x)`, `str(x)` and `{$ x $}` render a class instance structurally, as `Name(field: value, …)` (Language Reference §1-16.4). A method named `to_string()` does **not** change that, which surprises Python (`__str__`) and Java (`toString`) programmers:

```loup,not_desired_behavior
@mode(web.compiled)

class Money {
    pub let cents: int;
    pub let currency: string;
    new(cents: int, currency: string) { this.cents = cents; this.currency = currency; }

    pub func to_string(): string {
        let whole = this.cents // 100;
        let frac = this.cents % 100;
        let pad = if (frac < 10) { "0" } else { "" };
        return i"{$ this.currency $} {$ whole $}.{$ pad $}{$ frac $}";
    }
}

func main(): void {
    let price = Money(1995, "USD");
    println(i"Price: {$ price $}");
}
```

```text
Price: Money(cents: 1995, currency: "USD")
```

`to_string()` runs only when you call it:

```loup
@mode(web.compiled)

class Money {
    pub let cents: int;
    pub let currency: string;
    new(cents: int, currency: string) { this.cents = cents; this.currency = currency; }

    pub func to_string(): string {
        let whole = this.cents // 100;
        let frac = this.cents % 100;
        let pad = if (frac < 10) { "0" } else { "" };
        return i"{$ this.currency $} {$ whole $}.{$ pad $}{$ frac $}";
    }
}

func main(): void {
    let price = Money(1995, "USD");
    let fee = Money(305, "EUR");
    println(i"Price: {$ price.to_string() $}");
    println(i"Fee: {$ fee.to_string() $}");
}
```

*Listing 10-7: Formatting an object by calling its method explicitly*

```text
Price: USD 19.95
Fee: EUR 3.05
```

> ⚠ **Planned.** The specified way to control how a type renders in `println`, interpolation and template fences is the `Renderable` interface, `func render(ctx: RenderContext): string` (Language Reference §1-16.4). In Loup 1.25, `RenderContext` is not available as a type, and the rendering pipeline does not call a class's `render` method, so implementing `Renderable` has no effect yet. Chapter 14 covers template fences.

### `Sendable`

`Sendable` is a marker interface with no methods. It says that values of a type may cross an isolation boundary, such as a message to a web worker (Language Reference §2-1.10). Structs whose fields are all `Sendable` qualify, and you can say so explicitly with `struct Point implements Sendable { x: int; y: int; }`. The worker APIs that check it in the browser are planned (Chapter 18), so in Loup 1.25 the marker documents intent.

## Async methods in interfaces

An interface method can be `async` (Language Reference §1-11.1). That is how you describe a data source whose real implementation will wait on the network while a test implementation answers at once:

```loup
@mode(web.compiled)

interface UserSource {
    async func displayName(id: int): Task[string];
}

class FakeUsers implements UserSource {
    pub async func displayName(id: int): Task[string] {
        return i"user-{$ id $}";
    }
}

async func greeting(src: UserSource, id: int): Task[string] {
    let name = await src.displayName(id);
    return "Welcome back, " + name;
}

async func main(): Task[void] {
    println(await greeting(FakeUsers(), 42));
}
```

```text
Welcome back, user-42
```

On the Loup 1.25 interpreter, calling an `async` function runs its body immediately; Chapter 15 explains what that means for ordering.

## A worked example: pluggable storage

A settings page needs to remember a few values. In tests you want an in-memory store; in the browser you will want one backed by `localStorage`. An interface lets the rest of the app not care. Here a third implementation wraps any other store and records what was written, a decorator in the classic sense:

```loup
@mode(web.compiled)

interface Storage {
    func get(key: string): Option[string];
    func set(key: string, value: string): void;
}

class MemoryStorage implements Storage {
    var items: dict[string, string] = {};

    pub func get(key: string): Option[string] {
        return this.items.get(key);
    }

    pub modifier func set(key: string, value: string): void {
        this.items[key] = value;
    }
}

class LoggingStorage implements Storage {
    var inner: Storage;
    pub var log: list[string] = [];

    new(inner: Storage) {
        this.inner = inner;
    }

    pub func get(key: string): Option[string] {
        return this.inner.get(key);
    }

    pub modifier func set(key: string, value: string): void {
        this.log.push(i"set {$ key $}");
        this.inner.set(key, value);
    }
}

safe func rememberLastPage(edits store: Storage, path: string): void {
    store.set("last_page", path);
}

func main(): void {
    let memory = MemoryStorage();
    let logged = LoggingStorage(memory);

    rememberLastPage(edits: logged, "/settings");
    rememberLastPage(edits: logged, "/profile");

    println(logged.get("last_page").unwrap_or("none"));
    println(memory.get("last_page").unwrap_or("none"));
    println(logged.log);
    println(memory.get("theme").unwrap_or("light"));
}
```

*Listing 10-8: Three `Storage` implementations behind one interface*

```text
/profile
/profile
["set last_page", "set last_page"]
light
```

`rememberLastPage` does not know which store it was given. `LoggingStorage` holds another `Storage` and forwards to it, so both names see the same data.

The interface declares `set` as a plain method and the classes implement it as `modifier`, which is allowed (an implementation may add a contract). The Language Reference (§1-11.1) also lets the interface itself say `modifier func set(...)`, so that every implementation must be a `modifier` and calls through the interface need a safe context. Stage0 1.25.1 does not parse `modifier` inside an interface body ("expected `func` in interface body"), and it does not apply the safe-context check to calls made through an interface type.

> ⚠ **Planned.** A `LocalStorage` class backed by the browser's storage (`web.storage`, Chapter 18) is the production implementation this design is meant for. No `web.*` module ships in Loup 1.25.

## When to reach for an interface

- Several types share behaviour, and callers should not care which one they hold (shapes, validators, stores).
- You want third-party code to be able to plug in (a storage backend, a logger).
- You want to swap a real implementation for a fake in tests.
- You want to add an optional behaviour with a default method.

If there is only one implementation, a plain class is enough. Add the interface when the second implementation appears.

## Try it yourself

1. Write a `Logger` interface with `info(msg: string)` and `error(msg: string)`. Implement `ConsoleLogger` with `println`, and `BufferLogger`, which collects messages in a list for tests.
2. Add an `Email` validator to Listing 10-2 that requires an `@` (use `value.contains("@")`), and run the password rules against an e-mail address to see which rules apply.
3. Remove `implements Comparable[Version]` from Listing 10-6 and read the error you get for `v > best`.

## Summary

- An interface lists method signatures; a type implements it only by declaring `implements` (nominal typing), and the compiler checks the whole contract there.
- Missing methods, mismatched signatures and undeclared implementations are compile errors with specific messages.
- Interfaces can be generic (`Validator[T]`), can extend other interfaces, and can provide default methods. Stage0 rejects `override` on a replaced interface default, although the specification asks for it.
- Use `is` to test for an interface and `as?` to convert; there is no narrowing after `is` yet.
- `Iterable[T]`/`Iterator[T]` make a type work in `for`; a `gen func` is usually simpler.
- `Comparable[T]` needs `compareTo` and gives `<`/`>`; `==` on class instances is identity until you define `operator func ==`.
- Rendering is structural; call your own formatting method explicitly. `Renderable` and value-based `Hashable` keys are planned.
- Interface methods can be `async`. `modifier` methods inside an interface body are specified but not yet parsed.
