Section 8.1

Throwing and catching

throw raises an instance of an archetype; catch takes it by its exact type.

An error, in Archon, is an instance of an archetype — section 7 just gave you everything needed to understand one.

archetype FailedPotion {
    message: str;
}

archetype Still {
    distil(dose: int) -> int {
        if (dose > 3) {
            throw FailedPotion(message: "too much mandrake");
        }
        return dose * 2;
    }
}

@entry(start)
object MyProgram {
    start() {
        var still := Still();

        try {
            Console.print(text: $"essence: {still.distil(dose: 2)}");
            Console.print(text: $"essence: {still.distil(dose: 5)}");
            Console.print(text: "never reached");
        } catch (raised: FailedPotion) {
            Console.print(text: $"failed: {raised.message}");
        }

        Console.print(text: "the program goes on");
    }
}

throw raises an instance

throw FailedPotion(message: "too much mandrake");

FailedPotion is an ordinary archetype — a message field, nothing special, not even an imposed naming convention. throw raises an instance of it, built like any other. Never a bare value: an error carries what it takes to tell its story, and an archetype is what knows how to carry fields.

And remember 5.2: throw ends the method as well as return does. A method that throws returns nothing — it has answered another way.

The error climbs until a catch

distil(dose: 5) throws deep inside the still; the catch is in start. In between, the error climbs: it leaves distil, skips the “never reached” line, and looks for the first catch that matches. Uncaught, it would have climbed to the entry point and stopped the program — which is what 6.1 showed you.

} catch (raised: FailedPotion) {
    Console.print(text: $"failed: {raised.message}");
}

The catch names a type and receives the raised instance: raised.message reads its field, like any field from section 7.

The match is exact

catch (raised: FailedPotion) only takes what was thrown as FailedPotion — never another archetype that happens to have the same fields. It is a deliberate departure from the rest of the language: here the type’s identity matters, not its shape.

Your turn

  • Set the first dose to 4: the “essence” line disappears, the failure comes on the first call.
  • Remove the try/catch and keep the dose at 5: the error climbs to the very top, the program stops — “the program goes on” never prints.
  • Give FailedPotion a second field — dose: int — throw it filled, and print it in the catch. An error carries whatever you put in it.