Control flow decides which statements of a block run, and how many times: if chooses by conditions, match chooses by the shape of a value, while and each repeat. These are Cone's statements (refflow.html), unchanged. They work the same in value blocks and acting blocks; in an acting block, the branches may hold acts.

What a world adds is a limit: bounded running. Every block runs within a budget, and long work yields and resumes, so that one world's runaway loop cannot stall a server shared by everyone in it.

One distinction to keep clear from the start. The loops on this page run inside a block, to completion, within one moment of the world. "While the visitor is in the water, do this" is a different thing, a condition over the world's time; it is written with events (an entry and an exit), not with a loop (below).

if, else if, else

if x < 0 {
  -x
} else if x == 0 {
  0
} else {
  x
}

The first condition that is true chooses its block; else takes the rest and may be left out (refif.html). A condition must be a bool. As an expression, an if gives the value of the branch taken, which is that branch's last line written without a semicolon, and then needs its else:

let target = if near && lampLevel > 0 { 1.45 } else { 1 };

In an acting block, an if guards acts. Exactly one branch runs, even when an act in the first changes what a later condition would test. Separate guards, one per case, would be an order-sensitive chain that could fire two branches; else if and else remove that:

on(action(ask, q, to: g, by: v), {
  if !within(v, g, 4 m) {
    // too far: the question is not heard
  } else if asked[v] {
    say(g, "You have had your question.", to: v);
  } else {
    set(asked[v], true);
    say(g, answer(g, q), to: v);
  }
})

match

match compares one value against patterns in order, and runs the block of the first that fits (refmatch.html). As an expression its value is that block's value: the block's last line, written without a semicolon.

match q {
  case middle { ... }
  case fellow { ... }
  case truth  { ... }
  case mine   { ... }       // every question named: no else needed
}

The patterns are Cone's:

PatternMatchesExample
a valuean equal valuecase 0, case "box"
a comparisonvalues the comparison is true ofcase <0, case >=10
a rangevalues in the rangecase 0 ..< 3, case 'a' ..= 'z'
several, with |any of themcase 2 | 3 | 5
an enum casethat case; a binding gives what it holdscase middle, case let Some(h)
a guardthe pattern, if the condition also holdscase let Some(x) if x > 0
elseanything leftelse { ... }

Covering every value. A match must cover every value it can be given, or end in else. A match on an enum is covered when every case is named; on a bool, by true and false. Numbers, text and guarded cases are never counted, so a match on them ends in else. A match that misses a case is an error naming what is missing, and a case that can never run is an error too (refmatch.html, "Covering every value"). For a world this is the check that every question has an answer, every mode of a creature a behavior, every weather a look.

Several values at once: match mode, event { case calm, struck { ... } ... }, one pattern per value, _ for any (refmatch.html, "Several values").

while, break, continue

while repeats its block as long as its condition is true (refwhile.html):

fn fact(var n: number) number {
  var result = 1;
  while n > 1 {
    result *= n;
    n -= 1;
  }
  result
}

Not a trigger. The world has no while condition: "while X, do Y" is written as an entry and an exit, on(became(c), ...) and on(became(!c), ...), or as a subscription when Y is a value, as a lantern's nearness gain is. The engine watches conditions in the tick, only when their inputs change, and acts fire only when a condition changes, never every tick. A while that expands to that pair may be added once it is clear how far the pair goes; swimming while deep enough, or a control usable while inside a circle of influence, are the cases that call for it. See Events.

each

each walks a sequence: a range of numbers, a list, or anything that hands out values one at a time (refeach.html).

each i in 0 ..< 20 {            // 0 to 19
  ...
}
each i in 0 ..= 5 { let x = 10 - 2 * i; }    // x is 10, 8, 6, 4, 2, 0
each row in rows if row.count > 3 { }       // a filter: other items are skipped (rows is a list of lists)
each key, value in table { }    // two variables unpack a pair

In an acting block, each may act once per item. A boss's army (illustration) spawns twenty trolls in a ring when its health falls below half:

part boss                                           // illustration
  part army                                         // empty at load; filled at run time
  on(became(health < 0.5), {
    each i in 0 ..< 20 {
      spawn(troll(stance: oneOf([guard, charge, flank], seed: i)),
            under: army, at: ring(self, 6 m, i / 20));      // army.troll[0] ... troll[19]
    }
  })

Over beings. Ranging over the beings in the world, each v in visitors, and conditions over every being in turn (any(visitors, v => ...), a became for each being) are needed by the riddle's thread and the creatures, and are open.

Generators

Cone has generators: a function declared yields T hands its caller values one at a time, pausing at each yield and going on from there when asked for the next; each walks one like any sequence (refcorout.html):

fn count(n: number) yields number {
  var i = 0;
  while i < n {
    yield i;
    i += 1;
  }
}
each x in count(4) { ... }      // 0 1 2 3

Generators would suit construction, a scatter that hands out places one at a time to a tool. Whether ConeScript takes them is open.

Bounded running

What it is. Every block runs within a budget. Long work yields and resumes later, so a runaway loop in one world cannot stall a server shared by everyone in it.

What it means for an author. Loops and recursion are allowed, and may be long, but they do not get the machine to themselves. The content never states the budget or how work is divided; those are resource decisions made below. A world whose logic is too heavy for any budget is doing work that belongs in a package: the ceiling on world logic is high for logic and low only for heavy computation, such as building meshes or stepping physics.

Why. A program trusts itself; a world's content comes from strangers and runs on machines shared with other people. Budgets, with purity, are what keep a shared world alive and predictable.

Exceptions

Cone has throw and results for failures (refresult.html). Whether ConeScript has them is open; see Values: results.

How it connects

Open questions

← Functions | Contents | Text and structured text →