(std fmt)

Reference

Overview

Formatting in two layers: interpolation of scalars, and a composable layout of blocks.

(import (std prelude))
(import (std fmt))

;; Layer 1: interpolation
(println* ["Hello, " 42 ", ok? " #t])

;; Layer 2: layout
(show "Hello\n")
(show ["Padding: [" (padded/left 10 "foo") "]" nl])
(show ["Number: " (numeric/comma 1234567.89) nl])
(show ["Columns:" nl
       (columnar [["first long text string" "second text string"]
                  ["col 2" "more text for col 2"]])])
;; A string, an int or a char is a block, as a variable or a call as
;; well as written out. A vec of blocks is a block too.
(def name "Ada")
(def n 3)
(show (padded/left 8 name))                     ;;      Ada
(show ["Hello, " name "! You have " n " new messages" #\. nl])
;; Hello, Ada! You have 3 new messages.
(show [(padded/left 8 ["x = " n]) "|" nl])      ;;    x = 3|
;; A block made from a record, by a function with a signature.
;; A double goes through numeric; an if picks a block or nothing.
(type (: Item (Record (: name string) (: price double) (: stock int))))

(: item-line (-> Item Block))
(defun (item-line i)
  [(padded/right 10 (record-ref i name))
   (padded/left 9 (numeric (record-ref i price) #:precision 2 #:comma 3))
   (if (= (record-ref i stock) 0) "  sold out" nothing)
   nl])

;; vec-map gives a vec of blocks, which is a block.
(show (vec-map item-line stock))
;; Green tea      3.50
;; Gaiwan     1,240.00
;; Kettle        89.90  sold out
;; A row per record. A row is a (Vec Block), a block per cell.
(: item-row (-> Item (Vec Block)))
(defun (item-row i)
  [(record-ref i name)
   (numeric (record-ref i price) #:precision 2)
   (record-ref i stock)])

(show (tabular/rows (vec-map item-row stock)
                    #:titles ["Item" "Price" "Stock"]
                    #:align [AlignLeft (AlignOn #\.) AlignRight]
                    #:style StyleAscii))
;; +-----------+---------+-------+
;; | Item      |   Price | Stock |
;; +-----------+---------+-------+
;; | Green tea |    3.50 |    12 |
;; | Gaiwan    | 1240.00 |     2 |
;; | Kettle    |   89.90 |     0 |
;; +-----------+---------+-------+
;; What an if, a cond, a match or a let gives is a block if it is
;; one of the things a block can be.
(show ["Title: " (match title ((Some t) t) (None "-")) nl])

(: greeting (-> string (Option string) Block))
(defun (greeting name title)
  ["Hello, "
   (match title
     ((Some t) [t " "])
     (None nothing))
   name "!"])

(show [(greeting "Ada" (Some "Dr")) nl (greeting "Bob" None) nl])
;; Hello, Dr Ada!
;; Hello, Bob!

Interpolation combines scalars of mixed types into text. It works on a (Vec Disp): a vec literal whose elements are strings, ints, doubles, chars and bools. The grammar Disp reads each element into the case that holds it. println* prints one.

Layout is a block algebra, for padding, aligning, wrapping paragraphs and laying out tables. Everything is a Block: a piece of output. Every function here that lays something out takes its content as a block and gives back a block, so they nest. Nothing is written until a block is run by show, render or render->string.

What is a block. Where a Block is wanted, as an argument or in a vec literal of blocks, each of these is one:

What you writeWhat it is
A string, an int or a char: written out, a variable or a callBStr, BInt, BChar
A vec of any of these, written out or a (Vec Block)BMany: its blocks in order
A Block: nl, nothing, a call such as (padded/left 5 "x")itself
An if, cond, match or let whose results are any of thesethe result it gives

So a single thing needs no brackets, and several things are a vec:

(show "hej")                                   ; hej
(show (padded/left 5 name))                    ; "  Ada"
(show (padded/left 8 ["x = " n]))              ; "   x = 3"
(show [(padded/left 5 "x") (padded/left 5 "y") nl])

A vec in a vec is a block in a block, so extra brackets change nothing. A table is the place where the brackets matter: tabular/rows takes a vec of rows, and a row is a (Vec Block) with a block for each cell. A cell of several pieces is a vec in the row: ["tea" [3 " cups"]] is two cells.

Anything else is converted first:

You haveWrite
A double x(numeric x), or (numeric x #:precision 2)
A bool b(if b "yes" "no")
A long n(long->string n), or (bytes n) for a size
A (Vec string) xs(vec-map BStr xs)
A (List %a) xs(joined f xs ", "), where f makes the block of one element

Where the type is known. A string or a vec is read as a block where a block is wanted: as an argument, as what a function declared to return a Block returns, and as the value of a binding whose type is written. A lambda given to a function that does not say what the lambda returns, such as vec-map, has no type to read a vec literal at, so a vec of mixed pieces that it returns is refused: give it to each, as in (vec-map (fun (x) (each [x nl])) xs), or write a function with a signature. A function of your own that takes blocks needs its signature for the same reason.

A number bound by def. (def n 3) does not say what kind of number n is. Where a block is wanted, it is read as the number written there would be: ["n = " n] is what ["n = " 3] is, and n is an int from then on. A number used as a double later, as in (+ n 0.5), needs (: n double) above its def.

When it does not compile. The messages for the common mistakes, and what to write instead:

The compiler saysWrite
Block: … found a double(numeric x)
Block: … found the number 1.5(numeric 1.5)
Block: … found a boolean, or found a bool(if b "yes" "no")
Block: … found a (Vec string)(vec-map BStr xs), or (: xs (Vec Block)) above (def xs ...)
Block: … found a (Vec (Vec …)), at a lambda that returns a vec(each [...]) around the vec the lambda returns

A combinator of your own is a BRun and the two functions that run blocks, run-block and run-blocks. It is the same kind of thing as the ones in this module:

(: boxed (-> Block Block))
(defun (boxed b)
  (BRun (fun (out col)
          (run-blocks out col ["[" (render->string b) "]"]))))

Settings are parameters, so they are dynamically scoped: with-fmt binds one around a block while it runs, and it is restored when the block ends; with-fmt* binds several, as parameterize does. A block reads a setting when it runs, so a parameterize works around show, but not around the code that builds the block. The settings here are:

ParameterDefaultWhat it sets
current-pad#\spaceThe character padding is made of.
current-width78The right margin of wrapped, justified, columnar, and a table that cuts or wraps.
current-ellipsis""What replaces the part a trim cuts off.
current-string-widththe character countHow wide a string is, in columns. terminal-width counts as a terminal does.
current-precision-1Digits after the decimal separator; negative is as many as needed.
current-comma0Digits per group; zero is no grouping.
current-comma-sep#\,The character between groups.
current-decimal-sep#\.The decimal separator.
current-signed#fWhether a positive number gets a leading +.

A setting of your own works the same way: with-fmt takes the parameter as an argument. Read it with parameter-ref inside the BRun’s function, when the block runs. Read where the block is built, it is read before any enclosing with-fmt has bound it.

Tables are tabular, tabular/rows and columnar as functions, table-row a row at a time, and Table as a quoted literal that the compiler checks. They draw borders in a TableStyle, a header and rules, align a column on a character, and fit current-width by cutting or wrapping cells. bytes, percent, bar and sparkline are blocks for what goes in a cell, or anywhere else. Set current-string-width to terminal-width for text with wide characters or colours in it.

See also: println*, show, render->string, with-fmt, Block

Reference

Types

Functions

Values

Macros

Types

Disp

union ↑ Types

A scalar to interpolate: a string, int, double, char or bool.

DStr
A string, as it is.
DInt
An int, by int->string.
DDouble
A double, by double->string.
DChar
A char, as one character.
DBool
A bool, as #t or #f.

Disp is a grammar (see def/grammar). In a vec literal of mixed scalars, such as ["n = " 42 ", ok? " #t], the grammar reads each element into the case that can hold it. Thus you rarely write the cases.

See also: println*

Sink

type alias ↑ Types

Where a block writes its text: a function given each piece of it.

See also: Block, run-block

Block

union ↑ Types

A piece of layout: a scalar, or a function that writes output and returns the final column position.

BStr
A string, written as it is.
BInt
An int, written in decimal.
BChar
A char.
BMany
Blocks in order: what a vec of blocks is.
BRun
A function taking (sink, col) that writes output and returns the resulting column.
(render->string ["x = " 7 #\!])   ; "x = 7!": a vec of a string, an int and a char
;; The cases are rarely written: where a block is wanted, a value is
;; read as the case that holds it.
(def s "tea")
(: b Block)
(def b s)                      ; (BStr "tea")
(: bs (Vec Block))
(def bs [s " and " b])
(show [bs nl])                 ; tea and tea: a vec in a vec is a block in a block
(show (vec-map BStr ["a" "b"]))   ; ab: a vec of strings made a vec of blocks
;; A function that returns a block.
(: bullets (-> (Vec string) Block))
(defun (bullets xs)
  (vec-map (fun (x) (each ["  * " x nl])) xs))

(: labelled (-> string int Block))
(defun (labelled label n)
  [label ": " n nl])

(show [(bullets ["tea" "milk"]) (labelled "cups" 3)])
;;   * tea
;;   * milk
;; cups: 3

The column is counted in characters since the last newline written. It is the one thing threaded through every block, and what lets fl, space-to and wrapped know where the cursor is.

Block is a grammar (see def/grammar). Where a block is wanted, a string, an int, a char or a vec of blocks is read as the case that holds it, whether it is written out or the value of a variable or a call. The module doc lists what else is a block, and how to convert what is not.

See also: run-block, each, captured

Align

union ↑ Types

Where a cell sits in its table column.

AlignLeft
Against the left edge, padded on the right. The default.
AlignRight
Against the right edge, padded on the left: what numbers want.
AlignCentre
In the middle, the odd column of padding on the right.
AlignOn
Every line of the column has the character in the same place, as a decimal point is in a column of numbers. A line without the character ends where the character would be. The part as a whole is aligned to the right, and so is the title over it.
(show (tabular/rows [["tea" "3.5"] ["gaiwan" "1240"] ["kettle" "89.90"]]
                    #:align [AlignLeft (AlignOn #\.)]))
;; tea       3.5
;; gaiwan 1240
;; kettle   89.90

AlignOn needs the widest part of a line before the character and the widest part from it on, so it is measured by the tables that see all their rows: tabular and tabular/rows. A table laid out a row at a time, and columnar, align such a column to the right.

See also: tabular, tabular/rows, table-layout, columnar

TableStyle

union ↑ Types

How a table’s borders and rules are drawn.

StyleNone
No borders: the columns with a separator between them. The default.
StyleSimple
No borders, two spaces between the columns, and a line of ─ under the header.
StyleAscii
A box of +, - and |.
StyleLight
A box of light lines: ┌─┬─┐.
StyleHeavy
A box of heavy lines: ┏━┳━┓.
StyleDouble
A box of double lines: ╔═╦═╗.
StyleRounded
A box of light lines with round corners: ╭─┬─╮.
StyleMarkdown
A Markdown table, whose rule under the header says how each column is aligned.
StyleCustom
The pieces of a BorderChars.
(show (tabular/rows [["tea" "3.50"] ["gaiwan" "1240.00"]]
                    #:titles ["Item" "Price"]
                    #:style StyleAscii
                    #:align [AlignLeft AlignRight]))
;; +--------+---------+
;; | Item   |   Price |
;; +--------+---------+
;; | tea    |    3.50 |
;; | gaiwan | 1240.00 |
;; +--------+---------+
;;
;; The other boxes are the same table drawn with other characters.

A style with a box pads each cell with a space on each side, and pads every cell to its column, the last one too, so that the right edge is straight. A Markdown column is at least three wide, as its rule needs.

See also: tabular/rows, tabular, table-layout, BorderChars, TableRules

TableRules

union ↑ Types

Which horizontal rules a table has inside its borders.

RulesHeader
A rule under the header. The default.
RulesEvery
A rule under the header and between every two rows. Markdown has none between rows.
RulesNone
No rules inside the table.

See also: TableStyle, tabular/rows

ColumnWidth

union ↑ Types

How wide a table column is.

WidthAuto
As wide as its widest line. The default.
WidthFixed
This many columns, at least one.
WidthAtLeast
As wide as its content, and at least this many columns.
WidthAtMost
As wide as its content, and at most this many columns.
WidthShare
A share of what the other columns and the borders leave of current-width: two columns of shares 1 and 2 take a third and two thirds of it.
(with-fmt current-width 30
  (tabular/rows [["a" "b" "c"]] #:style StyleAscii
                #:widths [(WidthFixed 5) (WidthShare 1) (WidthShare 2)]))
;; +-------+-------+------------+
;; | a     | b     | c          |
;; +-------+-------+------------+

A column narrower than its content shows the content as its Overflow says: pushed out of line, cut or wrapped. A width of no more than zero is one.

See also: tabular/rows, Overflow, current-width

Overflow

union ↑ Types

What a table cell wider than its column does.

OverflowPush
It pushes the rest of its row out of line. The default, and what a table without width rules does.
OverflowCut
It is cut to the column, on the side away from its alignment, with current-ellipsis in place of what is cut.
OverflowWrap
It is broken at its spaces onto more lines, and a word wider than the column is broken where it reaches the edge.
(with-fmt current-width 40
  (tabular/rows [["tea" "A green tea from the hills, picked in spring and rolled by hand."]]
                #:titles ["Item" "Note"] #:style StyleAscii #:overflow OverflowWrap))
;; +------+-------------------------------+
;; | Item | Note                          |
;; +------+-------------------------------+
;; | tea  | A green tea from the hills,   |
;; |      | picked in spring and rolled   |
;; |      | by hand.                      |
;; +------+-------------------------------+

A table that cuts or wraps is made to fit current-width: while it is too wide, the widest column that may shrink loses a column. A column of a fixed width does not shrink, nor one at its WidthAtLeast, nor one below one column. A column aligned on a character is neither cut nor wrapped, since the character would no longer line up.

See also: tabular/rows, ColumnWidth, current-width, current-ellipsis

BorderChars

record ↑ Types

The pieces a table’s borders are drawn with, for a style of your own.

top-left
The start of the line above the table.
top
Repeated across each column in the line above the table. Empty means there is no such line.
top-join
Where two columns meet in the line above the table.
top-right
The end of the line above the table.
left
Before the first cell of a row.
column
Between two cells of a row.
right
After the last cell of a row.
rule-left
The start of a rule.
rule
Repeated across each column in a rule. Empty means there are no rules.
rule-join
Where two columns meet in a rule.
rule-right
The end of a rule.
bottom-left
The start of the line under the table.
bottom
Repeated across each column in the line under the table. Empty means there is no such line.
bottom-join
Where two columns meet in the line under the table.
bottom-right
The end of the line under the table.
marks
Whether the rule under the header marks each column's alignment with colons, as Markdown's does.

A piece of a horizontal line is as wide as the piece of a row it is under: top-left as left, top-join as column, and so on. The built-in boxes put a space on each side of a cell, so their left is "│ " and their top-left is "┌─". A table without left and right leaves out the empty cells at the end of a line and does not pad its last cell, as a table without borders does.

See also: TableStyle

TableLayout

record ↑ Types

The widths, alignments, separator and style of a table laid out a row at a time.

widths
The width of each column.
aligns
The alignment of each column, from the first; a column not given one is AlignLeft.
sep
Written between the columns, without borders.
style
How the borders are drawn.

See also: table-layout, table-row

Table

record ↑ Types

A table written as a quoted literal: its options, its columns, and its rows.

options
The table's style, header, rules, width and overflow, each there or not.
columns
The columns, each with its title, alignment, width and kind.
rows
The rows, each written or spliced.
(: (Vec (Vec TableCell)))
(def products
  [[(CellText "Green tea") (CellNum 3.5) (CellInt 12) (CellNum 0.4)]
   [(CellText "Gaiwan") (CellNum 1240.0) (CellInt 2) (CellNum 1.0)]])

(show-table
 '(table (style ascii) header
         (column "Item")
         (column "Price" (align (on #\.)) (number (precision 2) (comma 3)))
         (column "Stock" (align right))
         (column "Share" (percent (precision 0)))
         (rows ,products)))
;; +-----------+----------+-------+-------+
;; | Item      |    Price | Stock | Share |
;; +-----------+----------+-------+-------+
;; | Green tea |     3.50 |    12 |   40% |
;; | Gaiwan    | 1,240.00 |     2 |  100% |
;; +-----------+----------+-------+-------+
;;
;; A column of (bar (max 100.0)) shows each number as a bar of its
;; share of the max, as the block bar does.

(show-table '(table (style markdown) header (column "Key") (column "Value")
                    (rows (row "width" 78) (row "style" "markdown"))))

The literal is (table option ... column ... (rows row ...)). The options come first, each at most once and in any order: (style s) with a TableStyle as its name, rounded or markdown; header, which makes the column titles a header row; (rules r) with a TableRules; (width n), the width the table is fitted to in place of current-width; and (overflow o) with an Overflow.

A column is (column "Title" option ...), its options again each at most once: (align a) with an Align, as right or (on #\.); (width w) with a ColumnWidth, as 12 or (share 2); and a kind, which shows the column’s numbers as one of this module’s blocks: (number (precision 2) (comma 3) signed) as numeric, bytes, (percent (precision 1)), or (bar (max 100.0) (width 10)), a bar of the number’s share of the max. A column of a kind other than a bar is aligned to the right unless it says otherwise. A cell that is not a number is shown as it is in any column.

A row is written, (row cell ...), a cell being a string or a number or a spliced value of one; or spliced, as a (Vec TableCell) for one row or a (Vec (Vec TableCell)) for many. A spliced number bound by a def of a bare literal is read as the number written there would be, as it is in a block.

The literal is read where it is written, so a misspelled option, an option given twice, a style that is not one, a table with no columns and a splice of the wrong type are compile errors that say where.

See also: table-block, show-table, TableCell, tabular/rows

TableCell

union ↑ Types

A cell of a Table: what a written cell is, and what a spliced row holds.

CellText
Text, shown as it is.
CellInt
A whole number, shown as its column's kind says.
CellNum
A number, shown as its column's kind says.
CellBlocks
Blocks, shown as they are, as each shows them.

See also: Table

Functions

println*

function ↑ Functions

(: println* (-> (Vec Disp) void))
(println* ds)

Prints scalars of mixed types, then a newline.

ds
The scalars, written as a vec literal.
(println* ["n = " 42 ", ok? " #t ", " #\x ", " 1.5])

See also: Disp, show

render

function ↑ Functions

(: render (-> OutputPort Block void))
(render port b)

Writes a block to a port.

port
The port to write to.
b
The block, run from column 0.

It is #:sync, and cannot be otherwise: the sink is stored in a BRun, and a stored function’s colour is its type, so no suspending sink can go there. To render from a fiber, use render->string and write the string yourself.

See also: show, render->string

show

function ↑ Functions

(: show (-> Block void))
(show b)

Writes a block to the current output port.

b
The block, run from column 0.
(show "Hello\n")
(show ["Padding: [" (padded/left 10 "foo") "]" nl])

render to current-output-port, and #:sync for the same reason.

See also: render, render->string

render->string

function ↑ Functions

(: render->string (-> Block string))
(render->string b)

The text a block writes, as a string.

b
The block, run from column 0.
returns
Everything the block wrote.
(render->string (padded/left 5 "7"))   ; "    7"

See also: show, render, captured

run-block

function ↑ Functions

(: run-block (-> Sink int Block int))
(run-block out col b)

Runs one block: the engine’s step, for writing combinators.

out
The sink to write to.
col
The column the block starts at.
b
The block.
returns
The column it ends at.

See also: run-blocks, Block

run-blocks

function ↑ Functions

(: run-blocks (-> Sink int (Vec Block) int))
(run-blocks out col bs)

Runs blocks one after another, threading the column.

out
The sink to write to.
col
The column the first block starts at.
bs
The blocks.
returns
The column the last one ends at.

See also: run-block

with-fmt

function ↑ Functions

(: with-fmt (-> (Param %a) %a Block Block))
(with-fmt p v b)

Runs a block with a parameter bound to a value.

%a
The parameter's type.
p
The parameter: one of this module's settings, or one of your own.
v
Its value while the block runs.
b
The block.
returns
A block. The parameter is bound when it runs, not when it is built, and restored when it ends.
(show (with-fmt current-precision 2 (numeric 3.14159)))   ; 3.14
;; Settings nest; each applies to the blocks inside it.
(show [(with-fmt current-width 24
         [(with-fmt current-ellipsis "…"
            [(trimmed/right 8 "A green tea from the hills") nl])
          (wrapped "A green tea from the hills, picked in spring and rolled by hand.")])
       nl])
;; A green…
;; A green tea from the
;; hills, picked in spring
;; and rolled by hand.

A block reads a setting when it runs, not when it is built. So a parameterize around the code that builds a block does nothing: the block runs later, in show, when the parameterize has ended.

(parameterize ((current-width 20)) (show (wrapped text)))    ; 20 wide
(show (with-fmt current-width 20 (wrapped text)))            ; 20 wide
(show (parameterize ((current-width 20)) (wrapped text)))    ; 78 wide: the default

A parameterize around show is the simplest way to set something for a whole output. with-fmt and with-fmt* set it for part of one: a cell, a paragraph, the numbers of a column.

See also: with-fmt*, with-pad, current-width

with-pad

function ↑ Functions

(: with-pad (-> char Block Block))
(with-pad c b)

Runs a block with another padding character.

c
The padding character.
b
The block.
returns
A block: (with-fmt current-pad c b).
(show (with-pad #\. ["ab" (space-to 6) "c"]))   ; ab....c, a dot leader

See also: current-pad, space-to

terminal-width

function ↑ Functions

(: terminal-width (-> string int))
(terminal-width s)

How many columns a string takes in a terminal, for current-string-width.

s
The string.
returns
Its width in columns.
(def esc (char->string (integer->char 27)))
(terminal-width "tea")                                   ;; 3
(terminal-width "茶")                                     ;; 2
(terminal-width (str "e" (char->string (integer->char 769))))  ;; 1: e and an accent
(terminal-width (str esc "[1mtea" esc "[0m"))             ;; 3: the escapes take none

(with-fmt current-string-width terminal-width
  (tabular/rows [["茶" "1"] ["tea" "2"]] #:style StyleAscii))

East Asian Wide and Fullwidth characters, emoji among them, take two columns. Combining marks, format and control characters take none. An escape sequence takes none: a CSI, as in a colour, ends at a character from @ to ~, and an OSC, as in a link, at BEL or at ESC and a backslash. What follows a zero-width joiner takes no columns of its own, so a family emoji is two wide. Variation selector 16 makes a one-column character before it two wide, a skin tone modifier joins the emoji before it, and two regional indicators are one flag, two wide.

It is not the default measure, which counts code points and is cheaper. Set it where the text is for a terminal and holds such characters: a wide character measured as one column puts every column after it out of line.

The table is Unicode 16.0. A terminal whose own idea of a character’s width differs from it, as some do for emoji, draws that character wider or narrower than measured.

See also: current-string-width, width-of

each

function ↑ Functions

(: each (-> (Vec Block) Block))
(each bs)

Many blocks as one.

bs
The blocks.
returns
A block that runs them in order: (BMany bs).
;; vec-map does not say what its function returns, so a vec literal
;; of mixed pieces that the lambda returns is not read as blocks.
;; Under each, it is.
(show (vec-map (fun (i) (each [(record-ref i name) ": " (record-ref i stock) nl]))
               stock))
;; Green tea: 12
;; Gaiwan: 2
;; Kettle: 0

A vec of blocks is a block where a block is wanted, so each is needed only where nothing says that one is: as the result of a lambda given to a function that does not say what its function returns, such as vec-map. A lambda given to joined or captured returns a block, as their signatures say, and needs no each.

See also: nothing, joined, Block

captured

function ↑ Functions

(: captured (-> (-> string Block) Block Block))
(captured f b)

Renders a block to a string and passes it to a function returning a replacement block.

f
Given the text the block wrote; its block is run in its place.
b
The block to render.
returns
A block.
(captured (fun (s) (BInt (string-count s))) "abcd")   ; writes 4
;; Brackets around whatever the content is.
(: boxed (-> Block Block))
(defun (boxed b)
  (captured (fun (s) ["[" s "]"]) b))

(show [(boxed ["x = " 7]) nl])   ; [x = 7]

How a combinator that needs the whole text, rather than writing as it goes, is made: padding, trimming and wrapping are all written with it. The block is rendered from column 0, and f’s block runs at the current column.

See also: render->string

width-of

function ↑ Functions

(: width-of (-> string int))
(width-of s)

How many columns a string takes, by current-string-width.

s
The string.
returns
Its width.

See also: current-string-width

space-to

function ↑ Functions

(: space-to (-> int Block))
(space-to goal)

Pads to a column.

goal
The column to move to.
returns
A block that writes padding up to goal, and nothing if the cursor is already at or past it.
(render->string ["ab" (space-to 5) "c"])   ; "ab   c"
;; A dot leader: every price starts at column 12.
(show [(with-pad #\. ["Tea" (space-to 12) "3.50"]) nl
       (with-pad #\. ["Gaiwan" (space-to 12) "1240.00"]) nl])
;; Tea.........3.50
;; Gaiwan......1240.00

It fills with current-pad, so under with-pad it is a leader. It never moves backwards.

See also: tab-to, with-pad

tab-to

function ↑ Functions

(: tab-to (-> (#:width int) Block))
(tab-to #:width 8)

Pads to the next tab stop.

#:width
The distance between tab stops. 8 unless given.
returns
A block that pads to the next multiple of width.
(render->string ["ab" (tab-to #:width 4) "c"])     ; "ab  c"
(render->string ["abcd" (tab-to #:width 4) "c"])   ; "abcd    c"

It always moves: at a tab stop already, it pads to the next one.

See also: space-to

padded/left

function ↑ Functions

(: padded/left (-> int Block Block))
(padded/left w b)

Pads content on the left to a width: aligned right.

w
The width, in columns.
b
The content, a block.
returns
A block. Content wider than w is left as it is: padding never cuts.
(render->string (padded/left 6 ["x=" 7]))   ; "   x=7"
;; The content is a vec even when it is one thing.
(show [(padded/left 9 (numeric 1240.0 #:precision 2 #:comma 3)) nl])   ;  1,240.00
(show [(with-pad #\0 (padded/left 4 42)) nl])                        ; 0042

See also: padded/right, padded/both, fitted/left, current-pad

padded/right

function ↑ Functions

(: padded/right (-> int Block Block))
(padded/right w b)

Pads content on the right to a width: aligned left.

w
The width, in columns.
b
The content, a block.
returns
A block. Content wider than w is left as it is.
(show [(padded/right 10 "Green tea") "3.50" nl
       (padded/right 10 "Gaiwan") "1240.00" nl])
;; Green tea 3.50
;; Gaiwan    1240.00

See also: padded/left, padded/both, fitted/right

padded/both

function ↑ Functions

(: padded/both (-> int Block Block))
(padded/both w b)

Pads content on both sides to a width: centred.

w
The width, in columns.
b
The content, a block.
returns
A block, with the odd column, if there is one, on the right. Content wider than w is left as it is.
(show [(with-pad #\= (padded/both 20 " Receipt ")) nl])
;; ===== Receipt ======

See also: padded/left, padded/right, fitted/both

trimmed/left

function ↑ Functions

(: trimmed/left (-> int Block Block))
(trimmed/left w b)

Cuts content to a width, keeping its end.

w
The width, in columns.
b
The content, a block.
returns
A block. Content that fits is left as it is; otherwise current-ellipsis stands at the start, where the cut was.
(render->string (trimmed/left 3 "abcdef"))   ; "def"

See also: trimmed/right, trimmed/both, fitted/left, current-ellipsis

trimmed/right

function ↑ Functions

(: trimmed/right (-> int Block Block))
(trimmed/right w b)

Cuts content to a width, keeping its start.

w
The width, in columns.
b
The content, a block.
returns
A block. Content that fits is left as it is; otherwise current-ellipsis stands at the end.
(render->string (trimmed/right 3 "abcdef"))   ; "abc"

See also: trimmed/left, trimmed/both, fitted/right, current-ellipsis

trimmed/both

function ↑ Functions

(: trimmed/both (-> int Block Block))
(trimmed/both w b)

Cuts content to a width, keeping its middle.

w
The width, in columns.
b
The content, a block.
returns
A block. Content that fits is left as it is; otherwise current-ellipsis stands at both ends.
(render->string (trimmed/both 4 "abcdefgh"))   ; "cdef"

See also: trimmed/left, trimmed/right, fitted/both

fitted/left

function ↑ Functions

(: fitted/left (-> int Block Block))
(fitted/left w b)

Content at exactly a width, aligned right: padded if short, cut if long.

w
The width, in columns.
b
The content, a block.
returns
A block: trimmed/left, then padded/left.

See also: padded/left, trimmed/left

fitted/right

function ↑ Functions

(: fitted/right (-> int Block Block))
(fitted/right w b)

Content at exactly a width, aligned left: padded if short, cut if long.

w
The width, in columns.
b
The content, a block.
returns
A block: trimmed/right, then padded/right.
(render->string [(fitted/right 5 "ab") "|"])       ; "ab   |"
(render->string [(fitted/right 3 "abcdef") "|"])   ; "abc|"

See also: padded/right, trimmed/right

fitted/both

function ↑ Functions

(: fitted/both (-> int Block Block))
(fitted/both w b)

Content at exactly a width, centred: padded if short, cut in the middle if long.

w
The width, in columns.
b
The content, a block.
returns
A block: trimmed/both, then padded/both.
(show ["[" (fitted/both 6 "ab") "]" nl])           ; [  ab  ]

See also: padded/both, trimmed/both

joined

function ↑ Functions

(: joined (-> (-> %a Block) (List %a) Block Block))
(joined f xs sep)

Every element of a list, formatted, with a separator between them.

%a
The elements' type.
f
Formats one element.
xs
The elements.
sep
The separator, itself blocks.
returns
A block. One element has no separator, and none is nothing.
(show (joined (fun (n) (BInt n)) (list 1 2 3) ", "))   ; 1, 2, 3
;; A constructor of Block is a function, so a list of strings is
;; joined with BStr.
(show [(joined BStr (list "tea" "milk" "honey") ", ") nl])   ; tea, milk, honey

;; f returns a block, so content of several pieces is under each.
(show [(joined (fun (s) ["* " s]) (list "tea" "milk") nl) nl])
;; * tea
;; * milk

;; For a vec, a separator is vec->list away.
(show [(joined BStr (vec->list ["a" "b"]) " | ") nl])        ; a | b

See also: joined/prefix, joined/suffix, joined/last

joined/prefix

function ↑ Functions

(: joined/prefix (-> (-> %a Block) (List %a) Block Block))
(joined/prefix f xs sep)

Every element of a list, formatted, with a separator before each, the first included.

%a
The elements' type.
f
Formats one element.
xs
The elements.
sep
The separator, itself blocks.
returns
A block.
(show (joined/prefix (fun (s) (BStr s)) (list "a" "b") ","))   ; ,a,b

See also: joined, joined/suffix

joined/suffix

function ↑ Functions

(: joined/suffix (-> (-> %a Block) (List %a) Block Block))
(joined/suffix f xs sep)

Every element of a list, formatted, with a separator after each, the last included.

%a
The elements' type.
f
Formats one element.
xs
The elements.
sep
The separator, itself blocks.
returns
A block.
(show (joined/suffix (fun (s) (BStr s)) (list "a" "b") ","))   ; a,b,

See also: joined, joined/prefix

joined/last

function ↑ Functions

(: joined/last (-> (-> %a Block) (-> %a Block) (List %a) Block Block))
(joined/last f last-f xs sep)

Like joined, with the last element formatted by a function of its own.

%a
The elements' type.
f
Formats every element but the last.
last-f
Formats the last element.
xs
The elements.
sep
The separator, itself blocks.
returns
A block. A list of one is formatted by last-f alone.
(show (joined/last (fun (s) (BStr s))
                   (fun (s) ["and " s])
                   (list "a" "b" "c")
                   ", "))
;; a, b, and c

The separator is written before the last element too, so what last-f adds is a word, not a comma.

See also: joined

numeric

function ↑ Functions

(: numeric (-> double (#:precision int) (#:comma int) (#:comma-sep char) (#:decimal-sep char) (#:signed bool) Block))
(numeric x #:precision -1 #:comma -1 #:comma-sep #\nul
                #:decimal-sep #\nul #:signed #f)

A number, with a precision, grouped digits and separators of your choosing.

x
The number. An int is formatted as (cast double n).
#:precision
Digits after the decimal separator. Negative, the default, is current-precision.
#:comma
Digits per group; 0 is no grouping. Negative, the default, is current-comma.
#:comma-sep
The character between groups. #\nul, the default, is current-comma-sep.
#:decimal-sep
The decimal separator. #\nul, the default, is current-decimal-sep.
#:signed
#t gives a positive number a leading +. #f, the default, is current-signed.
returns
A block.
(numeric 2.0 #:precision 2)                  ; 2.00
(numeric -3.14159 #:precision 3)             ; -3.142
(numeric 1234567.891 #:precision 2 #:comma 3)  ; 1,234,567.89
(numeric 42.0 #:signed #t)                   ; +42
(numeric 1234.5 #:comma 3 #:comma-sep #\space #:decimal-sep #\,)  ; 1 234,5
;; A double is not a block. numeric makes it one.
(: total double)
(def total 1333.4)
(show ["Total: " (numeric total #:precision 2 #:comma 3) nl])   ; Total: 1,333.40

;; The settings apply to every numeric inside a with-fmt.
(show (with-fmt current-precision 1 ["pi is about " (numeric 3.14159) nl]))
;; pi is about 3.1

The digits are the invariant culture’s and the separators are settings, so the machine’s locale never shows. A keyword not given defaults to its parameter when the block runs. Since a keyword’s default is what stands for the parameter, a keyword cannot ask for that default against a parameter bound otherwise: #:signed #f under a current-signed of #t still signs, and a negative #:precision still reads current-precision.

See also: numeric/comma, current-precision, current-comma

numeric/comma

function ↑ Functions

(: numeric/comma (-> double Block))
(numeric/comma x)

A number with its digits grouped in thousands: the common case.

x
The number.
returns
A block: (numeric x #:comma 3).
(numeric/comma 1234567.0)   ; 1,234,567
(numeric/comma 42.0)        ; 42

See also: numeric

bytes

function ↑ Functions

(: bytes (-> long (#:si bool) (#:precision int) Block))
(bytes n #:si #f #:precision 1)

A number of bytes, in the largest unit there is at least one of.

n
The number of bytes.
#:si
Units of 1000, kB and MB, rather than of 1024, KiB and MiB. False unless given.
#:precision
Digits after the decimal separator, in any unit but B. One unless given.
returns
A block.
(show [(bytes 512L) " " (bytes 1258291L) " " (bytes 1500L #:si #t)])
;; 512 B 1.2 MiB 1.5 kB

Bytes are whole, so a size under one kibibyte, or kilobyte, has no decimals. The units go to EiB, or EB. The decimal separator is current-decimal-sep.

See also: percent, numeric, current-decimal-sep

percent

function ↑ Functions

(: percent (-> double (#:precision int) Block))
(percent x #:precision 1)

A share, written as a percentage: 0.125 is 12.5%.

x
The share, 1.0 being all of it.
#:precision
Digits after the decimal separator. One unless given.
returns
A block.
(show [(percent 0.125) " " (percent 1.0 #:precision 0)])
;; 12.5% 100%

See also: bytes, bar, numeric

bar

function ↑ Functions

(: bar (-> double (#:width int) (#:empty string) Block))
(bar fraction #:width 10 #:empty " ")

A horizontal bar, filled to a fraction of its width to an eighth of a column.

fraction
How much of the bar is filled, from 0 to 1; a fraction outside that is the nearer end.
#:width
The width of the whole bar in columns. Ten unless given.
#:empty
What the part not filled is made of, a column at a time. A space unless given.
returns
A block, exactly width columns wide.
(show ["[" (bar 0.62 #:width 20) "]"])
;; [████████████▍       ]
(show ["[" (bar 0.5 #:width 5 #:empty "░") "]"])
;; [██▌░░]

The filled part is whole blocks, █, and one of the seven eighths of a block, ▏ to ▉, for what is left over, rounded to the nearest eighth.

See also: sparkline, percent

sparkline

function ↑ Functions

(: sparkline (-> (Vec double) Block))
(sparkline xs)

Numbers as a line of bars of eight heights, a column each.

xs
The numbers.
returns
A block, a column for each number.
(show (sparkline [1.0 2.0 4.0 8.0 4.0 2.0 1.0]))
;; ▁▂▄█▄▂▁

The smallest number is the lowest bar, ▁, and the largest the highest, █; the rest are divided evenly between them. Numbers that are all the same are all in the middle, ▄.

See also: bar

wrapped

function ↑ Functions

(: wrapped (-> Block Block))
(wrapped b)

Content filled to the right margin, ragged on the right.

b
The content. Every run of whitespace in it, newlines too, is a break between words.
returns
A block.
(with-fmt current-width 12 (wrapped "the quick brown fox jumps"))
;; the quick
;; brown fox
;; jumps
;; The first line starts after the label, so it has less room; the
;; lines after it start at column 0. The content may mix strings and
;; numbers like any other.
(show [(with-fmt current-width 30
         ["Note: " (wrapped ["picked on day " 3 " of spring, rolled by hand and dried in the sun."])])
       nl])
;; Note: picked on day 3 of
;; spring, rolled by hand and
;; dried in the sun.

The first line starts wherever the cursor is, and has only the room left before current-width. Words are separated by one current-pad. A word wider than a line gets a line of its own rather than being cut.

See also: justified, current-width

justified

function ↑ Functions

(: justified (-> Block Block))
(justified b)

Content filled to the right margin and flush against both, the last line excepted.

b
The content. Every run of whitespace in it is a break between words.
returns
A block.
(with-fmt current-width 12 (justified "the quick brown fox jumps"))
;; the    quick
;; brown    fox
;; jumps

Lines are broken as wrapped breaks them, then the gaps are widened to fill the line, the leftmost ones taking any extra column. The last line, and a line of one word, are not stretched.

See also: wrapped, current-width

columnar

function ↑ Functions

(: columnar (-> (Vec (Vec Block)) (#:sep string) (#:align (Vec Align)) Block))
(columnar cols #:sep " " #:align [])

Columns side by side, dividing current-width evenly and cutting what does not fit.

cols
The columns, each a vec of blocks; a newline in one starts its next row.
#:sep
Written between the columns. A space unless given.
#:align
The alignment of each column, from the first; a column not given one is AlignLeft.
returns
A block.
(show (with-fmt current-width 11
        (columnar [["abc" nl "def"]
                   ["123" nl "456"]])))
;; abc   123
;; def   456

Each column is rendered on its own and the lines are laid out together. The width left once the separators are taken out is shared equally, and every cell is cut and padded to its share, the last column cut but not padded on the right. A cut keeps the side the cell is aligned to: the start of a left-aligned cell, the end of a right-aligned one, the middle of a centred one. A column with fewer lines than the others contributes blanks, and blanks at the end of a line are left out, separators and all. The table starts on a fresh line, and every row ends with a newline.

See also: tabular, Align, current-width

tabular

function ↑ Functions

(: tabular (-> (Vec (Vec Block)) (#:sep string) (#:align (Vec Align)) (#:style TableStyle) (#:titles (Vec Block)) (#:rules TableRules) (#:widths (Vec ColumnWidth)) (#:overflow Overflow) Block))
(tabular cols #:sep " " #:align [] #:style StyleNone #:titles [] #:rules RulesHeader
                #:widths [] #:overflow OverflowPush)

Columns side by side, each as wide as its widest line, cutting nothing.

cols
The columns, each a vec of blocks; a newline in one starts its next row.
#:sep
Written between the columns, without borders. A space unless given.
#:align
The alignment of each column, from the first; a column not given one is AlignLeft.
#:style
How the borders are drawn. StyleNone unless given.
#:titles
A header row, a block per column. None unless given.
#:rules
Which rules there are inside the borders. RulesHeader unless given.
#:widths
How wide each column is, from the first; a column not given a rule is as wide as its content.
#:overflow
What a cell wider than its column does. OverflowPush unless given.
returns
A block.
(show (tabular [["a" nl "bbb"]
                ["1" nl "2"]]))
;; a   1
;; bbb 2

(show (tabular [["a" nl "bbb"]
                ["1" nl "2"]]
               #:sep " | "))
;; a   | 1
;; bbb | 2
;; A vec of columns, each a vec of blocks, with nl between its cells.
;; Here the brackets in brackets are right: a table is a vec of vecs.
(show (tabular [["tea" nl "gaiwan" nl "kettle"]
                [3 nl 1240 nl 90]]
               #:sep "  " #:align [AlignLeft AlignRight]))
;; tea        3
;; gaiwan  1240
;; kettle    90

Laid out as columnar lays them out, but with each column padded to the width its own content asks for, and nothing cut. A left-aligned last column is not padded at all. A column with fewer lines than the others contributes blanks, and blanks at the end of a line are left out, separators and all. The table starts on a fresh line, and every row ends with a newline.

See also: tabular/rows, columnar, Align

tabular/rows

function ↑ Functions

(: tabular/rows (-> (Vec (Vec Block)) (#:sep string) (#:align (Vec Align)) (#:style TableStyle) (#:titles (Vec Block)) (#:rules TableRules) (#:widths (Vec ColumnWidth)) (#:overflow Overflow) Block))
(tabular/rows rows #:sep " " #:align [] #:style StyleNone #:titles [] #:rules RulesHeader
                     #:widths [] #:overflow OverflowPush)

A table given as rows, each column as wide as its widest cell.

rows
The rows, each a vec of cells, a block per cell; a newline in a cell gives its row another line.
#:sep
Written between the columns, without borders. A space unless given.
#:align
The alignment of each column, from the first; a column not given one is AlignLeft.
#:style
How the borders are drawn. StyleNone unless given.
#:titles
A header row, a block per column. None unless given.
#:rules
Which rules there are inside the borders. RulesHeader unless given.
#:widths
How wide each column is, from the first; a column not given a rule is as wide as its content.
#:overflow
What a cell wider than its column does. OverflowPush unless given.
returns
A block.
(show (tabular/rows [["name" "n" "time"]
                     ["sort" 10 "1.2 µs"]
                     ["sort" 1000 "210 µs"]]
                    #:sep "  "
                    #:align [AlignLeft AlignRight AlignRight]))
;; name     n    time
;; sort    10  1.2 µs
;; sort  1000  210 µs
;; A cell is one block. A cell of several pieces is a vec in the row,
;; and a newline in it gives its row another line.
(show (tabular/rows [["tea" [3 " cups"]]
                     ["kettle" ["boils in" nl "4 minutes"]]]
                    #:style StyleAscii))
;; +--------+-----------+
;; | tea    | 3 cups    |
;; | kettle | boils in  |
;; |        | 4 minutes |
;; +--------+-----------+
;; Rows made from data. A function with a signature makes each row, so
;; that the literal in it is read as blocks.
(: item-row (-> Item (Vec Block)))
(defun (item-row i)
  [(record-ref i name) (numeric (record-ref i price) #:precision 2) (record-ref i stock)])

(show (tabular/rows (vec-map item-row stock)
                    #:titles ["Item" "Price" "Stock"]
                    #:align [AlignLeft (AlignOn #\.) AlignRight]
                    #:style StyleAscii))
;; +-----------+---------+-------+
;; | Item      |   Price | Stock |
;; +-----------+---------+-------+
;; | Green tea |    3.50 |    12 |
;; | Gaiwan    | 1240.00 |     2 |
;; | Kettle    |   89.90 |     0 |
;; +-----------+---------+-------+

The same table as tabular makes of the columns these rows are made of. It is table-row on each row, with the layout that table-widths gives them; rows of different lengths are fine, a short one ending early.

See also: tabular, table-row, TableStyle

table-layout

function ↑ Functions

(: table-layout (-> (Vec int) (#:sep string) (#:align (Vec Align)) (#:style TableStyle) TableLayout))
(table-layout widths #:sep " " #:align [] #:style StyleNone)

A layout for table-row: the width of each column, settled before any row is written.

widths
The width of each column, in columns as current-string-width counts them.
#:sep
Written between the columns, without borders. A space unless given.
#:align
The alignment of each column, from the first; a column not given one is AlignLeft.
#:style
How the borders are drawn. StyleNone unless given.
returns
A TableLayout.

See also: table-row, table-widths, table-top, table-rule, table-bottom

table-top

function ↑ Functions

(: table-top (-> TableLayout Block))
(table-top layout)

The line above a table laid out a row at a time.

layout
The table's layout.
returns
A block: the line, or nothing for a style without one.
(def layout (table-layout [6 5] #:style StyleAscii #:align [AlignLeft AlignRight]))
(show [(table-top layout) (table-row layout ["Item" "Price"]) (table-rule layout)
       (table-row layout ["tea" "3.50"]) (table-bottom layout)])
;; +--------+-------+
;; | Item   | Price |
;; +--------+-------+
;; | tea    |  3.50 |
;; +--------+-------+

See also: table-row, table-rule, table-bottom

table-rule

function ↑ Functions

(: table-rule (-> TableLayout Block))
(table-rule layout)

A rule across a table laid out a row at a time: under its header, or between two rows.

layout
The table's layout.
returns
A block: the rule, or nothing for a style without rules.

See also: table-top, table-row, table-bottom

table-bottom

function ↑ Functions

(: table-bottom (-> TableLayout Block))
(table-bottom layout)

The line under a table laid out a row at a time.

layout
The table's layout.
returns
A block: the line, or nothing for a style without one.

See also: table-top, table-row, table-rule

table-widths

function ↑ Functions

(: table-widths (-> (Vec (Vec Block)) (Vec int)))
(table-widths rows)

The width each column of some rows asks for: its widest cell.

rows
The rows, each a vec of cells, a block per cell.
returns
A vec with a width for every column any of the rows has.
(table-widths [["name" "median"] ["fib 20" "14.9 µs" "note"]])
;; [6 7 4]

See also: table-layout, tabular/rows

table-row

function ↑ Functions

(: table-row (-> TableLayout (Vec Block) Block))
(table-row layout row)

One row of a table, laid out to a layout fixed beforehand, so that it can be written as soon as it is known.

layout
The widths, alignments and separator, from table-layout.
row
The cells, a block each; a newline in a cell gives the row another line.
returns
A block.
;; Each result printed when it is measured, under a heading printed first.
(def layout (table-layout [8 8] #:sep "  " #:align [AlignLeft AlignRight]))
(show (table-row layout ["name" "time"]))
(loop (:for (Tuple name t) results)
      (:do (show (table-row layout [name t]))))
;; name          time
;; sort        1.2 µs

Every cell is padded to its column’s width as its alignment says, and a left-aligned last cell is not padded at all. A cell wider than its column is not cut: it pushes the rest of its row to the right, and the rows after it are not affected. Cells past the columns of the layout are written as they are, with the separator before each. Empty cells at the end of a line are left out, separators and all. The row starts on a fresh line and ends with a newline.

See also: table-layout, table-widths, tabular/rows

table-block

function ↑ Functions

(: table-block (-> Table Block))
(table-block t)

A table literal as a block.

t
The table.
returns
A block, laid out as tabular/rows lays out a table.

See also: Table, show-table, tabular/rows

show-table

function ↑ Functions

(: show-table (-> Table void))
(show-table t)

Writes a table literal to the current output port.

t
The table.

See also: Table, table-block, show

Values

current-pad

value ↑ Values

(: current-pad (Param char))

The character padding is made of. A space unless set.

Padding that is not a whole number of pad characters wide is as many as fit. Words in wrapped and justified are separated by it too.

See also: with-pad, padded/left, space-to

current-width

value ↑ Values

(: current-width (Param int))

The right margin: what wrapped, justified and columnar fill up to. 78 unless set.

See also: wrapped, justified, columnar

current-ellipsis

value ↑ Values

(: current-ellipsis (Param string))

What replaces the part a trim cuts off. Empty, which cuts silently, unless set.

(with-fmt current-ellipsis "..." (trimmed/right 6 "abcdefgh"))   ; abc...

It takes its room out of the content, so a trimmed text is never wider than asked for. An ellipsis wider than that is cut itself.

See also: trimmed/right

current-string-width

value ↑ Values

(: current-string-width (Param (-> string int)))

How many columns a string takes. Its character count unless set.

;; An astral character takes two columns on a terminal.
(defun (astral-width s)
  (string-fold (fun (ch acc) (+ acc (if (> (char->integer ch) 65535) 2 1))) 0 s))

(with-fmt current-string-width astral-width (padded/left 5 "😀"))

Everything that measures or cuts goes through it: padding, trimming, wrapping and tables. The column a BRun is handed is not: it counts characters.

See also: width-of

current-precision

value ↑ Values

(: current-precision (Param int))

Digits after the decimal separator. Negative, the default, is as many as the number needs.

As many as it needs is what double->string would give. A precision rounds.

See also: numeric

current-comma

value ↑ Values

(: current-comma (Param int))

Digits per group in the integer part. Zero, the default, is no grouping.

See also: numeric, numeric/comma, current-comma-sep

current-comma-sep

value ↑ Values

(: current-comma-sep (Param char))

The character between groups of digits. A comma unless set.

See also: current-comma, numeric

current-decimal-sep

value ↑ Values

(: current-decimal-sep (Param char))

The decimal separator. A full stop unless set.

See also: numeric, current-comma-sep

current-signed

value ↑ Values

(: current-signed (Param bool))

Whether a number with no sign of its own gets a leading +. #f unless set.

See also: numeric

nothing

value ↑ Values

(: nothing Block)

An empty block that writes nothing, suitable as a no-op branch in conditionals.

(render->string ["a" (if verbose? nl nothing) "b"])
;; The arm of a match that writes nothing.
(show ["Hello, "
       (match title
         ((Some t) [t " "])
         (None nothing))
       name nl])

See also: each

nl

value ↑ Values

(: nl Block)

A newline.

See also: fl

fl

value ↑ Values

(: fl Block)

A fresh line: a newline, unless the cursor is already at the start of one.

(render->string ["a" nl fl "b"])   ; "a\nb"
(render->string [fl "a"])          ; "a"

See also: nl

Macros

with-fmt*

macro ↑ Macros

(with-fmt* ((p v) ...) block ...)

Runs blocks with settings bound, written as parameterize is.

p
A parameter: one of this module's settings, or one of your own.
v
Its value while the blocks run.
block ...
The blocks, run in order.
(show (with-fmt* ((current-width 24)
                  (current-ellipsis "…"))
        (trimmed/right 8 "A green tea from the hills") nl
        (wrapped "A green tea from the hills, picked in spring and rolled by hand.")))
;; A green…
;; A green tea from the
;; hills, picked in spring
;; and rolled by hand.

with-fmt for each setting, the first outermost, around the blocks as a vec. The values are computed where the form is, when the block is built, as the arguments of with-fmt are. With no settings it is the blocks alone.

See also: with-fmt, with-pad