This is a Unison transcript file. It's used for creating typechecked documentation, test cases, and scripts.
It's just a markdown file that does some special interpretation of certain fenced code blocks. It can be passed to ucm
as a command line argument. ucm
will run through the file change events and commands in the file in order and output a new .md
file with the responses to these inputs. It exits when it's done, so this can also be used for scripting.
---
filename: scratch.u
---
x = 1 + 1
The above will create the file scratch.u
with the given contents, parse and typecheck it, and insert the ucm
output into the resulting markdown, as a new fenced code block, immediately following the unison
fenced block. If you leave off the frontmatter with the filename, that's okay too, no file will be created. If the file doesn't parse or typecheck, ucm
exits with an error.
If you are instead expecting an error and want to show the resulting error message and continue, use unison:error
as the code fence:
x = 1 + "hi" -- thankfully does not typecheck
Another fenced code type is ucm
(ucm:error
if you are expecting an error):
.> view base.List.map
.> execute IO.putText "hello, world!!"
This will run the ucm
command and show its output in a new fenced code block immediately below. A ucm
fenced block can have multiple commands, in which case the output is inserted after each command.
You can elide the cd
/namespace
commands and just directly indicate the namespace with the prompt, for instance:
.foo> find
.bar> find
This will output the command sequence cd .foo
, find
, cd .bar
, find
.
Sounds good, now what about creating test cases? We provide a couple new commands:
assert.execute expr
exitsucm
with failure ifexecute expr
fails or evaluates to false.assert.test
exitsucm
with failure if thetest
command returns a failing statusassert.todo
exitsucm
with failure if thetodo
command returns anything to doassert.view foo
exitsucm
with failure ifview
command gives no results- ... can add to this as needed
.> assert true
.> assert.test
It might not be obvious how to use the above to construct test cases, so I thought I'd give a few examples. Suppose I'd like to check that fork
creates a copy of a namespace. To make the transcript pretty self-contained, I'll create a namespace with some definitions in it, then fork
that, cd
into it, and verify that viewing one of the definitions works.
ns0.x = 42
ns0.y = 19
.> add
.> fork ns0 ns1
.ns1> assert.view x
.ns1> assert.view y
In the absence of a full-blown codebase API accessible from Unison programs, there may be some creativity needed to arrange things so that the transcript fails in a way that indicates what you want. The above test is a bit weak, it would be nice if we could assert that the x
in the fork is the same as the x
in ns0
. How could we do that? Easy: just write a Unison test!
use .test
use .base
test> ns1.forkIsCopy1 = (.ns0.x == .ns1.x) |> expect |> run
test> ns1.forkIsCopy1 = (.ns0.y == .ns1.y) |> expect |> run
.> add
.> assert.test
Now we're actually testing that the values are as expected in the fork.
I'd say no, or maybe it could be a config option. I'd rather host the markdown files on a site that can render
unison
fenced blocks, like a github pages site with some custom syntax highlighting.