Unison.IO
Tags: unison, functional, alphatest, io
Published: 2020-02-17 15:00:44

#### Before we begin!

Quick reminder that `Unison` is still in `public alpha`! 

Parts of the language, testing functionality and libraries that appear in this post might be subject to change.

In the following post I will do my best to refer to command-line user *input and output* as *I/O* when in free text mode, which also seems to be the preferred term of the Unison documentation and guide. In code snippets, the ability will be referred to as `IO` with the `base.io.` namespace omitted for brevity.

##### Problem statement: I/O operations in Unison.

I will try and frame the problem statement as a BDD scenario:
```
GIVEN An individual interested in the Unison PL
WHEN  That individual investigates I/O
THEN  They have an brief document available to guide them
```

I will concede at this point that the *real* problem statement is:
```
GIVEN I have done I/O before on numerous occasions
WHEN  I need to remind myself yet again how to do it
THEN  I have my notes kept somewhere
```
😇 and 🤦‍♀️

##### Do I need to understand [abilities](https://www.unisonweb.org/docs/language-reference#abilities) in depth to do I/O?

Strictly speaking: to get acquainted with I/O you will have to get acquainted with abilities.

If all you care to do is at this point is "read some input from the console and write some output to it" an *in depth* understanding of what abilities are or how they are handled internally is *strictly* not necessary. I have however helpfully added the documentation link for your convenience, as some familiarity with the syntax and the moving parts is necessary.

##### How does `ucm` run a program?

Let's have a quick look at an excerpt of the output of the `ucm help` command:

```
$ ucm help

...
  ucm run .mylib.mymain
  Executes the definition `.mylib.mymain` from the codebase, then exits.
  
  ucm run.file foo.u mymain
  Executes the definition called `mymain` in `foo.u`, then exits.
...
```

`ucm` can be asked to `run` an existing codebase definition if pointed to an entity via its full namespace path. Furthermore the `run.file` flag type-checks the file `ucm` is pointed to and then runs the existing codebase definition.

At the time of writing for `ucm` to run an entity it must have a type of `'{ IO } ()`

##### So single ticks and curly braces?

It might look strange at first but abilities amongst other things are meant to allow "the same syntax for programs that do (asynchronous) I/O", to quote the documentation. In other languages you might see a signature like:

* `f :  Int => Unit` for a synchronous function
* `f :  Int => IO [Unit]` for a function that performs IO

One could observe that "sure `IO` involves input and output" but is that synchronous or asynchronous?

By using abilities and handlers the requirement for asynchronous context during the steps in the body of `f` would be expressed as:
* `f: Int -> {e} ()` (`e` being the empty set of abilities)
* `f: Int -> {IO} ()` (still synchronous but involves I/O)
* `f: Int -> '{IO} ()` (asynchronous and involves I/O)

While trying to not stray and discuss abilities in general, for the strict scope of I/O the last remaining bit to explain is the single tick. It denotes a [delayed computation](https://www.unisonweb.org/docs/language-reference#delayed-computations) which is the Unison way of doing asynchronous computations.

##### How can I get `IO` (the Unison ability) in my codebase ?

At the time of writing `IO` comes as part of the `ucm` `base` package that can be pulled while running `ucm`.

```
.> pull https://github.com/unisonweb/base .base
```

##### And what can `IO` do ?

A complete list of what `IO` can do can be found by means of looking under `base.io`. The focus for a simple `input-manipulate-output` program will be the two functions that allow reading and writing to the console:

```
base.io.readLine : '{IO} Text
base.io.printLine : Text ->{IO} ()
```

##### So how that documentation example?

```
use io

program : '{IO} ()
program = 'let
  printLine "What is your name?"
  name = !readLine
  printLine ("Hello, " ++ name)
```

* the function is declared as a delayed side effect `'{IO} ()`
* the body must begin with a "delayed let"
* delayed calls must be forced with `!` in this instance `!readLine`

It might seem a bit heavy to digest in one go but what you have at this point is a *program* that performs I/O by using the mechanics of the `IO` ability. The syntax is such that functions that require synchronous `IO` are called directly whereas functions that require asynchronous `IO` are forced (at the "end of the world" when the I/O is handled by the runtime).

##### Run it!

* Copy paste the code into `scratch.u`
* `add` the `program` function as an entity in your codebase (its namespace will be `.` by default, so it will live under `.program`)

Followed by one of these commands:

`ucm run.file scratch.u program`

`ucm run .program`

The output will be something along these lines:

```
$ ucm run .program

What is your name?
X 
Hello, X
```