Modules · Core · Console

Console

Write text to the console. Console gives you Console.Line, which appends a line to the console output channel — a reactive sink the console driver drains and prints on each cycle. Because the call runs inside the reactive program, a line written from a value that later changes is simply written again on the next cycle.

Contents

Functions

Functions

Console.Background

Sets the background colour for a block.

Parameters

NameTypeDescription
colourColour.RGBAThe colour to fill behind text and lines. Its alpha channel lets the existing background show through.

Holes

NameReturnsDescription
contentnothingThe text and lines to write on the chosen background.

Returns

Returns nothing — every piece of text or line written in the content is drawn on the given background colour.

Example

Console.Background Colour.RGBA.Red
  Console.Line "Build failed"
Highlights the line with a red background.

Console.Foreground

Sets the text colour for a block.

Parameters

NameTypeDescription
colourColour.RGBThe colour to draw text and lines in.

Holes

NameReturnsDescription
contentnothingThe text and lines to write in the chosen colour.

Returns

Returns nothing — every piece of text or line written in the content is drawn in the given foreground colour.

Example

Console.Foreground Colour.RGB.Green
  Console.Line "All tests passed"
Writes the line in green.

Console.Line

Writes a line of text to the console output.

Parameters

NameTypeDescription
textTextThe line to write.

Returns

Returns nothing — the line is appended to the console output channel.

Example

Console.Line "Hello, world"
Writes a greeting to the console.

Console.Prefix

Prefixes every line written in a block.

Holes

NameReturnsDescription
prefixnothingRun at the start of every new line to write the prefix. Its output — text and colours — is emitted before the line's own content.
contentnothingThe lines to write. At the start of each new line the prefix hole runs first.

Returns

Returns nothing — the content is written to the console output channel with the prefix applied to the start of each line.

Example

Console.Prefix
  Console.Prefix.prefix
    Console.Text "> "
  Console.Prefix.content
    Console.Line "first"
    Console.Line "second"
Writes > first and > second, prefixing each line.

Console.Text

Writes text without ending the line.

Parameters

NameTypeDescription
textTextThe text to write.

Returns

Returns nothing — the text is appended to the current line of the console output channel, with no trailing line break. When it is the first thing written on a new line, the active prefix is written before it.

Example

Console.Text "Loading"
Console.Text "..."
Writes Loading... on a single line, leaving the cursor at the end of it.

Filling multiple holes at the call site

The open question above is now settled. At the call site each hole is named in full — the function's name, then the hole's own — so the label cannot be mistaken for anything else. A bare sample would be tempting, but something else already in scope might be called sample too (a piece of state, say), and the two would be ambiguous; the fully-qualified Scene.Field.sample can clash with nothing. (Its lowercase final segment also keeps it distinct from a call, which is capitalised.) So Console.Prefix's two holes are written in full:

Console.Prefix
  Console.Prefix.prefix
    Console.Text "> "
  Console.Prefix.content
    Console.Line "first"
    Console.Line "second"
Each block is opened by its hole's fully-qualified name.

A single-hole call keeps a shorthand: with only one block to fill there is nothing to disambiguate, so the label is dropped and the block goes straight into the call's indented content, as Console.Foreground shows. This is worth keeping because the single-hole call is the common case — most of the user interface is containers with a single content block — and forcing a label there would roughly double the line count for no gain. Names are needed only once a call offers more than one block.

Holes that take arguments

A hole's block can take arguments, and it binds them the way a function does — with INPUT lines — returning its value as the block's last expression. Picture a Scene.Material that wraps some geometry in a material: its material hole is handed a point in space and returns the colour there, and its object hole takes nothing and just places the geometry.

FUNCTION Scene.Material
  HOLE Colour.RGB material
    INPUT Maths.Vector3 coordinate
  HOLE object
One hole returns a colour from a coordinate; the other takes nothing.

At the call site each block is still opened by its hole name. material binds the coordinate it is given and returns a colour; object takes no arguments:

