A value is a piece of data: a number, true or false, some text, a moment, a color, a position, or a group of these. A value has no agency: it does nothing by itself. Logic computes values from other values, and acts change which value a field holds. Every value has a type, which says what values are allowed, what can be done with them, and how they are encoded. Types are metadata: data about data.
Logic and data. Logic is an executable procedure and has agency; data is passive information that logic operates on. Logic can itself be written down as data (a world's description is data until the browser runs it), but pure data cannot be executed. Data whose single use is built into the browser gets that behavior implicitly: HTML never says "draw me", yet a browser always renders it, and in the browser a hairstyle recipe always means "grow this hair on this head". Much of a world is data of this kind.
ConeScript's values are Cone's values without anything about memory: a value is never borrowed, pointed at, allocated or freed; it is simply a value. On top of Cone's numbers, booleans, text, structs, enums, options, lists and tuples, a world needs values Cone leaves to libraries: numbers with units and ranges, moments and durations that name their clock, colors, positions, references to parts, and tables keyed by being. This page takes each in turn.
Contents: numbers · units · ranges · booleans · text · moments, durations and clocks · colors · positions and vectors · curves · parts as values · enums · options · records · lists and arrays · tuples · tables keyed by being · results
Numbers
Number literals are written as in Cone (refterm.html, "Literals"):
123 3.14159 1e6 // 1000000.0 -1_000_000 // underscores are ignored 0x1000 // hexadecimal
Cone gives every number a machine type (i32, u8, f64...), chosen by a suffix (65u8) or by the place the literal sits in: a decimal with no suffix is f64 unless its place, or the other operand beside it, gives another type. In ConeScript the author states what a number means: its unit, its range and its default. How many bits it takes is the encoding's business, because content states requirements and resources belong to the implementation. A stated range is at once a rule, an editor's slider and an encoding: a number known to lie in a narrow range fits in a byte.
field height: meters = 1.8 m, within: 1.2 ..= 2.4 // a height, its default, its range field gain : number = 1, within: 1 ..= 1.45, mutable: true // a plain number, ranged field lampLevel: fraction = 0, within: 0 ..= 1, mutable: true
The number types are number (any real number), fraction (a number from 0 to 1), count (a whole number, 0 or more), and a unit name standing for a number of that unit (below).
Counts. A value that counts things (a lantern's number, a loop's index, a pin's row, the gores of a balloon, a seed) is a count. A loop over 0 ..< 10 gives counts; a count may index a list. Counts are closed under + and *: the sum or product of counts is a count. - and / on counts give a plain number, because a difference may fall below 0 and a quotient need not be whole: k - first is a number, and row / 2 is 1.5 for a row of 3 (Expressions). A count is a number, so it may be used wherever a number may: row * 0.72 m is meters. A value that is not whole or is below 0, written to a count, is an error, as a value outside a field's range is.
field gores: count = 24, common: true // a balloon's gores var row: count = 0; // a pin's row, counted in a loop
Conversions between number types are explicit in Cone (f64.from(six), refexpr.html, "Numeric conversions"); in ConeScript the unit decides what a conversion means (below).
Units
What it is. Every number that measures something carries its unit: meters, seconds, nits, degrees. A unit is part of the number's type, not a comment. Cone's own reference gives the reason for typed units: distinct number types for units of measure avoid mistakes "like the loss of the $125 million Mars probe" (refnumber.html, "Custom Number Types"). In ConeScript units are built in rather than written as custom types, because nearly every number in a world measures something.
How you write it. A unit follows its number after a space. These are the units the language provides:
| Kind | Written | Example |
|---|---|---|
| Length | m, cm, mm; as a type meters | a lantern's area is a sphere of 4 m; the cap dips 2 cm |
| Duration | s, min, inside a clock: sim(0.4 s), world(15 min) | the lamps fade over a quarter of an hour of the world's day |
| Angle | ° or deg | the looking-glass opens for a visitor facing it within about 45° |
| Share | % | the smile closes 99% of its gap in 0.35 s |
| Light | nits, candelaPerNit, lumens | a lantern's light is 0.45 candela for each nit of the bulb |
| Mass | kg; as a type mass | a pin's mass is 3 kg |
| Frequency | Hz; as a type frequency | a guard's voice is a note of 98 Hz |
| Speed | m/s; as a type speed | a hard landing at 7 m/s; a being's v.speed |
| Acceleration | m/s²; as a type acceleration | a walker gathers speed at 12 m/s² |
| Turn rate | rad/s; as a type turnRate | a figure turns at 1.05 rad/s |
| Rate | rate(amount, per: duration) (below) | a gain that moves rate(0.45, per: sim(0.4 s)) |
| Image size | texels | the balloon's paint is (768 texels, 1024 texels), 32 texels across each gore (Textures: painting an image) |
sphere(radius: 4 m, at: (0, 0.2, 0)) chase(gain, toward: 1.45, speed: rate(0.45, per: sim(0.4 s))) // a rate: an amount per duration chase(smile, toward: 0.85, settle: rate(99%, per: sim(0.35 s))) // a share of the gap per duration
A unit as a type
A field's type may be a unit name, with an optional range in the field's within: option. The range's bounds are in the type's unit; the default is written with its unit, as any quantity is.
field base : meters = 0.24 m // a length field cdPerNit: candelaPerNit = 0.45 candelaPerNit, within: 0 ..= 2 // a light's candela for each nit of its bulb field glow : nits = 0 nits // a surface's brightness (a derive writes it: not mutable) field fade : duration = world(15 min) // a length of time, on a named clock
| Type | A value of it | Written |
|---|---|---|
meters | a length | 4 m, 2 cm, 6 mm |
seconds | a length of time with no clock: the unit inside a clock's parentheses | 15 s, 10 min |
duration | a length of time on a clock (below) | sim(0.4 s), world(15 min) |
angle | a turn | 90 deg, 1.2 rad |
mass | an amount of matter | 3 kg, 25 kg |
frequency | how many times a second: a sound's pitch | 98 Hz, 118 Hz |
nits | a surface's brightness | 2800 nits |
candelaPerNit | a light's strength for each nit of a surface | 0.45 candelaPerNit |
candela, lux, lumens | a light's strength; light on a surface; a light's total output | 100000 lux |
A plain number becomes a quantity by being multiplied by one of the unit: x * 1 nits. Most quantities never need it: a literal takes its unit (2800 nits), arithmetic carries units through, and a curve gives back values in the unit of its points.
What it means. Arithmetic follows the units: meters plus meters are meters; a moment plus a duration is a moment; a moment minus a moment is a duration; an amount per duration times a duration is an amount. A unit mismatch (meters plus seconds) is refused when the world is checked. Within one kind, units convert by themselves: 2 cm may be added to 1 m. A number with no unit is a pure number (a share, a count, a gain). A length compares only with a length. A bare number is not a length in a comparison, so a height tested against 1.433 m is written y >= 1.433 m, never y >= 1.433; the same holds for every unit.
Units multiply and divide as in algebra. Meters times meters are square meters, m²; the square root of square meters is meters; a length divided by a length is a plain number, and so is a duration divided by a duration of the same clock ((now(sim) - began) / sim(1 s) is the seconds since began, to feed sin or smoothstep); nits times candela per nit are candela. So the distance of a point from the y axis is worked out on its lengths as they are, sqrt(p.x * p.x + p.z * p.z), and comes out in meters, and u / 2.5 cm is a plain number of 2.5 cm cells. A radian is a plain number, so a length times a spin in radians a second is a speed: 0.3 m * p.spin is meters a second (Physics: reading a body). Motion follows the same algebra: a direction times a speed is a velocity (three numbers in meters a second); a velocity times a duration is a length, an offset in meters, that may be added to a place; and a speed times a duration is a length. So wind.at(1800 m) * (now(sim) - start) is an offset in meters, and place + velocity * sim(2 s) is where something moving at velocity has got to in two seconds. Which maths functions keep a unit and which need a plain number is listed with each on Expressions: maths functions.
Plain numbers in shapes. Where a shape tool wants a length, a plain number is read as meters, and this holds for each end of a range and each coordinate of a point, so a point may mix the two: (0.018, base + 4 mm) is a point 18 mm out and 4 mm above base. Adding a plain number to a length is still refused (base + 0.004); write the unit (base + 4 mm). Inside a part given scale: k, "meters" means the part's own units, before the scale; inside a part given unscaled: true, they are true meters again (Frames: a turn and a scale).
Why. Units serve three readers at once: the checker (a mismatch is caught before the world runs), the authoring tools (a slider labeled in meters), and the encoding (which can choose a precision suited to the quantity).
Ranges
A range is written with Cone's range operators: a ..= b includes b, a ..< b stops before it (refeach.html, "Number Range Iterator"; refmatch.html, "Range pattern"). In a field's within: option it states the values the field may hold:
field size: number = 1, within: 0.5 ..= 1 // a copy may be 0.5 to 1 of the design's size field gain: number = 1, within: 1 ..= 1.45, mutable: true field hurts: number, within: 10 m/s ..= 24 m/s // a range with units
Arithmetic on a range works on both its ends: a range times a number scales both ends (-1 * (0.052 ..= 0.062) is the extent from -0.062 to -0.052, as a shape tool reads it), and a range plus or minus a number shifts both (u + (-2 mm ..= 2 mm) is 4 mm centered on u). The number and the range's ends must be in one kind of unit.
The same operators make a loop's range (each i in 0 ..< 20, Control flow) and a range pattern (case 0 ..< 3). What happens when a value written to a field falls outside its range (refused, clamped, or reported) is open.
Booleans
A boolean is true or false, of type bool, as in Cone. Conditions combine with &&, || and !, which stop as soon as the answer is known (refif.html, "Boolean Operators").
field near: bool = false, mutable: true derive(riding, now(sim) - launched < sim(170 s)) // a bool computed from the clock
Text
Text is written in double quotes, "You have had your question.". ConeScript has one text type, text, where Cone distinguishes an owned String from a borrowed &str; the distinction is about memory, which ConeScript leaves out. Text, formatting and structured dialogue have a page of their own: Text and structured text.
Moments, durations and clocks
What it is. Time is in the language. A moment is a point in time, such as when the balloon was launched. A duration is a length of time, such as 170 seconds. Every duration names the clock it counts, because a world has more than one kind of time:
| Clock | Written | What it counts | Used for |
|---|---|---|---|
| Simulation time | sim(0.4 s); the default when no clock is named | the world's tick, which can pause or dilate under load; also called the scene's clock | nearly everything: motions, effects, a lantern brightening as someone comes near |
| World time | world(15 min) | the world's day and calendar | things tied to the sky, such as the lamps fading up at dusk |
| Real time | real(0.2 s) | the person's time | only things about the person rather than the world: interface timing, comfort |
What a visitor's machine reads. now(sim) on a visitor's machine is the server's simulation clock as it was last told, stepped forward locally between the times it is told. So a formula of now(sim) and a stored moment gives each machine the same answer to within the little it was last told behind, and a procedural pose is such a formula (Actions).
Why each duration names its clock. A world that changes its day length keeps its evening fades right, because they count world time; and under dilation everything in simulation time slows together, because it all counts the same clock. A duration with no clock would leave which of these happens to chance.
The day length: how fast world time runs
World time is driven by simulation time. A world's day length, one of its settings (a fixed field of the world, Fields), says how much simulation time one world day takes, and so how fast the world clock runs against the scene's: pause the simulation and the day stops too. The lanterns' world (The lanterns) has a day of 24 minutes, which makes its world clock run 60 times as fast as the scene's: an hour of the day is one minute of the scene, and a world minute one scene second. So world(15 min), its lamps' fade at dusk, lasts 15 seconds of simulation time; a world with a two-hour day would stretch the same fade to 75 seconds, still a quarter of an hour of its evening. A world also says what its day clock reads when simulation time starts: 7:00 in the lanterns' world, its setting startHour.
field dayLength: duration = sim(24 min) // the world's setting: one day of world time field startHour: number = 7, within: 0 ..< 24 // the world's setting: the day clock's hour at simulation time 0
How you write it.
field launched: moment = never, mutable: true, lives: server // a moment, not yet set
derive(riding, now(sim) - launched < sim(170 s)) // a moment minus a moment is a duration
derive(materials.lanternBulb.glow,
lampLevel * bulbBrightness.at(eveningHour(hour(now(world))))) // the hour, read off world time
on(dusk, start(transition(lampLevel, to: 1, over: world(15 min), begin: dusk.due)))
now(sim),now(world): the current moment on a clock. A value defined fromnow(...)is a value over time with no animator.hour(m): the hour of the day a world-time moment falls in, anumberof hours from 0 up to 24 with its fraction: 17.475 is 17:28:30. It is a plain number, not a duration, so it may be compared with17.5and handed to a curve whose points are hours.dusk.due: the moment an event was due, which may be in the past. An animator maybeginat a past moment, so a machine that reads the event late agrees with one that read it on time, and a clock set forward past dusk finds the lamps already lit.launched + ride.length(): a moment plus a duration is a moment.start(sim): the clock's zero, the moment the world began (a ground rhythm counted from it is on from the first moment).pulse,untiland thespanthey make are on Blocks.never: a moment that has not happened (the balloon before its first ride). Whether this is a value of its own or an empty option is open.
What it means. Storing a moment and computing from it is how a world keeps time-dependent values cheap to share: a value that is a formula of a start moment costs one stored moment, and any machine can work it out for itself. A loop that adds a little each frame cannot be shared that way. A clock may be paused or run fast; whether it may run backward is open.
Rates
What it is. A rate is an amount per duration on a clock: how much a value changes, or how much of a gap it closes, in a stated length of time. It is made by rate(amount, per: duration) and has the type rate. Rates are what a chase moves at, what share of its gap a chase settles in a given time, and how fast a motion damps.
rate(0.02, per: sim(1 s)) // 0.02 each simulated second rate(nearGain - 1, per: sim(0.4 s)) // a gain's rise, for a chase's speed rate(99%, per: sim(0.35 s)) // 99% of the gap in 0.35 s, for a chase's settle rate(0.05 m/s, per: sim(1 s)) // a speed that itself changes by 0.05 m/s each second
The amount may carry a unit, and the duration names its clock like any other duration, so a rate counts the clock it is written on. A rate times a duration on the same clock is an amount, and its two parts are fixed when it is made.
Colors
A color is a value of type color: red, green, blue and opacity. Inside, its numbers are linear: proportional to the light, so that two lights add and a surface's color multiplies the light that falls on it. There are two ways to write one:
| Written | Numbers | Use it for |
|---|---|---|
srgb(r, g, b) | 0 to 255, as a screen or a paint program gives them (the sRGB numbers); turned into linear values | a surface's color picked by eye: a paint, a wood, the bronze of a lantern, srgb(88, 62, 40) |
srgbFraction(r, g, b) | the same sRGB numbers as fractions, 0 to 1 (srgb(204, 133, 66) is about srgbFraction(0.8, 0.52, 0.26)); turned into linear values | a paint table kept in fractions, as the balloon's is, so a color worked out from one is exact |
color(r, g, b) | linear, 0 to 1 (a light's, a glow's or a tint's may go above 1: the weather tints the sky's fill light by up to 1.06 in blue) | a light's or a glow's color worked out from physics: the bulb's 2700 K, color(1, 0.397, 0.0953) |
Either may take a fourth number, the opacity, 0 to 1 (1 if left out). The vocabulary names two colors, white and black; any other name (a guard's blue) is a fixed field the world declares, and a world's own white hides the vocabulary's (Parts: how a name is found).
field kiltBlue : color = srgb(46, 82, 168) // a fixed field: a named color (the west guard's cloth) field kilt : color // the guard design's part westGuard : guard(kilt: kiltBlue, height: 1.82 m) let amber = color(1, 0.3, 0.045); // linear: the lantern glass's glow
srgb() keeps the true sRGB curve. The island's creature code reads its sRGB numbers with a plain gamma of 2.2 instead, a small difference that no one can see in a picture.
Color arithmetic works on the linear numbers, one channel at a time:
a + badds two colors, as two lights falling on one place add;a * bmultiplies them channel by channel, as a light's color times a surface's color gives what is seen.c * k, a color times a plain number, scales it:roomColor * 0.4is the same hue at 0.4 of the strength. The opacity is kept.mix(a, b, t, in: linear | srgb)is the color the fractiontof the way fromatob, worked out on the linear numbers (in: linear, the default) or on the sRGB numbers (in: srgb), as a paint program blends.scaled(c, by: k)scales the sRGB numbers of a color by a factork, as a paint program's brightness does:scaled(c, by: 1.06)is 6% lighter on the sRGB numbers (the leaf ribs). The factor may be above 1; the result is not clamped when it is made, and its components may pass 1. A color is clamped to 0 to 1 only when it is drawn or painted into an image.c.r,c.g,c.bandc.opacityread its linear numbers.
Because the arithmetic is linear, darkening a paint to 0.75 of its sRGB numbers (as a paint program's brightness slider does) is not c * 0.75, which is 0.75 of its light. It is mix(black, c, 0.75, in: srgb), 0.75 of the way from black in sRGB, about 0.53 of its light. A color is an input to light, not an absolute fact: a shirt shows green only under light that contains green.
Positions and vectors
What it is. A position is three lengths, (x, y, z), stated in the frame of the part's parent, so moving the parent moves everything under it. A vector is the same three numbers used as an offset or a direction. Angles say how a part is turned.
part westDoor: doorway, at: (-8, 0, 20.5) // in the hall's frame set(cap.offset, (0, -2 cm, 0)) // an offset with units sphere(radius: 4 m, at: (0, 0.2, 0)) // a point 0.2 m up the post
yis up and north is-z. Lengths are meters unless a unit says otherwise.- A position is written as a tuple of three numbers; its components are reached as
p.x,p.y,p.z. - A position is always three numbers. There are no two-number spots on the ground: a place on the land is a point like any other, and a question about the ground there reads only its
xandz, asland.height(p)does (Terrain). Two numbers(x, y)are the points of a flat outline (Sculpting), never a place in the world. - A position is always relative to some frame. Placing, frames and their kinds (placed, around the camera, by direction) are on Frames.
The types. Several names are used for three numbers, and for now they are one type under several names, each saying what the value is for (an offset, a place's difference from another, is the fourth):
| Type | What it is | Example |
|---|---|---|
place | a position in the frame of the part's parent, three lengths; what the at: option takes, and what a field holds that says where something goes | field hangsAt: place = (0, 0, 0); at: hangsAt + (0, 0.335 m * size, 0) |
point | a position inside a shape, in the shape's own frame | bar(from: (-0.046, 0.3, 0.059), ...) |
direction | three numbers used for which way, not how far: their length does not matter | a spot light's aim: (0, -0.5, 1); up |
offset | a place difference: three lengths, how far and which way one place is from another; a - b is the offset from b to a, and a place plus an offset is a place | field link: offset = mouth.worldPlace - pane.worldPlace; (0, -2 cm, 0) |
A plain number in a place is meters, as in a shape (above). Whether the three should become separate types, so that a direction cannot be given where a place is wanted, is open.
Arithmetic on places and directions
Three numbers are added, subtracted and scaled as a whole, coordinate by coordinate, and the units follow:
| Written | Gives | Example |
|---|---|---|
p + v, p - v | a place moved by an offset | hangsAt + (0, 0.335 m * size, 0) |
a - b | the offset from place b to place a | aim - v.feet |
u + v, u - v, -v | offsets added, taken away, reversed | |
d * k, k * d, d / k | each number times (or divided by) k: a direction times a length is an offset in meters | lane * 11 m, 11 m along the lane |
t * d | a direction or offset turned by the rotation t (below) | rotation(-90 deg, about: up) * lane, the lane's right |
dot(a, b) | a number: the sum of the products of the coordinates; 0 when the two are at right angles | |
cross(a, b) | the three numbers at right angles to both, by the right-hand rule: cross(right, up) is front | |
normalize(v) | a direction of length 1 the same way as v; refused for (0, 0, 0) | |
length(v), distance(a, b) | the length of an offset; the length between two places | both in meters |
The named directions are up, down, right, left, front (toward the viewer, +z) and back (north, -z) (Shape tools: how to read the tables). On the ground, east is the same direction as right (+x), and likewise north is back, south is front and west is left. These functions are listed with the other maths on Expressions: maths functions.
let lane = rotation(90 deg, about: up) * back; // a quarter turn from north: the lane runs west let right = rotation(-90 deg, about: up) * lane; // the lane's right: north let spot = rack + lane * (row * 0.72 m) + right * (across * 0.72 m); // a pin's foot on the plan
Rotations and poses
How a thing is turned is a value of type rotation: a rotation, whatever its axis. A pin lying in the grass is turned some way that is no single angle about up, and that way must be read, kept, eased and set.
- Made by
rotation(angle, about: direction = up): turned by the angle about an axis through the origin, counterclockwise seen from the axis's tip, as every positive turn is. The axis,about, isupwhen left out.noRotationis no turn at all. - Made from a facing by
rotation(forward: direction, up: direction): the rotation that points the thing's own forward (the way it faces when unturned) alongforwardand leans its top as near toupas it can be. A bird flying along its velocity, banked into a turn, is turned so:rotation(forward: velocity, up: leaning).forwardandupmay be any lengths;forwardmay not be (0, 0, 0), andupmay not lie alongforward. - Combined by
*:a * bis the rotationband thena. The same*turns a direction or an offset:t * d. - Eased by
mix(a, b, t): the rotation the fractiontof the way fromatob, about one axis, at an even rate, and always the shortest way round. - A part's rotation is readable as
p.rotation, in its parent's frame: what itsrotation:option says (Frames: a turn and a scale), or where an animator or physics has turned it since. It may be set (set(p.rotation, t)) while nothing else drives it: no animator writes it and no awake body owns it (Frames: who owns a part's current place).
A pose is a place and a rotation together, a record pose(place: p, rotation: t), with the fields .place and .rotation. Every placed part has its current pose, p.pose, and its home, p.home, the pose it is brought back to (Frames: a part's home). An animator may move a pose as one value, place and rotation eased together (Animators: a pose).
let lying = p.rotation; // how a pin lies now let upright = noRotation; mix(lying, upright, 0.5) // half way to standing, the short way round
Curves
What it is. A curve is a value that gives a number for a number by blending between points the author lists: a lamp's brightness at each hour of the evening, a sound's loudness at each distance. It is a table with a rule for what lies between its rows. It is a library value, as a list is, not a world construct.
How you write it.
curve(points: [(x, y), ...], between: linear | logarithmic | smooth = linear) curve([(x, y), ...]) // points by position; linear between them c.at(x) // the curve's value at x
field bulbBrightness: curve = curve( // a bulb's radiance at each evening hour
points: [(16.975, 3600 nits), (17.475, 2800 nits), (17.725, 1600 nits), (17.975, 800 nits),
(18.475, 160 nits), (18.975, 24 nits), (19.475, 3 nits), (19.975, 1.3 nits)],
between: logarithmic)
bulbBrightness.at(18.2) // about 390 nits
- Points are pairs
(x, y), in rising order ofx. Thexvalues may carry a unit, all the same one (heights in meters), or be plain (an hour, a control point's number). Theyvalues may carry a unit, all the same one;c.at(x)gives back a value in that unit, so the curve above gives nits. A table with one value for each control point of a spine is a curve whosexis the control point's number (0 for the first), read between two points byc.at(2.5)(The land). - Between two points,
linearruns straight from one value to the next;logarithmicruns straight on a log scale, so each equal step ofxmultiplies the value by the same factor (from 2800 to 1600 nits and on to 800, as light fades);smootheases in and out of each point (a smoothstep between neighbors, flat at every point). A logarithmic curve's values must all be above 0. - It holds at the ends: before the first point the value is the first point's, after the last point the last's. The curve above gives 3600 nits at any hour before 16:58 and 1.3 nits at any hour after 19:58.
- A curve is a value: it may be held in a field, passed to a function, and read inside a
derive, which follows thexit is given (Subscriptions).
Parts as values
A value may name a part of the world. World logic holds only values and ids, never a reference into an engine's memory, so a part reference is a value like any other: it can be stored in a field, compared, and passed to a function.
field liar: guard = draw([westGuard, eastGuard], per: run), lives: server // which guard lies: one of two parts
fn honest(g: guard) bool { g != liar } // compares part references
The type is the design's, guard; the draw chooses which of the two named parts the field holds. A world's state often names its own parts: which door is open, which guard lies, which seat is taken.
Enums
An enum is a closed set of named cases, written as in Cone (refenum.html). The riddle's four questions are one:
enum question {
middle, fellow, truth, mine
}
fn words(q: question) text { // the words a visitor reads
match q {
case middle { "Which door leads to the middle?" }
case fellow { "Which door would your fellow name?" }
case truth { "Do you always tell the truth?" }
case mine { "Does the door beside you lead there?" }
}
}
Because the set is closed, a match over it must name every case or end in else, and the checker says which is missing (Control flow). A case may also carry values of its own, as Cone's variants do; the forms are Cone's: a bare name, a name with a type in parentheses, or a struct.
Options
An option is a value that may be absent. Its type is written ?T; its values are Some(v) and None, as in Cone (refoption.html). It is unwrapped by a pattern before use:
field laying: Table[being, ?handle] = None // a thread being laid, or none (riddle)
if let Some(h) = laying[v] {
stop(h);
}
A value written to an option wraps itself. Giving a T where a ?T is wanted makes it Some(value), so set(p.glide, start(...)) stands for a field glide: ?handle; writing Some(...) round the handle is allowed but not needed. Going the other way always needs a pattern, as above.
The absent value is None; the absent moment never is a None of an option too, unless never proves worth keeping as a moment of its own.
Records
A record is a group of named values with no parts and no handlers: a Cone struct, of which values are made and copied. A world's weather is one: a set of numbers that blend in a straight line between two states.
struct weatherState { // fifteen numbers, each typed (Sky and weather)
cloudLow: fraction; // how much of the sky the low layer covers
cloudHigh: fraction;
density: fraction = 1; // how opaque the clouds are
cloudLight: fraction = 1; // how much light the clouds are lit with
skyGray: fraction = 0; // how far the blue sky is drained to gray
skyDim: fraction = 1; // how bright the sky is left
sun: fraction = 1; // how much direct light gets through
fog: number = 0; // thickness for each meter of sight
ambient: number = 1; // the scale of the sky's fill light
tint: color = color(1, 1, 1); // its tint: a channel may exceed 1
ev: number = 0; // stops the exposure is opened by
rain: fraction = 0;
snow: fraction = 0;
}
field fair: weatherState = weatherState(cloudLow: 0.42, cloudHigh: 0.5) // the rest as defaulted
field weather: weatherState = fair, mutable: true
A record differs from a part: it has no path, no frame and no place in the tree, and it is copied whenever it is passed. A record is made by its name and named values.
Defaults. A record's field may be given a default after its type, name: type = value;, as in Cone; a field with a default may be left out when the record is made, and one without must be given; required fields come first in the declaration, and a field with a default is given by name (Expressions: the argument rule). A default is a constant: a literal, a fixed field, or an expression of them.
struct pulseRhythm {
from: moment; // no default: must be given
every: duration = sim(4 s); // a default
for: duration = sim(3 s);
}
let ground = pulseRhythm(from: landed); // every 4 s, 3 s on
let climb = pulseRhythm(from: launched, every: sim(5 s), for: sim(3.2 s));
Measurements a design's shapes share are not a record: they are fixed fields of the design, which its shapes read by name (Construction: a shape is a function).
Lists and arrays
A list holds any number of values of one type, written in square brackets, and grows and shrinks; an array has a fixed size. Both are as in Cone (refarray.html; List[T] in reftypecoll.html), without the memory detail. Elements are read with xs[i], counted from 0, and walked with each. A list's length is xs.count, a count; min(xs) and max(xs) give its least and greatest element (Expressions: maths functions).
let presets = [clear, fair, cloudy, overcast, rain, snow]; // the six weathers presets.count // 6: a list's length keys(cap.offset, [key(sim(0 s), (0, -2 cm, 0)), key(sim(0.25 s), (0, 0, 0))])
Cone's append operator, xs <- v, adds to a list, and List[T]() <- a, each xs, b builds a new one from single values and whole lists: List[prop]() <- ball, each copiesOf(ninepin) is the ball and every pin (Designs: finding every copy). List[T]() <- each x in src yield f(x) builds one from a loop (refeach.html, "Building a collection"). A growing list held in a field is state like any other: the paint splats on a wall are a list of children of the wall.
Tuples
A tuple holds a few values in order, unnamed, as in Cone (reftuple.html): (x, y), and element t.0, t.1. Positions are tuples of three lengths. A function may return a tuple, and parallel assignment unpacks one: lo, hi = rangeOf(xs);.
Tables keyed by being
What it is. Some values exist once for each being in the world: whether this being has had its question, where its thread runs. A table keyed by being is a field holding one value per being, read and written by indexing with the being. A being is a body in the world that a visitor drives or the world runs (a guard, a villager). It is not a player: content never names a player.
How you write it. Table[key type, value type]; a compound key is a tuple, Table[(being, faction), number]:
part labyrinth field asked : Table[being, bool] = false, mutable: true, lives: both // one question per visitor field thread: Table[being, trail] = empty, mutable: true, lives: both // Ariadne's line, per being on(left(underground, by: v), set(asked[v], false)) // a row lasts while its being is below
Every being starts at the default. asked[v] reads the row for being v; set(asked[v], true) writes it.
What it means.
- A lifetime. A row needs a lifetime, or the rows of every visitor who ever came would pile up. Lifetimes are stated by relation to the world (while the being is in this region, until that event), not as a time. The riddle's two tables end differently:
askedwhen the being leaves the labyrinth by any way,threadonly when it crosses the exit gate. - Where it lives. Like any field it lives
client,serverorboth(itslives:option).askedlives on both because every visitor can see a guard refuse a second question (Distribution). - Who. A being is an id for a body, which content may compare, use as a key and measure distance to, never a person.
Results
Cone has Result[T, E], with Ok and Error, and throw as a way to return an error (refresult.html). Whether ConeScript keeps results and throw is open: throw also names an action in the world (hurling an object), so the word cannot mean both.
How values connect
- Fields hold values on parts, with units, ranges, defaults and a home: Fields.
- Values computed from others: Expressions, Blocks, Subscriptions.
- Values moving over time: Animators. An animator is itself a value, built and then started.
- Where a value lives and what crosses the network: Distribution.
- Seeds, and values drawn afresh each run (
draw(run)): Designs, Generators.
Open questions
- Out of range. What happens when a value written to a field falls outside its stated range.
- Moments and their clock. Whether a moment carries the clock it was read on, so that
now(world) - launchedis refused whenlaunchedis a simulation moment; and whetherneveris a moment or an empty option. - Which clock a value stepped by each visitor's machine counts: the sound's shelter chase is about the person's hearing, which argues for real time.
- Steps as time. "Four steps" of the simulation had to become
sim(0.067 s). - A clock running backward: what an animator that has ended does when the clock runs back past its end.
- Place, point, direction: one type under three names (proposal), or three types the checker tells apart; and the type of a region.
- Colors in other spaces:
srgb(0 to 255),srgbFraction(0 to 1) and linearcolorare proposed (Colors); whether a temperature (2700 K) or a wider gamut need spellings of their own. - What a being handle is. A visitor's body is not in the content's tree. The model needs a reference to "a being in the world" that can be stored, compared, used as a key and measured, and must say what happens to rows when that being goes away (a visitor who disconnects); and how to say "a being a person drives" without saying which person.
- Lifetimes of keyed values: whether a keyed field can declare its lifetime ("while below", "until the exit") instead of a handler clearing it.
- Keys other than beings: whether tables may be keyed by other things (a part, a camera).
- Results and
throw: whether world logic needs them, and the clash with thethrowaction. - An enum case used as a number. An index such as
lantern[2 * p + side], withsideone of[left, right], multiplies a case. Either cases have an order an index may use, or the index is built from the case itself (a pair of the number and the case); the example pairs each side with a number instead (The lanterns).
← What ConeScript is | Contents | Expressions and operators →