# Effects and capabilities


In most languages any function can open a file, read the clock, or call the
network, and you find out by reading its body. In Xo the signature says it.
Two ideas work together:

- an **effect** is a name in the `uses` clause (`fs.read`, `clock`, `stdio`);
- a **capability** is a value (`Fs`, `Clock`, `Stdio`) that grants the
  permission. `main` receives them all in `os: Os`; everything else must be
  passed one.

## Pure, and what it costs to stop being pure

`caps-basics/main.xo`

```xo
/// Pure: no `uses` clause, so it cannot touch the outside world.
pub fn parse_pair(line: Str) -> (Str, Str)? {
  let (k, v) = line.cut("=")?
  (k.trim(), v.trim())
}

/// Reads one file: the signature says so, and the caller must hand over an `Fs`.
pub fn count_keys(fs: Fs, path: Str) -> Int ! FsErr uses fs.read {
  let text = fs.read_text(path)?
  var n = 0
  for line in text.lines() {
    if parse_pair(line) is Some(_) {
      n += 1
    }
  }
  n
}

/// Reads the clock and prints: both effects are in the signature.
pub fn stamp(clock: Clock, out: Stdio, msg: Str) uses (clock, stdio) {
  out.println("${clock.now().unix()}: ${msg}")
}

fn main(os: Os) {
  os.stdio.println("pure: ${parse_pair("port = 8080"):?}")
  match count_keys(os.fs, "no-such-file.conf") {
    Ok(n) => os.stdio.println("keys: ${n}")
    Err(e) => os.stdio.println("could not read: ${e.display()}")
  }
}

test "count_keys reads the fake file system" {
  os.fs.write("app.conf", b"a = 1\nb = 2\nnot a pair\n")?
  expect(count_keys(os.fs, "app.conf")?) == 2
}

test "stamp reads the virtual clock" {
  stamp(os.clock, os.stdio, "hello")
  expect(os.stdio.output()) == "1767225600: hello\n"
}
```


Output:

```text
pure: Some(("port", "8080"))
could not read: no-such-file.conf: no such file or directory
```



- `parse_pair` has no `uses` clause, so it cannot reach the outside world.
  Calling something that does is a compile error.
- `count_keys` takes an `Fs` and declares `uses fs.read`. The effect is the
  promise, the `Fs` argument is the means.
- `stamp` declares two effects, `clock` and `stdio`, in one `uses (...)` list.
- The tests run against a fake `os`: an in-memory file system, a virtual clock
  fixed at 1767225600, and captured output. No test touches the disk.

Forgetting the declaration is caught at compile time, with the effect named:

```xo
pub fn greet(out: Stdio, name: Str) {
  out.println("hello, ${name}")
}

fn main(os: Os) { greet(os.stdio, "Ada") }
```

[Try it in the playground](/play/#example=mistakes/effect)
{.play-link}

`xo check` says:

```text
main.xo:1:8: error XO0701: `greet` is pub with no `uses` clause, so it is pure, but performs `stdio`
  note: declare them: `uses stdio` (`xo fix` writes it)
  note: main.xo:2:3: `Stdio.println` uses `stdio` here
  fix: add the clause `uses stdio`
1 module(s), 1 error(s), 0 warning(s)
```


## Handing out less

A capability can be narrowed before it is passed on. A function that only
needs to read gets a read-only view; one that writes under a directory gets a
sub-directory.

`caps-narrow/main.xo`

```xo
/// A report writer should only be able to write under one directory.
pub fn save_report(out: Fs, name: Str, body: Str) -> () ! FsErr uses fs.write {
  out.write("${name}.txt", body.bytes())?
}

/// A previewer should only read.
pub fn preview(src: Fs, name: Str) -> Str ! FsErr uses fs.read {
  src.read_text("${name}.txt")?
}

fn main(os: Os) {
  let reports = os.fs.sub("/tmp").read_only()
  match save_report(reports, "xo-book-demo", "hello") {
    Ok(_) => os.stdio.println("saved")
    Err(e) => os.stdio.println("refused: ${e.display()}")
  }
}

test "a read only capability refuses a write" {
  let reports = os.fs.sub("/reports").read_only()
  match save_report(reports, "q1", "numbers") {
    Ok(_) => fault("the write should have been denied")
    Err(e) => expect(e.display()) == "q1.txt: permission denied"
  }
}

test "the same function writes through a writable one" {
  os.fs.create_dir_all("/reports")?
  let reports = os.fs.sub("/reports")
  save_report(reports, "q1", "numbers")?
  expect(preview(reports.read_only(), "q1")?) == "numbers"
}

test "paths cannot leave the sub capability" {
  let reports = os.fs.sub("/reports")
  match reports.read_text("../etc/passwd") {
    Ok(_) => fault("escaped")
    Err(Denied(_)) => ()
    Err(e) => fault("unexpected: ${e.display()}")
  }
}
```


Output:

```text
refused: xo-book-demo.txt: permission denied
```



- `os.fs.sub("/tmp")` is an `Fs` rooted at `/tmp`; `.read_only()` removes the
  write effects from what it will grant.
- `save_report` cannot tell that its `Fs` is narrowed. It still returns an
  error value, `permission denied`, instead of failing in some other way.
- A path that climbs out with `..` is refused too (the third test).

Because the narrowing is a value, you can audit a program by reading `main`:
whatever it never hands to a function, that function cannot use.

## Effects that pass through

A higher order function should not guess what its argument does. An effect
variable stands for "whatever `f` does":

`caps-fx/main.xo`

```xo
/// Runs `f` twice. The effect variable `fx` is whatever `f` does, so `twice` does the same.
pub fn twice[fx: Effects](f: fn() -> () uses fx) -> () uses fx {
  f()
  f()
}

fn shout(out: Stdio, word: Str) uses stdio {
  out.println("${word.to_upper()}!")
}

fn main(os: Os) {
  // `twice` of a pure closure is pure; `twice` of a printing closure prints
  twice(fn() { let _ = "x".to_upper() })
  twice(fn() { shout(os.stdio, "hi") })
}

test "twice passes the effects through" {
  twice(fn() { shout(os.stdio, "go") })
  expect(os.stdio.output()) == "GO!\nGO!\n"
}
```


Output:

```text
HI!
HI!
```



`twice[fx: Effects]` is pure when given a pure closure and prints when given a
printing one, so one definition serves both.

## What to take away

- Read a signature's `uses` clause to know what a function can reach.
- Pass the narrowest capability that does the job.
- Test with the fake `os`: the fakes are the same types as the real ones.

Next: [Error handling](/docs/book/02-error-handling/).

