`cmark`’s pretty-printer matches our output pretty well, with a few differences: - it puts a space between the fence and the info string for in code blocks; - it prefers `-` over `*` for bulleted lists (as do I) and it indents them; - it `\`-escapes certain chars very conservatively; - it prefers indented/unfenced code blocks if there is no info string; and - it prefers `*` over `_` (unlike any sane person). This also shows how the change fixes a number of issues: - fix2158-1.output.md also illustrates how this change fixes #1809; - alias-many.output.md and input-parse-errors.output.md show how fenced code blocks without an info string would use the beginning of the content as the info string; - transcripts-round-trip/main.output.md shows how output blocks for generated `unison` stanzas (which could contain nested fenced blocks) might not have long-enough fences; and - error-messages.output.md and generic-parse-errors.output.md show how Unison errors were reported on the wrong line number (and thus the printed error lines were also incorrect).
3.0 KiB
This transcript shows how suffix-based name resolution works when definitions in the file share a suffix with definitions already in the codebase.
Setup
As setup, we'll add a data type Day
and a definition foo.bar : Nat
.
unique type Day = Sun | Mon | Tue | Wed | Thu | Fri | Sat
foo.bar : Nat
foo.bar = 23
Suffix-based name resolution prefers to use names locally defined in the current file, then checks for matches in the codebase. Here are the precise rules, which will be explained below with examples:
- If a symbol,
s
, is a suffix of exactly one definitiond
in the file, thens
refers tod
. - Otherwise, if
s
is a suffix of exactly one definitiond
in the codebase, thens
refers tod
. - Otherwise, if
s
is a suffix of multiple definitions in the file or the codebase, then (at least for terms) type-directed name resolution will be attempted to figure out which definitions
refers to.
Example 1: local file term definitions shadow codebase term definitions
This should typecheck, using the file's bar : Text
rather than the codebase foo.bar : Nat
:
use Text ++
bar : Text
bar = "hello"
baz = bar ++ ", world!"
Example 2: any locally unique term suffix shadows codebase term definitions
This should also typecheck, using the file's oog.bar
. This shows you can refer to a definition in the file by any suffix that is unique to definitions in the file (even if that suffix may match other definitions in the codebase). See example 4 below for overriding this behavior.
use Text ++
oog.bar = "hello"
baz = bar ++ ", world!"
This subtle test establishes that we aren't using type-directed name resolution (TDNR) for the local term references in the file. If this were using TDNR, it would fail with an ambiguity as there's nothing that pins down the expected type of bar
here:
use Text ++
oog.bar = "hello"
baz = (bar, 42)
This subtle test establishes that locally introduced variables (within a function, say) correctly shadow definitions introduced at the file top level:
use Text ++
oog.bar = "hello"
baz : [Int] -> ([Int], Nat)
baz bar = (bar, 42) -- here, `bar` refers to the parameter
Example 3: Local type and constructor definitions shadow codebase definitions
This should also typecheck, using the local Sun
, and not Day.Sun
which exists in the codebase, and the local Day
, not the codebase Day
.
structural type Zoot = Zonk | Sun
structural type Day = Day Int
use Zoot Zonk
flip : Zoot -> Zoot
flip = cases
Sun -> Zonk
Zonk -> Sun
day1 : Day
day1 = Day +1
Example 4: Refering to codebase definitions via a unique suffix
Even though local definitions are preferred, you can refer to definitions in the codebase via any unique suffix that doesn't also exist in the file.
structural type Zoot = Zonk | Sun
use Zoot Zonk
blah = cases
Day.Sun -> Day.Tue
day -> day
blah2 =
-- imports work too if you get tired of typing Day.Sun over and over
use Day Sun
cases Sun -> Wed
day -> day