Behavior modules make reusable operations explicit. A canonical module occupies the behavior's ordinary slot; named alternatives are values passed deliberately at the call site. Canonical defaults resolve only after the subject type is inferred; Ship never searches structurally for a convenient implementation. Rejected programs shows the missing and duplicate implementations that fail, and a message outside an implementation's associated row.
type! Task = {
title: String;
priority: Int;
}
module behavior Rank(Task) {
comparecompare: fn(left: Book.LanguageReference.BehaviorModules.Task, right: Book.LanguageReference.BehaviorModules.Task) -> Int = (leftleft: Book.LanguageReference.BehaviorModules.Task: Task, rightright: Book.LanguageReference.BehaviorModules.Task: Task) => rightright: Book.LanguageReference.BehaviorModules.Task.prioritypriority: Int - leftleft: Book.LanguageReference.BehaviorModules.Task.prioritypriority: Int;
}
module behavior ByTitle: Rank(Task) {
comparecompare: fn(left: Book.LanguageReference.BehaviorModules.Task, right: Book.LanguageReference.BehaviorModules.Task) -> Int = (leftleft: Book.LanguageReference.BehaviorModules.Task: Task, rightright: Book.LanguageReference.BehaviorModules.Task: Task) =>
String.comparecompare: pure fn!(left: String, right: String) -> Int(leftleft: Book.LanguageReference.BehaviorModules.Task.titletitle: String, rightright: Book.LanguageReference.BehaviorModules.Task.titletitle: String);
}
module behavior Reverse(struct 'a)(Base: Rank(struct 'a)): Rank(struct 'a) {
comparecompare: fn(left: 'a, right: 'a) -> Int = (leftleft: 'a: 'a, rightright: 'a: 'a) => Base.comparecompare: pure fn!(left: 'a, right: 'a) -> Int(rightright: 'a, leftleft: 'a);
}
let pick_firstpick_first: fn(struct 'a)(left: 'a, right: 'a, ~rank: Book.LanguageReference.BehaviorModules.Rank('a) = Rank(struct 'a)) -> 'a = fn(struct 'a)(leftleft: 'a: 'a, rightright: 'a: 'a, ~rankrank: Book.LanguageReference.BehaviorModules.Rank('a) = Rank(struct 'a)) {
if rankrank: Book.LanguageReference.BehaviorModules.Rank('a).comparecompare: pure fn!(left: 'a, right: 'a) -> Int(leftleft: 'a, rightright: 'a) <= 00: Int {
| leftleft: 'a
| rightright: 'a
}
};
Explicit Behavior
let shipship: Book.LanguageReference.BehaviorModules.Task = Task { title = "ship""ship": String; priority = 11: Int; };
let docsdocs: Book.LanguageReference.BehaviorModules.Task = Task { title = "docs""docs": String; priority = 22: Int; };
expect ✓pick_firstpick_first: fn(struct 'a)(left: Book.LanguageReference.BehaviorModules.Task, right: Book.LanguageReference.BehaviorModules.Task, ~rank: Book.LanguageReference.BehaviorModules.Rank(Book.LanguageReference.BehaviorModules.Task) = Rank(struct 'a)) -> Book.LanguageReference.BehaviorModules.Task(shipship: Book.LanguageReference.BehaviorModules.Task, docsdocs: Book.LanguageReference.BehaviorModules.Task).titletitle: String == "docs""docs": String;
expect ✓pick_firstpick_first: fn(struct 'a)(left: Book.LanguageReference.BehaviorModules.Task, right: Book.LanguageReference.BehaviorModules.Task, ~rank: Book.LanguageReference.BehaviorModules.Rank(Book.LanguageReference.BehaviorModules.Task) = Rank(struct 'a)) -> Book.LanguageReference.BehaviorModules.Task(shipship: Book.LanguageReference.BehaviorModules.Task, docsdocs: Book.LanguageReference.BehaviorModules.Task, rank = ByTitleByTitle: Book.LanguageReference.BehaviorModules.Rank(Book.LanguageReference.BehaviorModules.Task)).titletitle: String == "docs""docs": String;
expect ✓pick_firstpick_first: fn(struct 'a)(left: Book.LanguageReference.BehaviorModules.Task, right: Book.LanguageReference.BehaviorModules.Task, ~rank: Book.LanguageReference.BehaviorModules.Rank(Book.LanguageReference.BehaviorModules.Task) = Rank(struct 'a)) -> Book.LanguageReference.BehaviorModules.Task(shipship: Book.LanguageReference.BehaviorModules.Task, docsdocs: Book.LanguageReference.BehaviorModules.Task, rank = Reverse(ByTitleByTitle: Book.LanguageReference.BehaviorModules.Rank(Book.LanguageReference.BehaviorModules.Task))).titletitle: String == "ship""ship": String;An adapter application is itself a behavior module: call its operations directly, or name it with a module alias that several sites share. The adapter body is checked once against its result signature.
module TitleDescending = Reverse(ByTitleByTitle: Book.LanguageReference.BehaviorModules.Rank(Book.LanguageReference.BehaviorModules.Task));
Adapter Application
let shipship: Book.LanguageReference.BehaviorModules.Task = Task { title = "ship""ship": String; priority = 11: Int; };
let docsdocs: Book.LanguageReference.BehaviorModules.Task = Task { title = "docs""docs": String; priority = 22: Int; };
expect ✓Reverse(ByTitle).compare(shipship: Book.LanguageReference.BehaviorModules.Task, docsdocs: Book.LanguageReference.BehaviorModules.Task) < 00: Int;
expect ✓TitleDescendingTitleDescending: Book.LanguageReference.BehaviorModules.Rank(Book.LanguageReference.BehaviorModules.Task).comparecompare: pure fn!(left: Book.LanguageReference.BehaviorModules.Task, right: Book.LanguageReference.BehaviorModules.Task) -> Int(shipship: Book.LanguageReference.BehaviorModules.Task, docsdocs: Book.LanguageReference.BehaviorModules.Task) == Reverse(ByTitle).compare(shipship: Book.LanguageReference.BehaviorModules.Task, docsdocs: Book.LanguageReference.BehaviorModules.Task);
expect ✓pick_firstpick_first: fn(struct 'a)(left: Book.LanguageReference.BehaviorModules.Task, right: Book.LanguageReference.BehaviorModules.Task, ~rank: Book.LanguageReference.BehaviorModules.Rank(Book.LanguageReference.BehaviorModules.Task) = Rank(struct 'a)) -> Book.LanguageReference.BehaviorModules.Task(shipship: Book.LanguageReference.BehaviorModules.Task, docsdocs: Book.LanguageReference.BehaviorModules.Task, rank = TitleDescendingTitleDescending: Book.LanguageReference.BehaviorModules.Rank(Book.LanguageReference.BehaviorModules.Task)).titletitle: String == "ship""ship": String;Default operations
An interface can give an operation a default body. Implementations inherit it unless they write their own.
module behavior Ranked(struct 'a) {
rank: pure fn(value: 'a) -> Int;
let labellabel: fn(value: 'a) -> String = (valuevalue: 'a) => Int.to_stringto_string: pure fn!(value: Int) -> String(rankrank: pure fn(value: 'a) -> Int(valuevalue: 'a));
}
module struct Point {
type t = {
x: Int;
}
}
module behavior Ranked(Point) {
rankrank: fn(value: Book.LanguageReference.BehaviorModules.Point) -> Int = (valuevalue: Book.LanguageReference.BehaviorModules.Point) => valuevalue: Book.LanguageReference.BehaviorModules.Point.xx: Int;
}
Inherited Default Operation
let pointpoint: Book.LanguageReference.BehaviorModules.Point = Point { x = 33: Int; };
expect ✓Ranked.label(pointpoint: Book.LanguageReference.BehaviorModules.Point) == "3""3": String;
expect ✓Ranked.rank(pointpoint: Book.LanguageReference.BehaviorModules.Point) == 33: Int;A signed default writes the operation type and body together. Witnesses inherit it unless they override the name. Anonymous modules inherit the same way.
module behavior Greet(struct 'a) {
name: pure fn(value: 'a) -> String;
greetgreet: fn(value: 'a) -> String: pure fn(value: 'a) -> String = (valuevalue: 'a) => "hi ""hi ": String ++ namename: pure fn(value: 'a) -> String(valuevalue: 'a);
}
module behavior Greet(Point) {
namename: fn(value: Book.LanguageReference.BehaviorModules.Point) -> String = (valuevalue: Book.LanguageReference.BehaviorModules.Point) => "p""p": String ++ Int.to_stringto_string: pure fn!(value: Int) -> String(valuevalue: Book.LanguageReference.BehaviorModules.Point.xx: Int);
}
module behavior LoudGreet: Greet(Point) {
namename: fn(value: Book.LanguageReference.BehaviorModules.Point) -> String = (valuevalue: Book.LanguageReference.BehaviorModules.Point) => "P""P": String ++ Int.to_stringto_string: pure fn!(value: Int) -> String(valuevalue: Book.LanguageReference.BehaviorModules.Point.xx: Int);
greetgreet: fn(value: Book.LanguageReference.BehaviorModules.Point) -> String = (valuevalue: Book.LanguageReference.BehaviorModules.Point) => "HI ""HI ": String ++ namename: fn(value: Book.LanguageReference.BehaviorModules.Point) -> String(valuevalue: Book.LanguageReference.BehaviorModules.Point);
}
Signed And Overridden Defaults
let pointpoint: Book.LanguageReference.BehaviorModules.Point = Point { x = 33: Int; };
expect ✓Greet.greet(pointpoint: Book.LanguageReference.BehaviorModules.Point) == "hi p3""hi p3": String;
expect ✓LoudGreet.greetgreet: fn(value: Book.LanguageReference.BehaviorModules.Point) -> String(pointpoint: Book.LanguageReference.BehaviorModules.Point) == "HI P3""HI P3": String;
let rankedranked: Book.LanguageReference.BehaviorModules.Ranked(Book.LanguageReference.BehaviorModules.Point): Ranked(Point) = module #{
rank = (valuevalue: Book.LanguageReference.BehaviorModules.Point) => valuevalue: Book.LanguageReference.BehaviorModules.Point.xx: Int;
};
expect ✓rankedranked: Book.LanguageReference.BehaviorModules.Ranked(Book.LanguageReference.BehaviorModules.Point).labellabel: fn(value: Book.LanguageReference.BehaviorModules.Point) -> String(Point { x = 44: Int; }) == "4""4": String;Anonymous modules have fresh identity and infer their signature from an expected module slot. Write module: Signature #{ ... } only when the surrounding expression does not already provide that signature.
Local Anonymous Behavior
let reverse_titlereverse_title: Book.LanguageReference.BehaviorModules.Rank(Book.LanguageReference.BehaviorModules.Task): Rank(Task) = module #{
compare = (leftleft: Book.LanguageReference.BehaviorModules.Task: Task, rightright: Book.LanguageReference.BehaviorModules.Task: Task) =>
00: Int - String.comparecompare: pure fn!(left: String, right: String) -> Int(leftleft: Book.LanguageReference.BehaviorModules.Task.titletitle: String, rightright: Book.LanguageReference.BehaviorModules.Task.titletitle: String);
};
let shortshort: Book.LanguageReference.BehaviorModules.Task = Task { title = "fix""fix": String; priority = 11: Int; };
let longlong: Book.LanguageReference.BehaviorModules.Task = Task { title = "document""document": String; priority = 22: Int; };
expect ✓pick_firstpick_first: fn(struct 'a)(left: Book.LanguageReference.BehaviorModules.Task, right: Book.LanguageReference.BehaviorModules.Task, ~rank: Book.LanguageReference.BehaviorModules.Rank(Book.LanguageReference.BehaviorModules.Task) = Rank(struct 'a)) -> Book.LanguageReference.BehaviorModules.Task(shortshort: Book.LanguageReference.BehaviorModules.Task, longlong: Book.LanguageReference.BehaviorModules.Task, rank = reverse_titlereverse_title: Book.LanguageReference.BehaviorModules.Rank(Book.LanguageReference.BehaviorModules.Task)).titletitle: String == "fix""fix": String;Associated types
An interface can leave a type for each implementation to choose. Inside the interface the bare name stands for that choice; a call through the canonical module sees the type its implementation wrote.
module behavior Scale(struct 'a) {
type Factor;
apply: pure fn!(value: 'a, factor: Factor) -> Int;
}
module behavior Scale(Point) {
type Factor = Int
applyapply: fn(value: Book.LanguageReference.BehaviorModules.Point, factor: Int) -> Int = (valuevalue: Book.LanguageReference.BehaviorModules.Point, factorfactor: Int) => valuevalue: Book.LanguageReference.BehaviorModules.Point.xx: Int * factorfactor: Int;
}
Associated Input
expect ✓Scale.apply(Point { x = 33: Int; }, 22: Int) == 66: Int;A tag row is an ordinary choice. Each implementation names the messages it accepts, and a call through its witness accepts only those tags.
type Next('state) =
| Continue('state)
| Stop
module behavior Handler(struct 'state) {
type Message;
handle: pure fn!(state: 'state, message: Message) -> Next('state);
}
module behavior Handler(Point) {
type Message = [
| :shift(Int)
| :stop
]
handlehandle: fn(state: Book.LanguageReference.BehaviorModules.Point, message: [< :shift(Int) | :stop ]) -> Book.LanguageReference.BehaviorModules.Next(Book.LanguageReference.BehaviorModules.Point) = (statestate: Book.LanguageReference.BehaviorModules.Point, messagemessage: [< :shift(Int) | :stop ]) =>
match messagemessage: [< :shift(Int) | :stop ] {
| :shift(byby: Int) -> ContinueContinue: fn(Book.LanguageReference.BehaviorModules.Point) -> Book.LanguageReference.BehaviorModules.Next(Book.LanguageReference.BehaviorModules.Point)({ statestate: Book.LanguageReference.BehaviorModules.Point with x = statestate: Book.LanguageReference.BehaviorModules.Point.xx: Int + byby: Int; })
| :stop -> Stop
};
}
let deliverdeliver: fn(struct 'state)(state: 'state, message: Book.LanguageReference.BehaviorModules.Handler.Message('state), ~handler: Book.LanguageReference.BehaviorModules.Handler('state) = Handler(struct 'state)) -> 'state = fn(struct 'state)(statestate: 'a: 'state, messagemessage: Book.LanguageReference.BehaviorModules.Handler.Message('a), ~handlerhandler: Book.LanguageReference.BehaviorModules.Handler('a) = Handler(struct 'state)) {
match handlerhandler: Book.LanguageReference.BehaviorModules.Handler('a).handlehandle: pure fn!(state: 'a, message: Book.LanguageReference.BehaviorModules.Handler.Message('a)) -> Book.LanguageReference.BehaviorModules.Next('a)(statestate: 'a, messagemessage: Book.LanguageReference.BehaviorModules.Handler.Message('a)) {
| Continue(nextnext: 'a) -> nextnext: 'a
| Stop -> statestate: 'a
}
};
Associated Message Row
let pointpoint: Book.LanguageReference.BehaviorModules.Point = Point { x = 11: Int; };
expect ✓deliverdeliver: fn(struct 'state)(state: Book.LanguageReference.BehaviorModules.Point, message: Book.LanguageReference.BehaviorModules.Handler.Message(Book.LanguageReference.BehaviorModules.Point), ~handler: Book.LanguageReference.BehaviorModules.Handler(Book.LanguageReference.BehaviorModules.Point) = Handler(struct 'state)) -> Book.LanguageReference.BehaviorModules.Point(pointpoint: Book.LanguageReference.BehaviorModules.Point, :shift(22: Int)).xx: Int == 33: Int;
expect ✓deliverdeliver: fn(struct 'state)(state: Book.LanguageReference.BehaviorModules.Point, message: Book.LanguageReference.BehaviorModules.Handler.Message(Book.LanguageReference.BehaviorModules.Point), ~handler: Book.LanguageReference.BehaviorModules.Handler(Book.LanguageReference.BehaviorModules.Point) = Handler(struct 'state)) -> Book.LanguageReference.BehaviorModules.Point(pointpoint: Book.LanguageReference.BehaviorModules.Point, :stop).xx: Int == 11: Int;Behavior witnesses in types
When behavior determines a persistent value's invariant, its normalized module path is part of the type. Two values with different witnesses do not silently mix even when their stored fields have the same representation. The companion .shc publishes each reusable behavior with a bodyless complete head. Public contracts require a nameable witness and have no _ witness holes.
type! RankedTasks(order: Rank(Task)) = {
tasks: Vector(Task);
}
module TitleOrder = ByTitle;
let title_counttitle_count: fn(ranked: Book.LanguageReference.BehaviorModules.RankedTasks(Book.LanguageReference.BehaviorModules.ByTitle)) -> Int = (rankedranked: Book.LanguageReference.BehaviorModules.RankedTasks(Book.LanguageReference.BehaviorModules.ByTitle): RankedTasks(ByTitle)) =>
Vector.lengthlength: pure fn!('a)(items: Vector(Book.LanguageReference.BehaviorModules.Task)) -> Int(rankedranked: Book.LanguageReference.BehaviorModules.RankedTasks(Book.LanguageReference.BehaviorModules.ByTitle).taskstasks: Vector(Book.LanguageReference.BehaviorModules.Task));
Named Witness Identity
let taskstasks: Vector(Book.LanguageReference.BehaviorModules.Task) = [
Task { title = "ship""ship": String; priority = 11: Int; },
Task { title = "docs""docs": String; priority = 22: Int; },
];
let by_priorityby_priority: Book.LanguageReference.BehaviorModules.RankedTasks(Book.LanguageReference.BehaviorModules.Rank(Book.LanguageReference.BehaviorModules.Task)): RankedTasks(Rank(Task)) = RankedTasks { tasks = taskstasks: Vector(Book.LanguageReference.BehaviorModules.Task); };
✓by_priorityby_priority: Book.LanguageReference.BehaviorModules.RankedTasks(Book.LanguageReference.BehaviorModules.Rank(Book.LanguageReference.BehaviorModules.Task)): RankedTasks(Rank(Task));
let by_titleby_title: Book.LanguageReference.BehaviorModules.RankedTasks(Book.LanguageReference.BehaviorModules.ByTitle): RankedTasks(ByTitle) = RankedTasks { tasks = taskstasks: Vector(Book.LanguageReference.BehaviorModules.Task); };
✓by_titleby_title: Book.LanguageReference.BehaviorModules.RankedTasks(Book.LanguageReference.BehaviorModules.ByTitle): RankedTasks(ByTitle);
let by_aliasby_alias: Book.LanguageReference.BehaviorModules.RankedTasks(Book.LanguageReference.BehaviorModules.ByTitle): RankedTasks(TitleOrder) = RankedTasks { tasks = taskstasks: Vector(Book.LanguageReference.BehaviorModules.Task); };
✓by_aliasby_alias: Book.LanguageReference.BehaviorModules.RankedTasks(Book.LanguageReference.BehaviorModules.ByTitle): RankedTasks(ByTitle);
let reversedreversed: Book.LanguageReference.BehaviorModules.RankedTasks(Reverse(ByTitle)): RankedTasks(Reverse(ByTitle)) = RankedTasks { tasks = taskstasks: Vector(Book.LanguageReference.BehaviorModules.Task); };
✓reversedreversed: Book.LanguageReference.BehaviorModules.RankedTasks(Reverse(ByTitle)): RankedTasks(Reverse(ByTitle));
let by_adapter_aliasby_adapter_alias: Book.LanguageReference.BehaviorModules.RankedTasks(Reverse(ByTitle)): RankedTasks(TitleDescending) = reversedreversed: Book.LanguageReference.BehaviorModules.RankedTasks(Reverse(ByTitle));
✓by_adapter_aliasby_adapter_alias: Book.LanguageReference.BehaviorModules.RankedTasks(Reverse(ByTitle)): RankedTasks(Reverse(ByTitle));
expect ✓(
title_counttitle_count: fn(ranked: Book.LanguageReference.BehaviorModules.RankedTasks(Book.LanguageReference.BehaviorModules.ByTitle)) -> Int(by_aliasby_alias: Book.LanguageReference.BehaviorModules.RankedTasks(Book.LanguageReference.BehaviorModules.ByTitle)),
Vector.lengthlength: pure fn!('a)(items: Vector(Book.LanguageReference.BehaviorModules.Task)) -> Int(by_adapter_aliasby_adapter_alias: Book.LanguageReference.BehaviorModules.RankedTasks(Reverse(ByTitle)).taskstasks: Vector(Book.LanguageReference.BehaviorModules.Task)),
) == (22: Int, 22: Int);Runtime choices preserve distinct invariants with an ordinary variant. Pattern matching exposes the corresponding concrete witness in each arm.
type! TaskRanking =
| Priority(RankedTasks(Rank(Task)))
| Title(RankedTasks(ByTitle))
let ranking_countranking_count: fn(Book.LanguageReference.BehaviorModules.TaskRanking) -> Int = function {
| Priority(rankedranked: Book.LanguageReference.BehaviorModules.RankedTasks(Rank(Book.LanguageReference.BehaviorModules.Task))) -> Vector.lengthlength: pure fn!('a)(items: Vector(Book.LanguageReference.BehaviorModules.Task)) -> Int(rankedranked: Book.LanguageReference.BehaviorModules.RankedTasks(Rank(Book.LanguageReference.BehaviorModules.Task)).taskstasks: Vector(Book.LanguageReference.BehaviorModules.Task))
| Title(rankedranked: Book.LanguageReference.BehaviorModules.RankedTasks(ByTitle)) -> Vector.lengthlength: pure fn!('a)(items: Vector(Book.LanguageReference.BehaviorModules.Task)) -> Int(rankedranked: Book.LanguageReference.BehaviorModules.RankedTasks(ByTitle).taskstasks: Vector(Book.LanguageReference.BehaviorModules.Task))
};
Explicit Runtime Choice
let taskstasks: Vector(Book.LanguageReference.BehaviorModules.Task) = [Task { title = "ship""ship": String; priority = 11: Int; }];
let prioritypriority: Book.LanguageReference.BehaviorModules.TaskRanking = PriorityPriority: fn(Book.LanguageReference.BehaviorModules.RankedTasks(Rank(Book.LanguageReference.BehaviorModules.Task))) -> Book.LanguageReference.BehaviorModules.TaskRanking(RankedTasks { tasks = taskstasks: Vector(Book.LanguageReference.BehaviorModules.Task); });
let titletitle: Book.LanguageReference.BehaviorModules.TaskRanking = TitleTitle: fn(Book.LanguageReference.BehaviorModules.RankedTasks(ByTitle)) -> Book.LanguageReference.BehaviorModules.TaskRanking(RankedTasks { tasks = taskstasks: Vector(Book.LanguageReference.BehaviorModules.Task); });
expect ✓(ranking_countranking_count: fn(Book.LanguageReference.BehaviorModules.TaskRanking) -> Int(prioritypriority: Book.LanguageReference.BehaviorModules.TaskRanking), ranking_countranking_count: fn(Book.LanguageReference.BehaviorModules.TaskRanking) -> Int(titletitle: Book.LanguageReference.BehaviorModules.TaskRanking)) == (11: Int, 11: Int);