on master help sign in
j/docs/language.md
973 lines 28 kB history raw
1# The `j` language
3`j` is a small, pure, functional language. Every `j` command evaluates one
4expression in this language against the current repository. The language is
5the whole interface: there are no subcommands beyond a handful of reserved
6words, and no general flags apart from help. `--help` and `-h` open help as
7the first word or as a separate argument after a command: `j new --help`
8shows help without executing `new`. Inside a single expression argument or
9stdin, `--` still starts a language comment, so `j 'new --help'` executes
10`new`. Use `j help` for a command guide, `j commands` for everyday tasks,
11and `j commands all` for the complete vocabulary. `j setup NAME EMAIL`
12creates a new configuration with your identity without replacing an existing
13file.
15This document describes the language itself — its values, syntax, semantics,
16and the builtins — with examples throughout. Every example shows an expression
17and what it evaluates to, exactly as `j` prints it.
19> **A note on the examples.** Where an example evaluates to a `Repo` (the
20> repository), the output depends on the repository state, so those examples
21> are described rather than shown literally. Everything else is shown exactly.
23---
25## 1. The big picture
27An expression run of `j` looks like this:
291. Read the expression (from the command line or stdin).
302. Snapshot the working directory for evaluation.
313. Build a value `repo` describing the repository, focused on the
32 working-copy commit.
334. Evaluate the expression.
345. If the result is a function, apply it to `repo`.
356. If the final value is a `Repo` that differs from the one loaded, persist
36 the difference. An unchanged `Repo` saves nothing; other values print
37 without saving.
39So the most common thing you write is an **edit** — a function `Repo -> Repo`
40(or a value that becomes one). `describe "wip"` is an edit. `new` is an edit.
41`squash` is an edit. Composing edits with `.` builds bigger edits.
43The repository is applied **implicitly**: if your expression evaluates to a
44function, that function is applied to the `Repo`. There is no `repo` variable
45in scope — write a function (an edit, a revset, or a lambda) and it receives
46the repository. `length . commits . top` is a function `Repo -> Int`, so it
47gets applied and prints the count.
49```
50j 'describe "wip"' -- set the message of the focused commit
51j 'new' -- start a new commit on top
52j 'describe "wip" . new' -- start a new commit, then describe it
53```
55A result that is plain data is printed and persists nothing:
57```
58j 'length . commits . top' -- how many commits are visible?
59j 'tree' -- print the history tree (a Text)
60```
62---
64## 2. Values
66There are eight kinds of value.
68| kind | example | notes |
69|---|---|---|
70| `Int` | `42`, `0 - 7` | arbitrary-precision integer |
71| `Text` | `"hello"` | Unicode string |
72| `Bool` | `true`, `false` | |
73| list | `[1 2 3]` | finite, ordered |
74| record | `{ a = 1, b = "x" }` | unordered named fields |
75| function | `\x -> x + 1` | closure, builtin, or partial application |
76| `Id` | `@wqzt` | a jj change id |
77| `Blob` | `blob "…"` | the content of one file |
78| `Shape` | `Int`, `Commit` | a type name used as a value |
80### Integers
82Integers are arbitrary precision — they never overflow.
84```
85999999999999 * 999999999999
86 => 999999999998000000000001
87```
89There is no unary minus. Write `0 - 5` for negative five. A negative integer
90renders as `(0 - 5)` so that it round-trips:
92```
930 - 5
94 => (0 - 5)
95```
97### Text
99Text is a Unicode string in double quotes, with escapes `\"`, `\\`, `\n`,
100`\t`, `\r`.
102```
103"hello world"
104 => "hello world"
105"line one\nline two"
106 => "line one\nline two"
107```
109### Booleans
111`true` and `false`. Conditions in `if`, and the operands of `&&` and `||`,
112must be `Bool` — anything else is a crash (see §8).
114### Lists
116A list is written with square brackets. Elements are separated by
117**whitespace**, not commas.
119```
120[1 2 3]
121 => [1 2 3]
122["a" "b" "c"]
123 => ["a" "b" "c"]
124[] -- the empty list
125 => []
126```
128Each element is an atom (with optional selectors and updates). An application
129or operator expression as an element must be parenthesised:
131```
132[(1 + 2) (3 * 4)]
133 => [3 12]
134[("a" ++ "b") "c"]
135 => ["ab" "c"]
136```
138### Records
140A record is an unordered set of named fields, written with braces. Fields are
141separated by commas.
143```
144{ name = "j", version = 1 }
145 => { name = "j", version = 1 }
146```
148Records are **structural**: a record is whatever fields it has. There is no
149record *type* you must declare. Two records are equal if they have the same
150field names with equal values:
152```
153{ a = 1, b = 2 } == { b = 2, a = 1 }
154 => true
155```
157Select a field with `.name`:
159```
160{ name = "j", version = 1 }.version
161 => 1
162```
164Update fields with `record { field = value, … }`. Update returns a copy and
165**never adds fields** — updating a field that isn't there is a crash:
167```
168let r = { a = 1, b = 2 } in r { b = 99 }
169 => { a = 1, b = 99 }
170```
172> **The record-literal gotcha.** Postfix selectors and updates bind tighter
173> than application. So `f r { a = 1 }` means `f (r { a = 1 })` — the braces
174> attach to `r`, not to `f`. A record *literal* passed as an argument after
175> another atom must be parenthesised:
177> ```
178> show ({ a = 1 }) -- OK
179> extract Int ({ a = 1 }) -- OK
180> (\r -> r.name) ({ name = "j" }) -- OK
181> ```
183> The same applies inside lists: a record literal that follows another
184> element is parenthesised. Written side by side, the second record updates
185> the first, so `[{ id = 1 } { id = 2 }]` is the one-element list
186> `[{ id = 2 }]`:
188> ```
189> [({ id = 1 }) ({ id = 2 })] -- OK: two records
190> [{ root = c, children = [] }] -- OK: the only element
191> ```
193> A line break stops this: a `.name` or `{` that starts a line never attaches
194> to what came before, so `r` with `.a` on the next (indented) line is
195> `r (.a)`, not `r.a`. Keep a selector or update on the line where the
196> expression it applies to ends.
198### Functions
200A function is made with a lambda: `\x -> body`. Multiple parameters are
201curried: `\x y -> body` takes two.
203```
204(\x -> x + 1) 5
205 => 6
206(\x y -> x - y) 10 3
207 => 7
208```
210A lambda with `_` ignores the parameter:
212```
213(\_ -> 42) 99
214 => 42
215```
217Functions are first-class: they can be passed, returned, stored in lists, and
218partially applied.
220```
221let inc = (+ 1) in inc 41
222 => 42
223map (\x -> x * 2) [1 2 3]
224 => [2 4 6]
225```
227### Ids
229An `Id` is an opaque jj change id. A literal `@kpqx` (letters `k`–`z`) refers
230to an existing commit by unique prefix. The bare token `@` mints a fresh,
231distinct id each time it is evaluated (see §7).
233### Blobs
235A `Blob` is the content of one file, either resolved or unresolved (a
236conflict). You rarely write them directly; you get them from a commit's
237`files`. It carries the file kind as well as its bytes. `blob "…"` builds a
238resolved regular file from text; `text` reads it back:
240```
241text (blob "file content")
242 => "file content"
243unresolved (blob "x")
244 => false
245```
247### Shapes
249A type name used in an expression is a `Shape` value — `Int`, `Text`,
250`Commit`, and so on. Shapes are mostly used with `extract` (§9.4).
252---
254## 3. Definitions and scope
256### `let`
258`let` introduces local bindings. Bindings on one line are separated by `;`;
259across lines they line up by column.
261```
262let x = 10 in x * 2
263 => 20
265let square = \n -> n * n in square 9
266 => 81
268let a = 1; b = 2 in a + b
269 => 3
270```
272All the bindings of one `let` block are **mutually recursive**, so you can
273write recursive and mutually recursive functions:
275```
276let even = \n -> if n == 0 then true else odd (n - 1)
277 odd = \n -> if n == 0 then false else even (n - 1)
278in even 10
279 => true
280```
282> **Layout in `let` blocks.** After `let`, the column of the first token opens
283> the block. A later line whose first token is in *exactly that column* starts
284> a new binding; a line indented further continues the current one. So the
285> `even`/`odd` example above works because `odd` lines up with `let`'s first
286> binding column.
288Because evaluation is strict (call by value), a non-function binding is
289evaluated immediately, though after the bindings it needs, whatever order
290they are written in: `let a = b; b = 5 in a` is 5. Bindings that need each
291other's values, as in `let a = b; b = a in a`, crash; recursion works through
292*functions*, whose bodies aren't evaluated until applied.
294### No shadowing
296A `let` binding or lambda parameter may **not** reuse a name that's already
297bound in an enclosing scope (including top-level definitions and builtins).
298This is an error, not silent shadowing:
300```
301let x = 1 in let f = \x -> x in f 2 -- parse error: `x` is already bound
302```
304The one exception is inside `config.j`: a binder there may reuse the name of
305one of the config's own top-level definitions, as the reference config's
306`newCommit = \files -> …` does, and within that binder's scope the name means
307the binder. Builtin names and names bound by an enclosing lambda or `let` are
308still refused there.
310### Lexical scope
312Names resolve lexically: innermost `let` bindings, then lambda parameters,
313then top-level definitions and builtins. Closures capture their defining
314environment:
316```
317let x = 100 in let f = \y -> x + y in f 5
318 => 105
319```
321Field names are *not* in any namespace — they exist only after `.`, in record
322literals, and in record updates. So a field can be called `id` even though
323`id` is the identity function.
325---
327## 4. Operators
329The operator set is fixed — you cannot declare new ones. Precedence and
330associativity:
332```
333infixr 9 . -- composition (tightest)
334infixl 7 *
335infixl 6 + -
336infixr 5 ++ ::
337infix 4 == /= < <= > >= -- non-associative
338infixr 3 &&
339infixr 2 ||
340infixl 0 or -- loosest
341```
343```
3442 + 3 * 4
345 => 14 -- * binds tighter than +
34720 - 5 - 3
348 => 12 -- - is left-associative
3501 :: 2 :: []
351 => [1 2] -- :: is right-associative
3531 == 1 || 1 == 2 && 1 == 3
354 => true -- && binds tighter than ||
355```
357Non-associative operators (`==`, `<`, …) cannot be chained; use parentheses:
359```
3601 < 2 < 3 -- parse error: `<` is non-associative
361(1 < 2) == true -- OK
362```
364### Sections and operators as functions
366Any operator can be used as a function, or partially applied as a *section*:
368```
369(+) -- the addition function
370(+ 1) 5 -- right section: \x -> x + 1
371 => 6
372(1 +) 5 -- left section: \x -> 1 + x
373 => 6
374map (2 *) [1 2 3]
375 => [2 4 6]
376```
378### The selector as a function
380A bare `.name` is the accessor function `\x -> x.name`:
382```
383.name ({ name = "j" })
384 => "j"
385map (.id) [({ id = 1 }) ({ id = 2 })]
386 => [1 2]
387```
389(Parenthesise `.id` when it follows a function name, just like a record
390literal — otherwise the selector attaches to that name.)
392---
394## 5. Control flow
396### `if`
398```
399if 1 < 2 then "yes" else "no"
400 => "yes"
401```
403The condition must be a `Bool`. `if 1 then …` is a crash.
405### `or`: catching crashes
407`a or b` evaluates `a`. If `a` **crashes**, the crash is discarded and `b` is
408returned instead. Otherwise `a` is the result (and `b` is not evaluated) —
409unless `a` is a function, in which case the two are combined pointwise (see
410below).
412```
413head [] or 42
414 => 42 -- head [] crashes, so 42
4161 or head []
417 => 1 -- 1 is fine; head [] never runs
419"x" or crash "boom"
420 => "x"
421```
423**The function-lifting rule** is what makes `or` useful for edits. A function
424value cannot crash until it is applied, so `edit or id` would catch nothing if
425`or` just returned the function. Instead, when `a` is a function, `b` is
426evaluated too, and if it's also a function the result is `\x -> a x or b x`:
428```
429(\x -> x.missing) or (\x -> 0)
430 => a function that yields 0 for any input
432-- the classic: try an edit, keep the repository if it fails
433edit or id
434```
436This is the whole error-handling model: there are no exceptions to declare,
437no `Maybe` — just crash, and catch with `or`.
439---
441## 6. Working with lists
443```
444head [5 6 7] => 5
445tail [5 6 7] => [6 7]
446last [5 6 7] => 7
447nth 1 [10 20 30] => 20 -- 0-based
448take 2 [1 2 3 4] => [1 2]
449drop 2 [1 2 3 4] => [3 4]
450length [1 2 3] => 3
451null [] => true
452member 3 [1 2 3] => true
4531 :: [2 3] => [1 2 3] -- cons
454```
456`take` and `drop` clamp to the list length:
458```
459take 99 [1 2] => [1 2]
460drop 99 [1 2] => []
461```
463`head`, `tail`, `last`, and out-of-range `nth` crash on empty/out-of-bounds:
465```
466head [] -- CRASH: head: empty list
467nth 5 [1 2 3] -- CRASH: nth: index 5 out of range
468```
470### `map`, `filter`, `foldl`
472```
473map (\x -> x * 2) [1 2 3 4]
474 => [2 4 6 8]
476filter (\x -> x > 2) [1 2 3 4]
477 => [3 4]
479foldl (+) 0 [1 2 3 4 5]
480 => 15
481```
483`foldl f z xs` is a left fold: `foldl f z [x y] = f (f z x) y`, and `z` for
484the empty list.
486```
487foldl (\a -> \b -> a - b) 0 [1 2 3]
488 => (0 - 6) -- ((0 - 1) - 2) - 3
489```
491### `range`
493```
494range 1 6 => [1 2 3 4 5] -- [a (a+1) … (b-1)]
495range 5 1 => [] -- empty if b <= a
496```
498### `concat` and `++`
500These two dispatch on the kind of the argument — they work on lists *and* on
501text.
503```
504[1 2] ++ [3 4] => [1 2 3 4] -- list concatenation
505"foo" ++ "bar" => "foobar" -- text concatenation
507concat [[1 2] [3] []] => [1 2 3] -- flatten [[a]] -> [a]
508concat ["foo" "bar"] => "foobar" -- join [Text] -> Text
509```
511`concat []` crashes — there is nothing to dispatch on.
513---
515## 7. Working with text
517```
518splitOn "," "a,b,c" => ["a" "b" "c"]
519splitOn "/" "src/lexer.rs" => ["src" "lexer.rs"]
520splitOn "/" "" => [""]
521startsWith "he" "hello" => true
522endsWith "rs" "lexer.rs" => true
523```
525`splitOn` with an empty separator crashes.
527---
529## 8. Equality, comparison, and crashes
531`==` is structural on ints, texts, bools, lists, and records; identity on
532`Id`; content-and-mode on `Blob`. `/=` is its negation.
534```
5352 == 2 => true
536"a" == "a" => true
537[1 2] == [1 2] => true
538{ a = 1 } == { a = 1 } => true
539```
541Comparing two **functions** crashes — there is no meaningful equality on
542closures.
544Ordering operators (`<`, `<=`, `>`, `>=`) are defined on `Int` only and crash
545otherwise.
547### Crashes
549`crash msg` aborts evaluation with `msg`. Every runtime error the interpreter
550detects is also a crash: unbound name, wrong kind of value, missing field,
551out-of-range index, `head []`, comparing functions, a non-`Bool` condition,
552arity/kind errors in builtins. All of these are catchable with `or`.
554```
555crash "my message"
556 -- CRASH: my message
558(crash "boom") or "recovered"
559 => "recovered"
560```
562Parse errors, configuration errors, and `Id`-resolution failures happen
563*before* evaluation and are **not** catchable.
564CLI diagnostics identify the source and line for syntax errors and offer
565quoting, missing-argument, or spelling hints where available. Configuration
566errors name the file to repair; `j help config` works even with broken config.
568---
570## 9. Functions in depth
572### Currying and partial application
574Every function is curried. Applying a function to fewer arguments than it
575takes yields a partial application, which is itself a value:
577```
578let add = \x y -> x + y in (add 3) 4
579 => 7
581map (nth 0) [["a" "b"] ["c" "d"]]
582 => ["a" "c"]
583```
585### Composition
587`.` composes two functions: `(f . g) x = f (g x)`. It is right-associative
588and binds very tightly.
590```
591((\x -> x + 1) . (\x -> x * 2)) 5
592 => 11 -- (5 * 2) + 1
594((\x -> x * 2) . (\x -> x + 1)) 5
595 => 12 -- (5 + 1) * 2
596```
598Composition is how edits are chained. `describe "wip" . new` first makes a
599new commit, then describes it.
601```
602let compose = \f g -> \x -> f (g x) -- `.` is just this, built in
603```
605### `id` and `const`
607```
608id 42 => 42
609(id . id) 7 => 7
610const 5 99 => 5 -- const a b = a
611```
613`id` is the identity edit — `edit or id` falls back to it. `const` ignores its
614second argument.
616---
618## 10. Ids, blobs, and shapes
620### Existing ids: `@prefix`
622`@wqzt` refers to the commit whose change id begins with `wqzt`, resolved
623*before* evaluation. If the prefix matches no commit, or more than one, the
624program stops with an error listing the candidates — nothing is evaluated.
626```
627by @wqzt -- an edit: refocus the repository on that commit
628```
630After resolution the literal is an ordinary full `Id`, so `==` on ids is pure
631identity.
633### New ids: `@`
635The bare token `@` mints a fresh, distinct `Id` every time it is evaluated.
636In a lambda body (evaluated per application) each call mints a new id; that is
637how `newCommit` gives each new commit a unique id.
639### Blobs and `unresolved`
641```
642blob "content" -- a resolved regular-file blob
643text (blob "content") => "content"
644unresolved (blob "x") => false
645```
647`unresolved b` is `true` iff the blob is a conflict (produced by a clashing
648`replay`). `text` on an unresolved blob yields its conflict-marker rendering.
650`blobHex kind hex` constructs a resolved blob with arbitrary bytes and a
651file kind: `"regular"`, `"executable"`, or `"symlink"`. Its second argument
652is an even-length hexadecimal text, accepting either letter case. Unknown
653kinds and invalid hex crash. For example,
654`text (blobHex "regular" "6869")` is `"hi"`. `text` crashes on non-UTF-8
655content; persisting a symlink requires its target to be valid UTF-8 too.
657### Shapes and `extract`
659A type name in an expression is a `Shape`. `extract S v` returns every
660subvalue of `v` that is an `S`, walking lists in order and records in
661ascending field-name order, descending into elements and fields (but not into
662functions or blobs).
664```
665extract Int ({ a = 1, b = 2 })
666 => [1 2]
668extract Int ({ a = 1, b = [2 "x" ({ c = 3 })] })
669 => [1 2 3]
671extract Text ({ a = "x", b = [1 "y"] })
672 => ["x" "y"]
673```
675A record is an `S` if its field set is *exactly* `S`'s. This is how
676`extract Commit repo` finds every commit in a repository, however deeply
677nested.
679---
681## 11. Contracts
683A function that has a **signature** (a builtin, or a definition with one in
684`config.j`) is checked on every application: each argument against its
685parameter type as it is supplied, and the final result against the result
686type.
688Infix `&&` and `||` require their declarations in `config.j` and check the
689contracts of each evaluated operand and the result. Short-circuiting skips
690both evaluation and checking of the right operand.
692The check is one level deep — it never inspects inside a list or a record's
693field values (that would make every call cost the size of the value).
695| declared type | check |
696|---|---|
697| `Int`, `Text`, `Bool`, `Id`, `Blob`, `Shape` | the value is of that kind |
698| `[T]` | the value is a list (elements not inspected) |
699| `{ a : T, … }` or a shape name | a record with exactly those field names |
700| `A -> B` | the value is a function |
701| a type variable | anything |
702| an alias (`Edit`, `Revset`, `Snapshot`) | what it stands for; function aliases are unfolded |
704A violation is a crash, catchable by `or`:
706```
707describe 3
708 -- CRASH: contract: describe expected Text as argument 1, got Int
710nth "x" [1]
711 -- CRASH: contract: nth expected Int as argument 1, got Text
712```
714Lambdas without a signature, and the command-line expression itself, are
715unchecked except through the functions they call.
717---
719## 12. `show`: rendering values as text
721When it succeeds, `show v` renders a value as an expression in the language.
722Except for closures and unresolved blobs, the result parses and evaluates
723back to an equal value. Since `show` returns a `Text`, and a top-level `Text`
724prints raw, `j show …` is how you inspect values.
726Regular blobs with UTF-8 content render as `blob "…"`. Other resolved blobs
727render as `blobHex "kind" "…"` with lowercase hexadecimal bytes, preserving
728binary content and executable or symlink kinds. If an older custom config
729lacks `blobHex : Text -> Text -> Blob`, `show` crashes with instructions to
730add it when such a value is encountered. Existing configs are not modified.
732```
733show 42 => "42"
734show "hi" => "\"hi\""
735show [1 "a" true] => "[1 \"a\" true]"
736show ({ x = 1 }) => "{ x = 1 }"
737show (0 - 5) => "(0 - 5)"
738show [[1 2] [3]] => "[[1 2] [3]]"
739```
741Functions render readably:
743```
744show (map) => "map"
745show (+) => "(+)"
746show (describe "wip") => "describe \"wip\""
747show (\x -> x + 1) => "\\x -> x + 1"
748```
750Output longer than 80 columns breaks across lines (one element per line,
751two-space indented, leading commas for records) — and still round-trips.
753---
755## 13. The `Repo` value and navigation
757The repository appears as a value of shape:
759```
760Repo = { root : Commit, children : [Subtree], context : [Frame] }
761Commit = { files : Snapshot, message : Text, labels : [Text], id : Id }
762Snapshot = [Entry]
763Entry = { path : Path, content : Blob }
764Subtree = { root : Commit, children : [Subtree] }
765```
767`Repo` is a **zipper**: `root` is the focused commit, `children` its children,
768and `context` the path back up to the top of history. You almost never build
769one by hand; you navigate and edit the one you're given.
771The reference `config.j` defines the whole vocabulary on top of this. A tour:
773```
774here -- the focused commit's id, as a Revset
775top -- refocus at the top of history
776tip -- refocus at the tip
777prev -- to the parent (crashes at the top)
778next -- to the only child (crashes if not exactly one)
779up -- like prev, the raw zipper move
780by @wqzt -- refocus on a specific commit
781tree . goto %main -- inspect main without moving the working copy
782new . goto %main -- start a mutable child on main
783parents -- the focus's parents, as a Revset
784kids -- its children
785ancestors -- it and its ancestors
786descendants -- it and its descendants
787siblings -- the other children of its parent
788all -- every visible commit
789```
791Edits — functions `Repo -> Repo`:
793```
794new -- a new empty child of the focus
795save -- record file edits without changing the description
796describe "wip" -- set the focus's message
797squash -- fold the focus into its parent
798abandon -- drop the focus; children join the parent
799split (\p -> p == ./a.txt) -- split the focus by a fileset
800rebase %main -- move the focused subtree onto main
801pick @wqzt -- apply a commit's change elsewhere as a new child
802backout @wqzt -- a new child undoing a commit's change
803contract everything -- collapse a subtree (see config.j)
804```
806And the inspections:
808```
809tree -- the history as a tree (a Text)
810status -- { id, message, labels, changed, conflicts, unrecorded }
811unrecorded -- paths edited since the last recording
812preview squash -- validate and show the resulting tree without saving
813review -- the focus's changes as one page of diffs
814files -- the focus's snapshot (its files)
815commits . top -- every commit, preorder
816```
818### A realistic session
820```
821-- start some work
822j 'new'
823-- (edit files in your editor)
824j 'describe "add the lexer"'
826-- more work on top
827j 'new'
828-- (edit more files)
829j 'describe "add the parser"'
831-- look at what you have
832j 'tree'
834-- the second commit was a mistake; fold it into the first
835j 'squash'
837-- squash leaves a fresh empty child; move back and rename the combined commit
838j prev
839j 'describe "add the front end"'
841-- undo the rename, the focus move, then the squash
842j undo
843j undo
844j undo
846-- move back to the first commit to fix something
847j 'prev'
848```
850Because edits compose with `.`, multi-step operations are single expressions:
852```
853-- start a new commit on top of main and describe it, in one step
854j 'describe "hotfix" . new . (goto %main)'
856-- describe every commit below the focus
857j 'forEach descendants (describe "wip")'
858```
860---
862## 14. `diff`, `difft`, and `treeWith`
864`diff a b` produces a unified diff (three lines of context, no headers)
865between two blobs' text:
867```
868diff (blob "a\nb\n") (blob "a\nc\n")
869 => "@@ -1,2 +1,2 @@\n a\n-b\n+c\n"
870```
872`difft p a b` renders the comparison with **difftastic** when it is on `PATH`
873(the Nix package provides it). When it is absent, the builtin returns a
874unified diff with path headers, or a summary for binary changes. `review`
875uses `difft` per changed path; other errors starting the tool still fail.
877`treeWith options repo` renders the history tree with an options record:
879```
880treeWith ({ detail = 2, margin = true, elide = false, icons = false, color = "auto", lanes = 4, author = false, date = false, files = false })
881```
883The reference config provides `tree`, `treeCompact` (folded runs and distant
884subtrees), `treeFull` (ages and authors in the margin), and `treeData` (extra
885date, files-changed, and author columns). Branches beyond `lanes` keep their
886state marker beside `»`; increase `lanes` to show more rails. Long file paths
887in detail rows wrap to fit the terminal.
889---
891## 15. Writing your own definitions
893Everything `j` knows is defined in `config.j` — builtins are *declared* there,
894and the whole vocabulary (`new`, `describe`, `squash`, `tree`, …) is *written*
895there in the language. You can add your own. Any definition whose value is an
896`Edit` is a new command.
898```
899-- in config.j:
901-- squash the whole stack into one commit called "release"
902release : Edit
903release = describe "release" . (contract everything)
905-- count the commits ahead of main
906ahead : Repo -> Int
907ahead = \repo -> length (descendants repo) - 1
908```
910Then:
912```
913j release
914j 'show ahead'
915```
917A definition with a signature is contract-checked (§11); one without is not
918(except through what it calls). A signature with no definition declares a
919builtin that `j` implements.
921---
923## 16. Laws worth knowing
925These identities hold for the reference `config.j` and are a good mental model
926(`=` means "equal results, or both crash"):
928```
929prev (next r) = r (when r has exactly one child)
930next (prev r) = r (when r is an only child)
931top (top r) = top r
932(abandon . new) r = r (up to the new focus's minted id)
933rebase parents r = r
934goto here r = r
935at rs id r = r
936(e or id) r = r (when e r crashes)
937edit or id -- the universal fallback
938replay b { from = b, to = x } = x
939invert (invert ch) = ch
940concat [xs] = xs
941```
943They capture the intent: navigation is reversible where it's defined, `id` is
944a neutral edit, `or` recovers from failure, and `replay`/`invert` behave like
945the algebra they are.
947---
949## 17. Quick reference
951**Special forms:** `if … then … else …`, `let … in …`, `\x -> …`, `or`.
953**Operators:** `.` `*` `+` `-` `++` `::` `==` `/=` `<` `<=` `>` `>=` `&&`
954`||`, and sections `(op e)` / `(e op)` / `(op)`.
956**Core builtins:** `id` `const` `crash` `show` `not` `.`
958**Lists:** `map` `filter` `foldl` `head` `tail` `last` `nth` `take` `drop`
959`length` `null` `member` `range` `::` `concat` `++`
961**Text:** `splitOn` `startsWith` `endsWith` `concat` `++`
963**Blobs/repos:** `blob` `blobHex` `text` `unresolved` `replay` `by` `meta` `extract`
964`diff` `difft` `treeWith` `validate`
966**Reserved commands** (not expressions): `help [NAME]` `commands [QUERY]`
967`setup [NAME EMAIL]` `init` `clone URL [DIR]` `remote URL`
968`fetch` `push EXPR` `undo` `redo` `ops`.
970Literal reserved-command arguments preserve shell quoting boundaries, as in
971`j clone URL 'my project'`. A whole reserved command passed as one string or
972on stdin is split on whitespace. Expression arguments still use language
973syntax: quote the whole expression, as in `j 'describe "my change"'`.