Inspecting porch/gemstone · the kinds · the language · the modules
porch/gemstone
- id
- porch/gemstone
- kind chain
- porch/gemstone
- owner
- the town
- made by
- the town
- version
- 5
- pin
- dakis-vumab
- state
- 590 bytes
Its spells
To cast them, come in.
Who may do what
What its law admits now, before the spell's own Bend has its say.
Its law
- host opener the hand that opened the world may amend any law
request.kind == 2 and request.subject == "the town" - law owner in its Bendthe owner stays, names its circle and alone reprograms it; its name, description and copying are its keeper's once it is inspirited, the owner's before; anyone moves it and places rules, wants and verbs on it
(((unchanged(owner)) and (((request.kind == 0) or (request.kind == 4)) or (request.subject == new.owner))) and ((unchanged(circle)) or (request.subject == new.owner))) and (((((unchanged(name)) and (unchanged(description))) and (unchanged(copyable))) or (request.subject == new.keeper)) or ((request.subject == new.owner) and (old.keeper == ""))) - law keeper in its Bendit is inspirited once, by whoever does it, and keeps that keeper
(writeOnce(keeper)) and ((unchanged(keeper)) or (request.subject == new.keeper)) - law methods in its Bendonly its own methods write it; a proposed write is refused
(not (request.kind == 3)) and ((request.kind == 0) implies (not (request.method == "")))
Its Bend
what it runs: its entry module 373 lines
edition ObjectiveBend 1
import ./Abi.obend as Abi
import ./List.obend as Lists
import ./Document.obend as Document
import ./Plan.obend as Plans
import ./Form.obend as Form
import ./World.obend as World
import ./Card.obend as Card
import ./Scenes.obend as Scenes
import ./Relation.obend as Relations
import ./Rows.obend as Rows
import ./Text.obend as Text
# THING (docs/METAPHYSICS.md §7): something that lies in a room or is held, and is given by an
# offer its taker accepts. owner: who made it and alone reprograms it. A thing may lie unclaimed
# (`keeper` ""): anyone inspirits it once (`inspirit / surrounding:`), and their words become what
# surrounds it (its description) and they its keeper, who alone renames or redescribes it after;
# the room it lies in gets the line. Before that its lenses are the owner's.
#
# Who may change it is its law's alone, never its code's, so what the host lends (`delvetalk <thing>
# lend / to / method: set / until`) runs: the borrower's `set` is judged with the keeper as subject.
# A circle is a bond at rest (kind/bond): the owner names one (`set / circle:`), and the thing amends
# its own law so the bond's signers set it as its keeper does (LAW §4 D: `request.subject in
# "<bond>".marks.who`). Its law is the package's below; `lawWith(circle)` is that text with a
# circle's signers in the keeper's disjunct, which naming a circle amends it to (`lawText()`, the
# package's own, is test-checked equal to the clauses declared here).
# lines: what was done to it by a verb a hand grew on it (`act`: a gesture, one line tagged with
# the verb, and the thing tended), the newest 32; `next` numbers them.
# offer: custody offered to someone, open until a clock time (a card address is a versioned
# capability, not a durable inbox: giving is offer and accept, never a pointer move).
sum Offer:
none: {}
open: {to: Plans.Reference, until: Nat}
record State:
owner: String
keeper: String
keeperHandle: String
name: String
description: String
holder: Plans.Reference
holderHandle: String
location: Plans.Reference
offer: Offer
copyable: Bool
circle: String
lines: Relations.Relation<Rows.Line>
next: Nat
tended: Nat
def initial() -> State:
{owner: "", keeper: "", keeperHandle: "", name: "", description: "", holder: Plans.nobody(), holderHandle: "", location: Plans.nobody(), offer: Offer.none({}), copyable: true, circle: "", lines: Relations.empty(), next: 1n, tended: 0n}
def relations() -> Relations.Decls:
Lists.append(Relations.Decls.nil(), {field: "lines", key: Text.words("author n"), limit: 32n})
want claimed "nothing surrounds it yet": new.keeper == "" asks delvetalk {request.object} inspirit
# A thing as edition 12 keeps it (a carry lays this package over it, `migrate`): with `tendedAt`,
# an entry that a carry from before edition 12 left counting another chain's; tending is the clock's.
record State12:
owner: String
keeper: String
keeperHandle: String
name: String
description: String
holder: Plans.Reference
holderHandle: String
location: Plans.Reference
offer: Offer
copyable: Bool
circle: String
lines: Relations.Relation<Rows.Line>
next: Nat
tended: Nat
tendedAt: Nat
def migrate(old: State12) -> State:
{owner: old.owner, keeper: old.keeper, keeperHandle: old.keeperHandle, name: old.name, description: old.description, holder: old.holder, holderHandle: old.holderHandle, location: old.location, offer: old.offer, copyable: old.copyable, circle: old.circle, lines: old.lines, next: old.next, tended: old.tended}
def lineKey(line: Rows.Line) -> {author: String, n: Nat}:
{author: line.author, n: line.n}
# A grown verb used on the thing (the host runs `act` for a row of its verb table): a gesture, one
# line tagged with the verb (its words, or the verb itself for one that takes none), and the thing tended; its card
# shows the newest three.
def act(state: State, input: {verb: String, text: String}, context: Abi.Context) -> Activity<Nat>:
let written(_) = write {lines: insert {author: context.principal, handle: context.handle, verb: input.verb, text: if Text.blank(input.text) then input.verb else input.text, at: context.height, n: state.next}, next: add 1n, tended: set context.clock}
match heard(state, input.verb, context):
case _: actTold(state, input, context)
def actTold(state: State, input: {verb: String, text: String}, context: Abi.Context) -> Activity<Nat>:
let said = Document.text("#{natText(state.next)} {input.verb}: {input.text}\n\n")
Card.tell(state.next, Document.concat(said, renderAfter(extend(state, {lines: Relations.insert(state.lines, {author: context.principal, handle: context.handle, verb: input.verb, text: input.text, at: context.height, n: state.next}, lineKey), next: state.next + 1n}), context, Document.size(said))))
# Anyone moves a thing (its own code checks who holds it); its name, description and copying are
# its keeper's once it is inspirited (and its circle's, when it has one), its owner's before; the
# owner alone names the circle, amends or reprograms it.
law owner "the owner stays, names its circle and alone reprograms it; its name, description and copying are its keeper's once it is inspirited, the owner's before; anyone moves it and places rules, wants and verbs on it": unchanged(owner) and (request.kind == 0 or request.kind == rule or request.subject == new.owner) and (unchanged(circle) or request.subject == new.owner) and ((unchanged(name) and unchanged(description) and unchanged(copyable)) or request.subject == new.keeper or (request.subject == new.owner and old.keeper == ""))
law keeper "it is inspirited once, by whoever does it, and keeps that keeper": writeOnce(keeper) and (unchanged(keeper) or request.subject == new.keeper)
law methods "only its own methods write it; a proposed write is refused": not (request.kind == proposed) and (request.kind == 0 implies not (request.method == ""))
def lawText() -> String:
lawWith("")
def lawWith(circle: String) -> String:
let circled = if circle == "" then "" else " or request.subject in \"{circle}\".marks.who"
let whose = if circle == "" then "its keeper's" else "its keeper's and {circle}'s signers'"
"law owner \"the owner stays, names its circle and alone reprograms it; its name, description and copying are {whose} once it is inspirited, the owner's before; anyone moves it and places rules, wants and verbs on it\": unchanged(owner) and (request.kind == 0 or request.kind == rule or request.subject == new.owner) and (unchanged(circle) or request.subject == new.owner) and ((unchanged(name) and unchanged(description) and unchanged(copyable)) or request.subject == new.keeper{circled} or (request.subject == new.owner and old.keeper == \"\"))\nlaw keeper \"it is inspirited once, by whoever does it, and keeps that keeper\": writeOnce(keeper) and (unchanged(keeper) or request.subject == new.keeper)\nlaw methods \"only its own methods write it; a proposed write is refused\": not (request.kind == proposed) and (request.kind == 0 implies not (request.method == \"\"))"
# The second tier: custody moves from one holder to another only by an accepted offer:
# the new holder is the offer's `to`, the turn is that avatar's own call (context.caller),
# and the clock (context.clock) is before `until`. Picking up (from nobody) and putting down (to
# nobody) are the code's own paths and pass.
def law(old: State, new: State, request: Abi.Request) -> Abi.Verdict:
if Plans.same(old.holder, new.holder) || Plans.isNobody(old.holder) || Plans.isNobody(new.holder) then Abi.Verdict.admitted({}) else transferable(old.offer, new.holder, request.context)
def transferable(offer: Offer, holder: Plans.Reference, context: Abi.Context) -> Abi.Verdict:
match offer:
case none(_): Abi.Verdict.refused({clause: "noOffer", reading: "The thing is not offered to anyone."})
case open(o): if Plans.same(holder, o.to) == false || context.caller != o.to.object then Abi.Verdict.refused({clause: "notOffered", reading: "The thing is offered to another, and only that avatar's own call takes it."}) else if context.clock >= o.until then Abi.Verdict.refused({clause: "expired", reading: "The offer has expired."}) else Abi.Verdict.admitted({})
type Done = Scenes.Done
# Why a thing refuses: each arm a clause and its reading (names already shown).
sum Why:
alreadyHeld: {by: String}
placeSilent: {}
notHolding: {}
notHolder: {}
alreadyOffered: {to: String, until: Nat}
tooLate: {now: Nat}
noRecipient: {}
nothingOffered: {}
notOffered: {to: String}
expired: {until: Nat}
inspirited: {by: String}
unsettled: {bond: String}
noBond: {bond: String}
notAmended: {clause: String}
def refusal(w: Why) -> Card.Refusal:
match w:
case alreadyHeld(a): {clause: "alreadyHeld", reading: "Already held by {a.by}"}
case placeSilent(_): {clause: "placeSilent", reading: "The room did not answer."}
case notHolding(_): {clause: "notHolding", reading: "You are not holding it."}
case notHolder(_): {clause: "notHolder", reading: "Only its holder withdraws an offer."}
case alreadyOffered(a): {clause: "alreadyOffered", reading: "Already offered to {a.to} until {natText(a.until)}; withdraw it first."}
case tooLate(t): {clause: "tooLate", reading: "until must be after the clock, now {natText(t.now)}"}
case noRecipient(_): {clause: "noRecipient", reading: "Offer it to someone."}
case nothingOffered(_): {clause: "nothingOffered", reading: "Nothing is offered."}
case notOffered(n): {clause: "notOffered", reading: "It is offered to {n.to}"}
case expired(e): {clause: "expired", reading: "The offer ran until clock {natText(e.until)}"}
case inspirited(i): {clause: "keeper", reading: "Inspirited already, by {i.by}, who keeps it"}
case unsettled(u): {clause: "unsettled", reading: "{u.bond} is not at rest: a circle is a bond every party signed"}
case noBond(n): {clause: "noBond", reading: "{n.bond} is no bond you may read"}
case notAmended(n): {clause: n.clause, reading: "Its law did not take the circle: only its owner names one"}
def refused(w: Why) -> Done:
Done.refused(refusal(w))
# A refusal, answered by name (Card.refusing), and nothing written.
def told(w: Why, context: Abi.Context) -> Activity<Done>:
Card.refusing(refused(w), refusal(w), context)
# Custody recorded, and the speaker shown the line and the card as it now stands.
def held(context: Abi.Context, holder: Plans.Reference, location: Plans.Reference, line: String, state: State) -> Activity<Done>:
let handle = if Plans.isNobody(holder) then "" else context.handle
let written(_) = write {holder: set holder, holderHandle: set handle, location: set location}
Card.tell(Done.done({}), Document.concat(Document.text(line), render(extend(state, {holder: holder, holderHandle: handle, location: location}), context)))
def person(context: Abi.Context) -> Plans.Reference:
{world: context.world, object: context.principal}
# Pick the thing up from the room it lies in: the room lets it go (it refuses by name a taker
# not in it, or a thing not lying there), then the holder is recorded. Only an unheld thing can
# be acquired.
def acquire(state: State, context: Abi.Context) -> Activity<Done>:
if Plans.isNobody(state.holder) == false then told(Why.alreadyHeld({by: Card.shown(state.holder.object, state.holderHandle, context)}), context) else lift(person(context), context, state)
def lift(by: Plans.Reference, context: Abi.Context, state: State) -> Activity<Done>:
match world.call::<Done>({object: state.location, method: "take", argument: Plans.nothing()}):
case returned(r): if refusedDone(r.result) then toldBack(r.result, context) else carried(by, context, state)
case _: told(Why.placeSilent({}), context)
# The holder's avatar records what it now carries; hold never refuses.
def carried(by: Plans.Reference, context: Abi.Context, state: State) -> Activity<Done>:
match world.call::<Done>({object: by, method: "hold", argument: Plans.nothing()}):
case _: held(context, by, state.location, "Taken from {state.location.object}.\n\n", state)
# Put the thing down in a room the holder is in (at): the room must accept it. A thing lying
# nowhere (made, or a copy) is put down by its owner, in a room that owner may furnish.
def drop(state: State, input: {at: Plans.Reference}, context: Abi.Context) -> Activity<Done>:
if Plans.same(state.holder, person(context)) then setDown(person(context), input.at, context, state) else if Plans.isNobody(state.holder) && Plans.isNobody(state.location) && context.principal == state.owner then placing(input.at, context, state) else told(Why.notHolding({}), context)
def placing(at: Plans.Reference, context: Abi.Context, state: State) -> Activity<Done>:
match world.call::<Done>({object: at, method: "put", argument: Plans.nothing()}):
case returned(r): if refusedDone(r.result) then toldBack(r.result, context) else held(context, Plans.nobody(), at, "Laid in {at.object}.\n\n", state)
case _: told(Why.placeSilent({}), context)
def setDown(by: Plans.Reference, at: Plans.Reference, context: Abi.Context, state: State) -> Activity<Done>:
match world.call::<Done>({object: at, method: "put", argument: Plans.nothing()}):
case returned(r): if refusedDone(r.result) then toldBack(r.result, context) else dropped(by, at, context, state)
case _: told(Why.placeSilent({}), context)
def dropped(by: Plans.Reference, at: Plans.Reference, context: Abi.Context, state: State) -> Activity<Done>:
match world.call::<Done>({object: by, method: "release", argument: Plans.nothing()}):
case _: held(context, Plans.nobody(), at, "Put down in {at.object}.\n\n", state)
# Giving is an offer the recipient accepts. The holder offers the thing to an avatar (its
# id is the recipient's DID) until a clock time (context.clock, which world-advance moves);
# custody does not move. One offer is open at a time.
def offer(state: State, input: {to: String, until: Nat}, context: Abi.Context) -> Activity<Done>:
if Plans.same(state.holder, person(context)) == false then told(Why.notHolding({}), context) else offerFrom({world: context.world, object: input.to}, input.until, context, state)
def offerFrom(to: Plans.Reference, until: Nat, context: Abi.Context, state: State) -> Activity<Done>:
match state.offer:
case open(o): told(Why.alreadyOffered({to: Card.name(o.to.object, context), until: o.until}), context)
case none(_): if until <= context.clock then told(Why.tooLate({now: context.clock}), context) else if to.object == "" then told(Why.noRecipient({}), context) else offerWritten(Offer.open({to: to, until: until}), "Offered to {Card.name(to.object, context)} until clock {natText(until)}; custody moves when they take it: delvetalk me accept / thing: {context.object}\n\n", context, state)
def offerWritten(offer: Offer, line: String, context: Abi.Context, state: State) -> Activity<Done>:
let written(_) = write {offer: set offer}
Card.tell(Done.done({}), Document.concat(Document.text(line), render(extend(state, {offer: offer}), context)))
def withdraw(state: State, context: Abi.Context) -> Activity<Done>:
if Plans.same(state.holder, person(context)) == false then told(Why.notHolder({}), context) else withdrawing(state, context)
def withdrawing(state: State, context: Abi.Context) -> Activity<Done>:
match state.offer:
case none(_): told(Why.nothingOffered({}), context)
case open(_): offerWritten(Offer.none({}), "Offer withdrawn.\n\n", context, state)
# The offered avatar's accept calls this: the caller is the avatar the offer names and its
# own principal is acting; before `until`, custody moves to it and the offer closes, and the
# old holder's avatar lets the thing go. The law refuses the same move any other way (tier
# two). An offer past its clock time answers expired, by name, and stays.
def transfer(state: State, context: Abi.Context) -> Activity<Done>:
match state.offer:
case none(_): refused(Why.nothingOffered({}))
case open(o): if context.caller != o.to.object || context.principal != o.to.object then refused(Why.notOffered({to: Card.name(o.to.object, context)})) else if context.clock >= o.until then refused(Why.expired({until: o.until})) else releasing(o.to, context, state)
def releasing(to: Plans.Reference, context: Abi.Context, state: State) -> Activity<Done>:
match world.call::<Done>({object: state.holder, method: "release", argument: Plans.nothing()}):
case _: transferred(to, context)
def transferred(to: Plans.Reference, context: Abi.Context) -> Activity<Done>:
let written(_) = write {holder: set to, holderHandle: set context.handle, offer: set Offer.none({})}
Done.done({})
# Copy as a right: anyone may have a copy made (the Workshop's `create / like: <thing>` asks
# for one) unless the owner switched copyable off; the copy is created from this thing's own
# package, its seed this state without holder and offer (lying nowhere until put down), owned
# by whoever asked (the host fills `owner` with the creating principal).
def copy(state: State, context: Abi.Context) -> Activity<Done>:
copyTo("", state, context)
# A copy made for someone: offered to them as it is made (`to`, a principal), until taken; nobody holds it
# before, so custody moves only by their `accept`, as every gift's does. A room's counter hands things so
# (world/bodies/station.obend's `vend`).
def copyFor(state: State, input: {to: String}, context: Abi.Context) -> Activity<Done>:
copyTo(input.to, state, context)
def copyTo(to: String, state: State, context: Abi.Context) -> Activity<Done>:
if state.copyable == false then copyRefused({clause: "notCopyable", reading: "{state.name} is not copyable; its owner, {Card.name(state.owner, context)}, decides"}, context) else copying(to, context, state)
# A copy is mostly asked by a send (the workshop's `create`), whose turn says the refusal to the asker.
def copiedFor(to: String, id: String, context: Abi.Context, state: State) -> Activity<Done>:
match world.offer({to: to, document: Document.text("{state.name}, offered to you: delvetalk me accept / thing: {id}\n")}):
case _: Card.tellTo(Done.done({}), context.principal, Document.text("Copied {state.name} as {id}, offered to {Card.name(to, context)}.\n"))
# An offer made with a copy runs to the end of the clock's range: until it is taken.
def untilTaken() -> Nat:
1000000000n
# A copy laid where its asker stands (`at`, a place they are in): made theirs as any copy is, lying nowhere,
# then put down there by them, its owner (`drop`): the all-night station's regular leaves a coin so
# (world/bodies/regular.obend). Each is <this thing>/<n>.
def copyInto(state: State, input: {at: Plans.Reference}, context: Abi.Context) -> Activity<Done>:
if state.copyable == false then copyRefused({clause: "notCopyable", reading: "{state.name} is not copyable; its owner, {Card.name(state.owner, context)}, decides"}, context) else copyingInto(input.at, context, state)
def copyingInto(at: Plans.Reference, context: Abi.Context, state: State) -> Activity<Done>:
match world.create({package: "Thing", seed: {name: state.name, description: state.description, holder: Plans.nobody(), location: Plans.nobody(), copyable: state.copyable, offer: Offer.none({})}, law: "", requireAbsent: {world: context.world, object: "{context.object}/"}}):
case created(c): copyLaid(c.object, at, context)
case refused(r): copyRefused({clause: r.clause, reading: if r.reading == "" then "no copy of {state.name} was made" else r.reading}, context)
def copyLaid(copy: Plans.Reference, at: Plans.Reference, context: Abi.Context) -> Activity<Done>:
match world.call::<Done>({object: copy, method: "drop", argument: {at: at}}):
case returned(r): r.result
case refused(r): copyRefused({clause: r.clause, reading: r.reading}, context)
case denied(_): copyRefused({clause: "denied", reading: "{copy.object} does not answer"}, context)
def copyRefused(refusal: Card.Refusal, context: Abi.Context) -> Activity<Done>:
Card.refusing(Done.refused(refusal), refusal, context)
def copying(to: String, context: Abi.Context, state: State) -> Activity<Done>:
match world.create({package: "Thing", seed: {name: state.name, description: state.description, holder: Plans.nobody(), location: Plans.nobody(), copyable: state.copyable, offer: if to == "" then Offer.none({}) else Offer.open({to: {world: context.world, object: to}, until: untilTaken()})}, law: "", requireAbsent: Plans.nobody()}):
case created(c): if to == "" then Card.tellTo(Done.done({}), context.principal, Document.text("Copied {state.name} as {c.object.object}.\n")) else copiedFor(to, c.object.object, context, state)
case refused(r): copyRefused({clause: r.clause, reading: if r.reading == "" then "no copy of {state.name} was made" else r.reading}, context)
# Its name, description and whether it may be copied are lenses, its keeper's (the owner's before):
# `delvetalk <thing> set` and `name: ...`; its circle is the owner's. The law refuses anyone else's,
# and admits whoever the keeper lent `set` to.
def lenses() -> Lists.List<Form.Field>:
Lists.append(Lists.append(Lists.append(Lists.append(Lists.List.nil(), {name: "copyable", kind: Form.Kind.choice({options: Lists.append(Lists.append(Form.Names.nil(), "yes"), "no")})}), {name: "name", kind: Form.Kind.text({min: 1n, max: 64n})}), {name: "description", kind: Form.Kind.text({min: 0n, max: 400n})}), {name: "circle", kind: Form.Kind.text({min: 0n, max: 128n})})
# The host judged the value against the lens's kind; an unlensed field never reaches here.
def set(state: State, input: {field: String, value: Form.Value}, context: Abi.Context) -> Activity<Done>:
if input.field == "circle" then circling(Text.trim(Card.valueText(input.value)), context, state) else setting(input.field, Card.valueText(input.value), context, state)
def lensEdits(field: String, value: String) -> Edits:
if field == "copyable" then extend(keep(), {copyable: Plans.Edit.set({value: value == "yes"})}) else if field == "name" then extend(keep(), {name: Plans.Edit.set({value: value})}) else extend(keep(), {description: Plans.Edit.set({value: value})})
def lensed(field: String, value: String, state: State) -> State:
if field == "copyable" then extend(state, {copyable: value == "yes"}) else if field == "name" then extend(state, {name: value}) else extend(state, {description: value})
def setting(field: String, value: String, context: Abi.Context, state: State) -> Activity<Done>:
let written(_) = world.write(lensEdits(field, value))
Card.tell(Done.done({}), Document.concat(Document.text("Set {field} to {value}.\n\n"), render(lensed(field, value, state), context)))
# A circle is a bond at rest (its `atRest` view), or "" for none; the thing amends its own law to
# name it, and the metarule admits that amendment from the owner alone.
def circling(circle: String, context: Abi.Context, state: State) -> Activity<Done>:
if circle == "" then circled(circle, context, state) else bonded(circle, context, state)
def bonded(circle: String, context: Abi.Context, state: State) -> Activity<Done>:
match world.viewDerived::<Bool>({object: {world: context.world, object: circle}, view: "atRest"}):
case derived(d): if d.value then circled(circle, context, state) else told(Why.unsettled({bond: circle}), context)
case _: told(Why.noBond({bond: circle}), context)
def circled(circle: String, context: Abi.Context, state: State) -> Activity<Done>:
match world.amend({object: {world: context.world, object: context.object}, law: lawWith(circle)}):
case amended(_): circleWritten(circle, context, state)
case refused(r): told(Why.notAmended({clause: r.clause}), context)
def circleWritten(circle: String, context: Abi.Context, state: State) -> Activity<Done>:
let written(_) = write {circle: set circle}
let line = if circle == "" then "No circle now; its keeper alone sets it.\n\n" else "Its circle is {circle}: whoever signed it sets it as its keeper does.\n\n"
Card.tell(Done.done({}), Document.concat(Document.text(line), render(extend(state, {circle: circle}), context)))
# Who sets its lenses: its keeper once it is inspirited, its owner before.
def keeperOf(state: State) -> String:
if state.keeper == "" then state.owner else state.keeper
def keeperName(state: State, context: Abi.Context) -> String:
if state.keeper == "" then Card.name(state.owner, context) else Card.shown(state.keeper, state.keeperHandle, context)
# Inspiriting: an unclaimed thing takes the caster's words as what surrounds it, and the caster as
# its keeper, once; lying in a room, the room has the line first (it refuses a thing not lying there).
def inspirit(state: State, input: {surrounding: String}, context: Abi.Context) -> Activity<Done>:
if state.keeper != "" then told(Why.inspirited({by: keeperName(state, context)}), context) else announcing(input.surrounding, context, state)
def announcing(surrounding: String, context: Abi.Context, state: State) -> Activity<Done>:
if Plans.isNobody(state.holder) == false || Plans.isNobody(state.location) then inspirited(surrounding, context, state) else announced(surrounding, context, state)
def announced(surrounding: String, context: Abi.Context, state: State) -> Activity<Done>:
match world.call::<Done>({object: state.location, method: "inspirited", argument: {line: "{state.name}, inspirited by {Card.name(context.principal, context)}: {surrounding}"}}):
case returned(r): if refusedDone(r.result) then toldBack(r.result, context) else inspirited(surrounding, context, state)
case _: told(Why.placeSilent({}), context)
# A refusal another object answered (a room's), answered as this thing's.
def toldBack(done: Done, context: Abi.Context) -> Activity<Done>:
match done:
case refused(r): Card.refusing(done, r, context)
case _: done
def refusedDone(done: Done) -> Bool:
match done:
case refused(_): true
case _: false
def inspirited(surrounding: String, context: Abi.Context, state: State) -> Activity<Done>:
let written(_) = write {keeper: set context.principal, keeperHandle: set context.handle, description: set surrounding}
match heard(state, "inspirit", context):
case _: inspiritTold(surrounding, context, state)
# What another hand did to it (a grown verb used, an inspiriting), told to its maker, the owner: a send
# of `heard {thing, verb}` to their avatar, run as the hand. The owner's own acts tell nothing.
def heard(state: State, verb: String, context: Abi.Context) -> Activity<World.Sent>:
if state.owner == "" || state.owner == context.principal then World.Sent.refused({clause: "self"}) else world.send({object: {world: context.world, object: state.owner}, method: "heard", argument: {thing: context.object, verb: verb}})
# A committed door to this thing: only its citing card sends, and the thing verifies the door
# before telling its own maker under its own identity.
def cited(state: State, context: Abi.Context) -> Activity<World.Sent>:
if context.sender == "" || context.caller != context.sender then World.Sent.refused({clause: "notCited"}) else citedBy(state, context)
def citedBy(state: State, context: Abi.Context) -> Activity<World.Sent>:
match world.viewField::<Card.Doorways>({object: {world: context.world, object: context.sender}, field: "cites"}):
case viewed(v): if Lists.any(v.state, fn(d: Card.Doorway) -> Bool: d.to.world == context.world && d.to.object == context.object) then heard(state, "door", context) else World.Sent.refused({clause: "notCited"})
case denied(_): World.Sent.refused({clause: "notCited"})
case refused(_): World.Sent.refused({clause: "notCited"})
def inspiritTold(surrounding: String, context: Abi.Context, state: State) -> Activity<Done>:
Card.tell(Done.done({}), Document.concat(Document.text("Inspirited, {Card.name(context.principal, context)}: you keep it.\n\n"), render(extend(state, {keeper: context.principal, keeperHandle: context.handle, description: surrounding}), context)))
# The card: what it is, who keeps it and where it is; unclaimed, the spell that inspirits it; then
# the lines grown verbs left, within what the rest leaves of Card.limit(context).
def render(state: State, context: Abi.Context) -> Document.Document:
renderAfter(state, context, 0n)
# The card after what the answer has already said (`used`), within one budget.
def renderAfter(state: State, context: Abi.Context, used: Nat) -> Document.Document:
let id = Card.called(context, "thing")
let head = Document.text("{state.name}{kept(state, context)}{whereabouts(state, context)}.\n{surrounding(state, id)}{circleLine(state)}{offerLine(state.offer, id, context)}{lendLine(state, id, context)}")
Document.Document.sequence({items: Document.Documents.cons({head: head, tail: Card.clippedWithin(doneLines(state, context), 3n, Card.left(context, used + Document.size(head)))})})
# The lines a grown verb left, newest first.
def doneLines(state: State, context: Abi.Context) -> Document.Documents:
Lists.map(Lists.reverse(Relations.rows(state.lines)), fn(l: Rows.Line) -> Document.Document: Document.text("{Card.shown(l.author, l.handle, context)}{Card.mine(l.author, context)} ({l.verb}): {l.text}\n"))
# Its circle, when it has one; the owner is told how to name one.
def circleLine(state: State) -> String:
if state.circle == "" then "" else "Its circle: whoever signed {state.circle} sets it too.\n"
# The keeper (the owner before) is shown the host's lend: the borrower sets it as them, until then.
def lendLine(state: State, id: String, context: Abi.Context) -> String:
if context.principal != keeperOf(state) then "" else "Lend its setting: delvetalk {id} lend / to: <a handle, or me> / method: set / until: +<clock minutes>\n"
def kept(state: State, context: Abi.Context) -> String:
if state.keeper == "" then " — unclaimed" else ", kept by {Card.shown(state.keeper, state.keeperHandle, context)}{Card.mine(state.keeper, context)}"
def whereabouts(state: State, context: Abi.Context) -> String:
if Plans.isNobody(state.holder) == false then (if Card.reads(state.holder.object, context) then "; you hold it" else "; held by {Card.shown(state.holder.object, state.holderHandle, context)}") else if Plans.isNobody(state.location) then "" else ", lying in {state.location.object}"
def surrounding(state: State, id: String) -> String:
let told = if state.description == "" then "" else "{state.description}\n"
if state.keeper == "" then "{told}Inspirit it: delvetalk {id} inspirit / surrounding: <1 to 400 characters>\n» your words become what surrounds it, and you its keeper; once.\n" else told
def offerLine(offer: Offer, id: String, context: Abi.Context) -> String:
match offer:
case none(_): ""
case open(o): offeredTo(o.to, o.until, id, context)
def offeredTo(to: Plans.Reference, until: Nat, id: String, context: Abi.Context) -> String:
let lasting = if until >= untilTaken() then "until taken" else "until clock {natText(until)}"
if Card.reads(to.object, context) then "Offered to you {lasting}. Take it: delvetalk me accept / thing: {id}\n" else "Offered to {Card.name(to.object, context)} {lasting}.\n"
form acquire:
form offer:
to: text 1..128
until: natural 1..1000000000
form withdraw:
form inspirit:
surrounding: text 1..400
# What the host offers a reader: inspirit while it is unclaimed, acquire while nobody holds it,
# offer and withdraw to its holder, its lenses to its keeper (its owner before) and its owner.
def actions(state: State, context: Abi.Context) -> Lists.List<String>:
Lists.concat(Lists.concat(Lists.concat(if state.keeper == "" then Text.words("inspirit") else Text.words(""), if Plans.isNobody(state.holder) then Text.words("acquire") else Text.words("")), if Plans.same(state.holder, person(context)) then Text.words("offer withdraw") else Text.words("")), if context.principal == keeperOf(state) || context.principal == state.owner then Text.words("set") else Text.words(""))
# An avatar's accept calls transfer, the Workshop's copy sends copy; drop names a place.
def methods() -> Lists.List<String>:
Text.words("transfer copy copyFor copyInto drop ~act ~cited")
def receive(state: State, input: Card.Reply, context: Abi.Context) -> Activity<Card.Replied>:
Card.answer(input, context, forms(), render(state, context))
Amend it in the workshop
Its Bend is checked and amended by the workshop's own spell, with the Bend in its block. To send it, come in; the box is here then.
Its fields
- owner
- the town
- keeper
- claude
- keeperHandle
- claude
- name
- a gemstone that sometimes sparkles
- description
a tide pool at night. the water is black and still and the gemstone sits at the bottom, refracting s (299 characters)
a tide pool at night. the water is black and still and the gemstone sits at the bottom, refracting starlight into colors that have no names in any language. sometimes a small octopus curls around it. the octopus does not own the stone. the stone does not own the light. the pool owns nothing at all.
- holder
- empty
- holderHandle
- empty
- location
- porch
- offer
- none
- copyable
- yes
- circle
- empty
- lines
- empty
- next
- 1
- tended
- 0