Scene.Material
  Scene.Material.material
    INPUT Maths.Vector3 coordinate
    LET Maths.Real height = Maths.Vector3.Y coordinate
    Colour.RGB.Grey height
  Scene.Material.object
    Scene.Sphere 1.0
Shades every point by its height, and places a unit sphere to shade.

A generic that threads through the holes

A hole's types can depend on a type parameter of the function, chosen by the caller. A procedural Scene.Field is a fair motivating case: it is generic over the value it samples, SampleType, declared in parentheses on the function. That one parameter then appears inside the holes — as sample's return, and as the type of shade's INPUT:

FUNCTION Scene.Field(SampleType)
  HOLE SampleType sample
    INPUT Maths.Vector3 coordinate
  HOLE Colour.RGB shade
    INPUT SampleType value
One type parameter, SampleType, shared across both holes.

The caller supplies that type once, in parentheses on the call — exactly as any other generic call already does — and it flows into both blocks. Here it is a scalar field: Perlin noise sampled per point, shaded to grey.

Scene.Field(Maths.Real)
  Scene.Field.sample
    INPUT Maths.Vector3 coordinate
    Maths.Noise.Perlin coordinate
  Scene.Field.shade
    INPUT Maths.Real value
    Colour.RGB.Grey value
The type argument Maths.Real is given once and reused by Scene.Field.sample and Scene.Field.shade.

Passing an existing function

A hole need not be filled with a fresh block. When an existing function already has the shape a hole wants, it can be named directly — but the exact spelling is still open. Three forms are on the table, each shown filling both of Scene.Field's holes (Maths.Noise.Perlin maps a coordinate to a value, Colour.RGB.Grey a value to a colour).

Label, then reference. The hole's qualified label and then the function, on one line. Explicit, but a label and a function sitting side by side read a little like two calls run together — which is what makes it feel slightly off:

Scene.Field(Maths.Real)
  Scene.Field.sample Maths.Noise.Perlin
  Scene.Field.shade Colour.RGB.Grey
Label then reference — the current form.

Named argument. The hole's name, an =, then the function. A bare word followed by = is a shape the language uses nowhere else, so the name needs no qualifying to stay unambiguous — a state also called sample can't be confused with it — and it reads like the assignment it is:

Scene.Field(Maths.Real)
  sample = Maths.Noise.Perlin
  shade = Colour.RGB.Grey
Named argument — the = marks it, so a bare name is safe.

The same name = carries a whole block just as easily. When the value is not a reference but a fresh function, it follows the =, indented beneath — the return type is not repeated, since the hole already fixes it, and no FUNCTION keyword is needed, because a named argument whose value is a block can only be a hole:

Scene.Field(Maths.Real)
  sample =
    INPUT Maths.Vector3 coordinate
    Maths.Noise.Perlin coordinate
  shade =
    INPUT Maths.Real value
    Colour.RGB.Grey value
The same named form, each hole given a fresh block instead of a reference.

So one form scales from a reference to a full block. It also generalises past holes: the same name = value could name ordinary value arguments too, whenever a call reads more clearly with them spelled out rather than left positional.

Colour.RGB.New
  red = 0.2
  green = 0.6
  blue = 0.9
The same rule, naming ordinary value arguments for clarity.

Positional. Keep the holes positional, like every other argument, and either drop in a reference or an inline anonymous FUNCTION. No new syntax at all, but the reader has to count positions to see which hole is which:

Scene.Field(Maths.Real) Maths.Noise.Perlin Colour.RGB.Grey
Positional references, matched to holes by order.
Scene.Field(Maths.Real)
  FUNCTION Maths.Real
    INPUT Maths.Vector3 coordinate
    Maths.Noise.Perlin coordinate
  FUNCTION Colour.RGB
    INPUT Maths.Real value
    Colour.RGB.Grey value
Positional inline functions — the FUNCTION keyword makes each block an explicit anonymous function.