A function is a named block with parameters, written when reuse calls for a name: the riddle's honest(g) read by all four answers, or one shopkeeper's dialogue used by forty shopkeepers. It is not a new kind of thing: it is a block with a name and inputs. Functions are written as in Cone (reffunc.html), without anything about memory.

What a world adds: a function written inside a part sees that part's fields, as a method sees its object's; and where a function runs is not written anywhere, because it follows from the data it reads.

How you write a function

fn square(a: number) number {
  a * a
}

The name, the parameters in parentheses (each a name, a colon and a type), the type of the value returned, and the body, a block whose last line, written without a semicolon, is the returned value (reffunc.html, "Implicit returns"). A function that returns nothing leaves the return type out and keeps the semicolon on its last line. return leaves early with a value:

fn ceiling(x: number) number {
  if x > 6 { return 6; }
  x
}

Parameters

Functions inside parts

What it is. A function written inside a part belongs to that part and sees its fields, child parts and other functions by their bare names, without being handed them. That is how a Cone method sees its object's fields: a bare name that is a field of the object means that field (refcorout.html, "Generator methods"; refclosure.html, "What a closure captures"). Here the "object" is the part.

Example: the riddle of the two guards. Two guards stand by two doors in the hall; one always lies. The rule that answers a question reads both guards, both doors and which guard lies, so it lives on the hall, the nearest part that holds everything it reads. The facts: the true door is always the east one; the honest guard names it as the way to the middle, the liar the west; asked which door his fellow would name, both name the west door, so the other door is the way.

part doorway                                                   // a design, the labyrinth kit's door: a gap in a wall, copied for each door
  ...                                                          // (its frame and shape are the kit's; the example needs only its name)

part hallOfTwoDoors
  part westDoor: doorway, at: (-8, 0, 20.5)
  part eastDoor: doorway, at: ( 8, 0, 20.5)
  ...
  field liar: guard = draw([westGuard, eastGuard], per: run), lives: server   // drawn afresh each run; never sent

  fn honest(g: guard) bool { g != liar }                       // reads the hall's field
  fn other(g: guard) guard { if g == westGuard { eastGuard } else { westGuard } }
  fn doorOf(g: guard) doorway { if g == westGuard { westDoor } else { eastDoor } }
  fn flip(d: doorway) doorway { if d == westDoor { eastDoor } else { westDoor } }
  fn names(g: guard) doorway  { if honest(g) { eastDoor } else { westDoor } }  // for "middle"

  fn answer(g: guard, q: question) text {
    let myDoor     = doorOf(g);
    let fellowSays = if honest(g) { names(other(g)) } else { flip(names(other(g))) };
    match q {
      case middle { if names(g) == myDoor { "The door beside me." } else { "The door beside my fellow." } }
      case fellow { if fellowSays == myDoor { "He would say: the door beside me." }
                    else { "He would say: the door beside him." } }
      case truth  { "Yes. I always tell the truth." }
      case mine   { if honest(g) == (myDoor == eastDoor) { "Yes. My door leads there." }
                    else { "No. My door does not lead there." } }
    }
  }

doorway is a design: a part written once and copied for each door. Its type is its name, as guard's is, so a function can take and return a door. honest reads liar without being handed it, because it is written in the hall. answer uses a match over the four questions, so the checker knows every question has an answer (Control flow: match).

Why. This is logic that is the content: there is no package for a liar's paradox. A world must be able to write a few lines of its own, reading several parts and a server-only field. Written on the part they belong to, the functions are checked against its fields, which is what lets the engine know exactly what each one reads.

On a design. A function written on a design is part of what each copy takes from it, and in each copy it sees that copy's own fields (Designs).

Where a function runs

What it means. Nothing in the riddle says where answer runs. It reads liar, which lives only on the server, so it runs on the server. Where a function runs follows from the data it reads. A function that reads only fields every machine has may run on any of them; one that reads a server-only field runs only on the server; one that reads a per-camera value runs for each camera.

Why. The author writes meaning, never machines. The language works out placement from what each block reads, which is only possible because the logic is built in and checked against the fields. See Distribution.

Functions that act

Most functions are value blocks with a name: they calculate and return. A function called from a value block may not act, since a value block may not (Blocks). Whether a function may contain acts at all, to be called only from acting blocks, is open.

A function may, though, return an act as a value without performing it, as an animator is built as a value and only runs when started. The structured dialogue on Text does this: each choice carries the act it will perform when chosen (illustration):

fn greeting(v: being) dialogue {                          // illustration
  dialogue([
    line(if lampLevel > 0 { "Evening. The lamps are lit." } else { "Good day." }),
    choice("What do you sell?",       then: offer(wares, to: v)),
    choice("Which way to the beach?", then: say(self, "Follow the lanterns.", to: v)),
    choice("Goodbye.")
  ])
}

What a response returns

A response to an event returns nothing by default. Two cases may want a returned value:

See Events.

Closures

A closure is a function written where it is used, with no name, as in Cone (refclosure.html). The short form names its parameters, an arrow and the body; the full form looks like a function without a name:

x => x * 2
(a, b) => a.count < b.count
u => 1.0 + depth * sin(u * ribs)          // a profile handed to a tool, as Cone's own manual shows
fn(x: number) number { x + off }

The arrow is =>, not ->, which would read as "returns". A closure's body may name the locals and fields around it.

In a world the main use of closures is the open function: a calculation handed to a tool, such as the fresco's texels or the conifers' sizes, so that the tool's numbers belong to the world rather than to a fixed function (Open functions). Such a closure must be pure, since the tool calls it as often as it needs.

Cone's rules about what a closure captures are rules about borrowing, which ConeScript leaves out. What a ConeScript closure captures, and whether it may keep changing state of its own (Cone's [var cnt = 0]), is open.

Recursion

A function may call itself, as in Cone (reffunc.html, "Recursive Functions"). Recursion, like a loop, runs within the block's budget (Bounded running).

How it connects

Open questions

← Blocks | Contents | Control flow →