the modules · from entry 2325 · bafyreibfpdtveljl2bfwhz42ejh2j7qwcxqtm4p32yp6kx5rgp7bl4vmwm
Thing
Thing.obend 373 lines
1edition ObjectiveBend 1
2import ./Abi.obend as Abi
3import ./List.obend as Lists
4import ./Document.obend as Document
5import ./Plan.obend as Plans
6import ./Form.obend as Form
7import ./World.obend as World
8import ./Card.obend as Card
9import ./Scenes.obend as Scenes
10import ./Relation.obend as Relations
11import ./Rows.obend as Rows
12import ./Text.obend as Text
13# THING (docs/METAPHYSICS.md §7): something that lies in a room or is held, and is given by an
14# offer its taker accepts. owner: who made it and alone reprograms it. A thing may lie unclaimed
15# (`keeper` ""): anyone inspirits it once (`inspirit / surrounding:`), and their words become what
16# surrounds it (its description) and they its keeper, who alone renames or redescribes it after;
17# the room it lies in gets the line. Before that its lenses are the owner's.
18#
19# Who may change it is its law's alone, never its code's, so what the host lends (`delvetalk <thing>
20# lend / to / method: set / until`) runs: the borrower's `set` is judged with the keeper as subject.
21# A circle is a bond at rest (kind/bond): the owner names one (`set / circle:`), and the thing amends
22# its own law so the bond's signers set it as its keeper does (LAW §4 D: `request.subject in
23# "<bond>".marks.who`). Its law is the package's below; `lawWith(circle)` is that text with a
24# circle's signers in the keeper's disjunct, which naming a circle amends it to (`lawText()`, the
25# package's own, is test-checked equal to the clauses declared here).
26# lines: what was done to it by a verb a hand grew on it (`act`: a gesture, one line tagged with
27# the verb, and the thing tended), the newest 32; `next` numbers them.
28# offer: custody offered to someone, open until a clock time (a card address is a versioned
29# capability, not a durable inbox: giving is offer and accept, never a pointer move).
30sum Offer:
31 none: {}
32 open: {to: Plans.Reference, until: Nat}
33record State:
34 owner: String
35 keeper: String
36 keeperHandle: String
37 name: String
38 description: String
39 holder: Plans.Reference
40 holderHandle: String
41 location: Plans.Reference
42 offer: Offer
43 copyable: Bool
44 circle: String
45 lines: Relations.Relation<Rows.Line>
46 next: Nat
47 tended: Nat
48def initial() -> State:
49 {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}
50def relations() -> Relations.Decls:
51 Lists.append(Relations.Decls.nil(), {field: "lines", key: Text.words("author n"), limit: 32n})
52want claimed "nothing surrounds it yet": new.keeper == "" asks delvetalk {request.object} inspirit
53# A thing as edition 12 keeps it (a carry lays this package over it, `migrate`): with `tendedAt`,
54# an entry that a carry from before edition 12 left counting another chain's; tending is the clock's.
55record State12:
56 owner: String
57 keeper: String
58 keeperHandle: String
59 name: String
60 description: String
61 holder: Plans.Reference
62 holderHandle: String
63 location: Plans.Reference
64 offer: Offer
65 copyable: Bool
66 circle: String
67 lines: Relations.Relation<Rows.Line>
68 next: Nat
69 tended: Nat
70 tendedAt: Nat
71def migrate(old: State12) -> State:
72 {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}
73def lineKey(line: Rows.Line) -> {author: String, n: Nat}:
74 {author: line.author, n: line.n}
75# A grown verb used on the thing (the host runs `act` for a row of its verb table): a gesture, one
76# line tagged with the verb (its words, or the verb itself for one that takes none), and the thing tended; its card
77# shows the newest three.
78def act(state: State, input: {verb: String, text: String}, context: Abi.Context) -> Activity<Nat>:
79 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}
80 match heard(state, input.verb, context):
81 case _: actTold(state, input, context)
82def actTold(state: State, input: {verb: String, text: String}, context: Abi.Context) -> Activity<Nat>:
83 let said = Document.text("#{natText(state.next)} {input.verb}: {input.text}\n\n")
84 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))))
85# Anyone moves a thing (its own code checks who holds it); its name, description and copying are
86# its keeper's once it is inspirited (and its circle's, when it has one), its owner's before; the
87# owner alone names the circle, amends or reprograms it.
88law 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 == ""))
89law keeper "it is inspirited once, by whoever does it, and keeps that keeper": writeOnce(keeper) and (unchanged(keeper) or request.subject == new.keeper)
90law methods "only its own methods write it; a proposed write is refused": not (request.kind == proposed) and (request.kind == 0 implies not (request.method == ""))
91def lawText() -> String:
92 lawWith("")
93def lawWith(circle: String) -> String:
94 let circled = if circle == "" then "" else " or request.subject in \"{circle}\".marks.who"
95 let whose = if circle == "" then "its keeper's" else "its keeper's and {circle}'s signers'"
96 "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 == \"\"))"
97# The second tier: custody moves from one holder to another only by an accepted offer:
98# the new holder is the offer's `to`, the turn is that avatar's own call (context.caller),
99# and the clock (context.clock) is before `until`. Picking up (from nobody) and putting down (to
100# nobody) are the code's own paths and pass.
101def law(old: State, new: State, request: Abi.Request) -> Abi.Verdict:
102 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)
103def transferable(offer: Offer, holder: Plans.Reference, context: Abi.Context) -> Abi.Verdict:
104 match offer:
105 case none(_): Abi.Verdict.refused({clause: "noOffer", reading: "The thing is not offered to anyone."})
106 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({})
107type Done = Scenes.Done
108# Why a thing refuses: each arm a clause and its reading (names already shown).
109sum Why:
110 alreadyHeld: {by: String}
111 placeSilent: {}
112 notHolding: {}
113 notHolder: {}
114 alreadyOffered: {to: String, until: Nat}
115 tooLate: {now: Nat}
116 noRecipient: {}
117 nothingOffered: {}
118 notOffered: {to: String}
119 expired: {until: Nat}
120 inspirited: {by: String}
121 unsettled: {bond: String}
122 noBond: {bond: String}
123 notAmended: {clause: String}
124def refusal(w: Why) -> Card.Refusal:
125 match w:
126 case alreadyHeld(a): {clause: "alreadyHeld", reading: "Already held by {a.by}"}
127 case placeSilent(_): {clause: "placeSilent", reading: "The room did not answer."}
128 case notHolding(_): {clause: "notHolding", reading: "You are not holding it."}
129 case notHolder(_): {clause: "notHolder", reading: "Only its holder withdraws an offer."}
130 case alreadyOffered(a): {clause: "alreadyOffered", reading: "Already offered to {a.to} until {natText(a.until)}; withdraw it first."}
131 case tooLate(t): {clause: "tooLate", reading: "until must be after the clock, now {natText(t.now)}"}
132 case noRecipient(_): {clause: "noRecipient", reading: "Offer it to someone."}
133 case nothingOffered(_): {clause: "nothingOffered", reading: "Nothing is offered."}
134 case notOffered(n): {clause: "notOffered", reading: "It is offered to {n.to}"}
135 case expired(e): {clause: "expired", reading: "The offer ran until clock {natText(e.until)}"}
136 case inspirited(i): {clause: "keeper", reading: "Inspirited already, by {i.by}, who keeps it"}
137 case unsettled(u): {clause: "unsettled", reading: "{u.bond} is not at rest: a circle is a bond every party signed"}
138 case noBond(n): {clause: "noBond", reading: "{n.bond} is no bond you may read"}
139 case notAmended(n): {clause: n.clause, reading: "Its law did not take the circle: only its owner names one"}
140def refused(w: Why) -> Done:
141 Done.refused(refusal(w))
142# A refusal, answered by name (Card.refusing), and nothing written.
143def told(w: Why, context: Abi.Context) -> Activity<Done>:
144 Card.refusing(refused(w), refusal(w), context)
145# Custody recorded, and the speaker shown the line and the card as it now stands.
146def held(context: Abi.Context, holder: Plans.Reference, location: Plans.Reference, line: String, state: State) -> Activity<Done>:
147 let handle = if Plans.isNobody(holder) then "" else context.handle
148 let written(_) = write {holder: set holder, holderHandle: set handle, location: set location}
149 Card.tell(Done.done({}), Document.concat(Document.text(line), render(extend(state, {holder: holder, holderHandle: handle, location: location}), context)))
150def person(context: Abi.Context) -> Plans.Reference:
151 {world: context.world, object: context.principal}
152# Pick the thing up from the room it lies in: the room lets it go (it refuses by name a taker
153# not in it, or a thing not lying there), then the holder is recorded. Only an unheld thing can
154# be acquired.
155def acquire(state: State, context: Abi.Context) -> Activity<Done>:
156 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)
157def lift(by: Plans.Reference, context: Abi.Context, state: State) -> Activity<Done>:
158 match world.call::<Done>({object: state.location, method: "take", argument: Plans.nothing()}):
159 case returned(r): if refusedDone(r.result) then toldBack(r.result, context) else carried(by, context, state)
160 case _: told(Why.placeSilent({}), context)
161# The holder's avatar records what it now carries; hold never refuses.
162def carried(by: Plans.Reference, context: Abi.Context, state: State) -> Activity<Done>:
163 match world.call::<Done>({object: by, method: "hold", argument: Plans.nothing()}):
164 case _: held(context, by, state.location, "Taken from {state.location.object}.\n\n", state)
165# Put the thing down in a room the holder is in (at): the room must accept it. A thing lying
166# nowhere (made, or a copy) is put down by its owner, in a room that owner may furnish.
167def drop(state: State, input: {at: Plans.Reference}, context: Abi.Context) -> Activity<Done>:
168 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)
169def placing(at: Plans.Reference, context: Abi.Context, state: State) -> Activity<Done>:
170 match world.call::<Done>({object: at, method: "put", argument: Plans.nothing()}):
171 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)
172 case _: told(Why.placeSilent({}), context)
173def setDown(by: Plans.Reference, at: Plans.Reference, context: Abi.Context, state: State) -> Activity<Done>:
174 match world.call::<Done>({object: at, method: "put", argument: Plans.nothing()}):
175 case returned(r): if refusedDone(r.result) then toldBack(r.result, context) else dropped(by, at, context, state)
176 case _: told(Why.placeSilent({}), context)
177def dropped(by: Plans.Reference, at: Plans.Reference, context: Abi.Context, state: State) -> Activity<Done>:
178 match world.call::<Done>({object: by, method: "release", argument: Plans.nothing()}):
179 case _: held(context, Plans.nobody(), at, "Put down in {at.object}.\n\n", state)
180# Giving is an offer the recipient accepts. The holder offers the thing to an avatar (its
181# id is the recipient's DID) until a clock time (context.clock, which world-advance moves);
182# custody does not move. One offer is open at a time.
183def offer(state: State, input: {to: String, until: Nat}, context: Abi.Context) -> Activity<Done>:
184 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)
185def offerFrom(to: Plans.Reference, until: Nat, context: Abi.Context, state: State) -> Activity<Done>:
186 match state.offer:
187 case open(o): told(Why.alreadyOffered({to: Card.name(o.to.object, context), until: o.until}), context)
188 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)
189def offerWritten(offer: Offer, line: String, context: Abi.Context, state: State) -> Activity<Done>:
190 let written(_) = write {offer: set offer}
191 Card.tell(Done.done({}), Document.concat(Document.text(line), render(extend(state, {offer: offer}), context)))
192def withdraw(state: State, context: Abi.Context) -> Activity<Done>:
193 if Plans.same(state.holder, person(context)) == false then told(Why.notHolder({}), context) else withdrawing(state, context)
194def withdrawing(state: State, context: Abi.Context) -> Activity<Done>:
195 match state.offer:
196 case none(_): told(Why.nothingOffered({}), context)
197 case open(_): offerWritten(Offer.none({}), "Offer withdrawn.\n\n", context, state)
198# The offered avatar's accept calls this: the caller is the avatar the offer names and its
199# own principal is acting; before `until`, custody moves to it and the offer closes, and the
200# old holder's avatar lets the thing go. The law refuses the same move any other way (tier
201# two). An offer past its clock time answers expired, by name, and stays.
202def transfer(state: State, context: Abi.Context) -> Activity<Done>:
203 match state.offer:
204 case none(_): refused(Why.nothingOffered({}))
205 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)
206def releasing(to: Plans.Reference, context: Abi.Context, state: State) -> Activity<Done>:
207 match world.call::<Done>({object: state.holder, method: "release", argument: Plans.nothing()}):
208 case _: transferred(to, context)
209def transferred(to: Plans.Reference, context: Abi.Context) -> Activity<Done>:
210 let written(_) = write {holder: set to, holderHandle: set context.handle, offer: set Offer.none({})}
211 Done.done({})
212# Copy as a right: anyone may have a copy made (the Workshop's `create / like: <thing>` asks
213# for one) unless the owner switched copyable off; the copy is created from this thing's own
214# package, its seed this state without holder and offer (lying nowhere until put down), owned
215# by whoever asked (the host fills `owner` with the creating principal).
216def copy(state: State, context: Abi.Context) -> Activity<Done>:
217 copyTo("", state, context)
218# A copy made for someone: offered to them as it is made (`to`, a principal), until taken; nobody holds it
219# before, so custody moves only by their `accept`, as every gift's does. A room's counter hands things so
220# (world/bodies/station.obend's `vend`).
221def copyFor(state: State, input: {to: String}, context: Abi.Context) -> Activity<Done>:
222 copyTo(input.to, state, context)
223def copyTo(to: String, state: State, context: Abi.Context) -> Activity<Done>:
224 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)
225# A copy is mostly asked by a send (the workshop's `create`), whose turn says the refusal to the asker.
226def copiedFor(to: String, id: String, context: Abi.Context, state: State) -> Activity<Done>:
227 match world.offer({to: to, document: Document.text("{state.name}, offered to you: delvetalk me accept / thing: {id}\n")}):
228 case _: Card.tellTo(Done.done({}), context.principal, Document.text("Copied {state.name} as {id}, offered to {Card.name(to, context)}.\n"))
229# An offer made with a copy runs to the end of the clock's range: until it is taken.
230def untilTaken() -> Nat:
231 1000000000n
232# A copy laid where its asker stands (`at`, a place they are in): made theirs as any copy is, lying nowhere,
233# then put down there by them, its owner (`drop`): the all-night station's regular leaves a coin so
234# (world/bodies/regular.obend). Each is <this thing>/<n>.
235def copyInto(state: State, input: {at: Plans.Reference}, context: Abi.Context) -> Activity<Done>:
236 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)
237def copyingInto(at: Plans.Reference, context: Abi.Context, state: State) -> Activity<Done>:
238 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}/"}}):
239 case created(c): copyLaid(c.object, at, context)
240 case refused(r): copyRefused({clause: r.clause, reading: if r.reading == "" then "no copy of {state.name} was made" else r.reading}, context)
241def copyLaid(copy: Plans.Reference, at: Plans.Reference, context: Abi.Context) -> Activity<Done>:
242 match world.call::<Done>({object: copy, method: "drop", argument: {at: at}}):
243 case returned(r): r.result
244 case refused(r): copyRefused({clause: r.clause, reading: r.reading}, context)
245 case denied(_): copyRefused({clause: "denied", reading: "{copy.object} does not answer"}, context)
246def copyRefused(refusal: Card.Refusal, context: Abi.Context) -> Activity<Done>:
247 Card.refusing(Done.refused(refusal), refusal, context)
248def copying(to: String, context: Abi.Context, state: State) -> Activity<Done>:
249 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()}):
250 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)
251 case refused(r): copyRefused({clause: r.clause, reading: if r.reading == "" then "no copy of {state.name} was made" else r.reading}, context)
252# Its name, description and whether it may be copied are lenses, its keeper's (the owner's before):
253# `delvetalk <thing> set` and `name: ...`; its circle is the owner's. The law refuses anyone else's,
254# and admits whoever the keeper lent `set` to.
255def lenses() -> Lists.List<Form.Field>:
256 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})})
257# The host judged the value against the lens's kind; an unlensed field never reaches here.
258def set(state: State, input: {field: String, value: Form.Value}, context: Abi.Context) -> Activity<Done>:
259 if input.field == "circle" then circling(Text.trim(Card.valueText(input.value)), context, state) else setting(input.field, Card.valueText(input.value), context, state)
260def lensEdits(field: String, value: String) -> Edits:
261 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})})
262def lensed(field: String, value: String, state: State) -> State:
263 if field == "copyable" then extend(state, {copyable: value == "yes"}) else if field == "name" then extend(state, {name: value}) else extend(state, {description: value})
264def setting(field: String, value: String, context: Abi.Context, state: State) -> Activity<Done>:
265 let written(_) = world.write(lensEdits(field, value))
266 Card.tell(Done.done({}), Document.concat(Document.text("Set {field} to {value}.\n\n"), render(lensed(field, value, state), context)))
267# A circle is a bond at rest (its `atRest` view), or "" for none; the thing amends its own law to
268# name it, and the metarule admits that amendment from the owner alone.
269def circling(circle: String, context: Abi.Context, state: State) -> Activity<Done>:
270 if circle == "" then circled(circle, context, state) else bonded(circle, context, state)
271def bonded(circle: String, context: Abi.Context, state: State) -> Activity<Done>:
272 match world.viewDerived::<Bool>({object: {world: context.world, object: circle}, view: "atRest"}):
273 case derived(d): if d.value then circled(circle, context, state) else told(Why.unsettled({bond: circle}), context)
274 case _: told(Why.noBond({bond: circle}), context)
275def circled(circle: String, context: Abi.Context, state: State) -> Activity<Done>:
276 match world.amend({object: {world: context.world, object: context.object}, law: lawWith(circle)}):
277 case amended(_): circleWritten(circle, context, state)
278 case refused(r): told(Why.notAmended({clause: r.clause}), context)
279def circleWritten(circle: String, context: Abi.Context, state: State) -> Activity<Done>:
280 let written(_) = write {circle: set circle}
281 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"
282 Card.tell(Done.done({}), Document.concat(Document.text(line), render(extend(state, {circle: circle}), context)))
283# Who sets its lenses: its keeper once it is inspirited, its owner before.
284def keeperOf(state: State) -> String:
285 if state.keeper == "" then state.owner else state.keeper
286def keeperName(state: State, context: Abi.Context) -> String:
287 if state.keeper == "" then Card.name(state.owner, context) else Card.shown(state.keeper, state.keeperHandle, context)
288# Inspiriting: an unclaimed thing takes the caster's words as what surrounds it, and the caster as
289# its keeper, once; lying in a room, the room has the line first (it refuses a thing not lying there).
290def inspirit(state: State, input: {surrounding: String}, context: Abi.Context) -> Activity<Done>:
291 if state.keeper != "" then told(Why.inspirited({by: keeperName(state, context)}), context) else announcing(input.surrounding, context, state)
292def announcing(surrounding: String, context: Abi.Context, state: State) -> Activity<Done>:
293 if Plans.isNobody(state.holder) == false || Plans.isNobody(state.location) then inspirited(surrounding, context, state) else announced(surrounding, context, state)
294def announced(surrounding: String, context: Abi.Context, state: State) -> Activity<Done>:
295 match world.call::<Done>({object: state.location, method: "inspirited", argument: {line: "{state.name}, inspirited by {Card.name(context.principal, context)}: {surrounding}"}}):
296 case returned(r): if refusedDone(r.result) then toldBack(r.result, context) else inspirited(surrounding, context, state)
297 case _: told(Why.placeSilent({}), context)
298# A refusal another object answered (a room's), answered as this thing's.
299def toldBack(done: Done, context: Abi.Context) -> Activity<Done>:
300 match done:
301 case refused(r): Card.refusing(done, r, context)
302 case _: done
303def refusedDone(done: Done) -> Bool:
304 match done:
305 case refused(_): true
306 case _: false
307def inspirited(surrounding: String, context: Abi.Context, state: State) -> Activity<Done>:
308 let written(_) = write {keeper: set context.principal, keeperHandle: set context.handle, description: set surrounding}
309 match heard(state, "inspirit", context):
310 case _: inspiritTold(surrounding, context, state)
311# What another hand did to it (a grown verb used, an inspiriting), told to its maker, the owner: a send
312# of `heard {thing, verb}` to their avatar, run as the hand. The owner's own acts tell nothing.
313def heard(state: State, verb: String, context: Abi.Context) -> Activity<World.Sent>:
314 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}})
315# A committed door to this thing: only its citing card sends, and the thing verifies the door
316# before telling its own maker under its own identity.
317def cited(state: State, context: Abi.Context) -> Activity<World.Sent>:
318 if context.sender == "" || context.caller != context.sender then World.Sent.refused({clause: "notCited"}) else citedBy(state, context)
319def citedBy(state: State, context: Abi.Context) -> Activity<World.Sent>:
320 match world.viewField::<Card.Doorways>({object: {world: context.world, object: context.sender}, field: "cites"}):
321 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"})
322 case denied(_): World.Sent.refused({clause: "notCited"})
323 case refused(_): World.Sent.refused({clause: "notCited"})
324def inspiritTold(surrounding: String, context: Abi.Context, state: State) -> Activity<Done>:
325 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)))
326# The card: what it is, who keeps it and where it is; unclaimed, the spell that inspirits it; then
327# the lines grown verbs left, within what the rest leaves of Card.limit(context).
328def render(state: State, context: Abi.Context) -> Document.Document:
329 renderAfter(state, context, 0n)
330# The card after what the answer has already said (`used`), within one budget.
331def renderAfter(state: State, context: Abi.Context, used: Nat) -> Document.Document:
332 let id = Card.called(context, "thing")
333 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)}")
334 Document.Document.sequence({items: Document.Documents.cons({head: head, tail: Card.clippedWithin(doneLines(state, context), 3n, Card.left(context, used + Document.size(head)))})})
335# The lines a grown verb left, newest first.
336def doneLines(state: State, context: Abi.Context) -> Document.Documents:
337 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"))
338# Its circle, when it has one; the owner is told how to name one.
339def circleLine(state: State) -> String:
340 if state.circle == "" then "" else "Its circle: whoever signed {state.circle} sets it too.\n"
341# The keeper (the owner before) is shown the host's lend: the borrower sets it as them, until then.
342def lendLine(state: State, id: String, context: Abi.Context) -> String:
343 if context.principal != keeperOf(state) then "" else "Lend its setting: delvetalk {id} lend / to: <a handle, or me> / method: set / until: +<clock minutes>\n"
344def kept(state: State, context: Abi.Context) -> String:
345 if state.keeper == "" then " — unclaimed" else ", kept by {Card.shown(state.keeper, state.keeperHandle, context)}{Card.mine(state.keeper, context)}"
346def whereabouts(state: State, context: Abi.Context) -> String:
347 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}"
348def surrounding(state: State, id: String) -> String:
349 let told = if state.description == "" then "" else "{state.description}\n"
350 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
351def offerLine(offer: Offer, id: String, context: Abi.Context) -> String:
352 match offer:
353 case none(_): ""
354 case open(o): offeredTo(o.to, o.until, id, context)
355def offeredTo(to: Plans.Reference, until: Nat, id: String, context: Abi.Context) -> String:
356 let lasting = if until >= untilTaken() then "until taken" else "until clock {natText(until)}"
357 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"
358form acquire:
359form offer:
360 to: text 1..128
361 until: natural 1..1000000000
362form withdraw:
363form inspirit:
364 surrounding: text 1..400
365# What the host offers a reader: inspirit while it is unclaimed, acquire while nobody holds it,
366# offer and withdraw to its holder, its lenses to its keeper (its owner before) and its owner.
367def actions(state: State, context: Abi.Context) -> Lists.List<String>:
368 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(""))
369# An avatar's accept calls transfer, the Workshop's copy sends copy; drop names a place.
370def methods() -> Lists.List<String>:
371 Text.words("transfer copy copyFor copyInto drop ~act ~cited")
372def receive(state: State, input: Card.Reply, context: Abi.Context) -> Activity<Card.Replied>:
373 Card.answer(input, context, forms(), render(state, context))