
If you have Docker and just want to quickly try out Vary, use our Docker image. Everything is pre-installed. The container deletes itself when you exit, so nothing touches your machine. Once you feel comfortable, you can install Vary properly on [Linux](/docs/install-linux/), [macOS](/docs/install-macos/), or [Windows](/docs/install-windows/).

This walkthrough takes about five minutes. By the end you will have run a Vary program, executed tests, and seen mutation testing find a gap in a test suite.

## Start a shell

Pull the image and drop into a shell:

```text
docker run -it --rm --hostname vary --entrypoint bash ghcr.io/ccollicutt/vary:latest
```

The `--entrypoint bash` overrides the default entrypoint (which is the `vary` command) to give you a shell. The `--rm` flag removes the container when you exit. The Vary compiler, test runner, formatter, mutation engine, and REPL are all pre-installed.

Verify it works:

```text
vary@vary:/workspace$ vary --version
Vary v113-alpha.1
```

## Run hello world

The image ships with example programs in `/opt/vary/examples/`. List them:

```text
vary@vary:/workspace$ ls /opt/vary/examples/
cli_demo.vary      enums.vary         fibonacci.vary     game_of_life.vary  math_utils.vary    ...
counter.vary       factorial.vary     fizzbuzz.vary      hello.vary         tuples.vary        ...
```

Run the simplest one:

```text
vary@vary:/workspace$ vary run /opt/vary/examples/hello.vary
Hello, World!
```

That is the entire program. Vary runs top-level code directly. No `main` function, no boilerplate.

## Run FizzBuzz

```text
vary@vary:/workspace$ vary run /opt/vary/examples/fizzbuzz.vary
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
...
```

This prints the classic FizzBuzz sequence from 1 to 100. The source is 13 lines of straightforward code:

```vary
mut i = 1
while i <= 100 {
    if i % 15 == 0 {
        print("FizzBuzz")
    } elif i % 3 == 0 {
        print("Fizz")
    } elif i % 5 == 0 {
        print("Buzz")
    } else {
        print(i)
    }
    i = i + 1
}
```

If you have written Python, this should look familiar. The differences: braces instead of indentation, explicit types on function parameters, and `mut` for mutable variables.

## Run tests

Vary has a built-in test runner. The examples include test files for the Fibonacci module:

```text
vary@vary:/workspace$ vary test /opt/vary/examples/fibonacci_test.vary

Found 1 test file(s)

fibonacci_test.vary:
  PASS: fib base cases
  PASS: fib sequence
  PASS: fib larger

==================================================
Results: 3 passed, 0 failed
```

Look at what the test file contains:

```vary-snippet
from fibonacci import fib

test "fib base cases" {
    observe fib(0) == 0
    observe fib(1) == 1
}

test "fib sequence" {
    observe fib(2) == 1
    observe fib(3) == 2
    observe fib(4) == 3
    observe fib(5) == 5
    observe fib(6) == 8
}

test "fib larger" {
    observe fib(10) == 55
}
```

Tests are `test "name" { }` blocks with `observe` assertions. No test framework to install, no imports to remember. The compiler knows what tests are.

## Try mutation testing

This is where things get interesting. Tests tell you the code works. Mutation testing tells you the tests are actually checking something.

Run mutation testing against the math utils example:

```text
vary@vary:/workspace$ vary mutate /opt/vary/examples/math_utils.vary
```

The mutation engine takes the `math_utils.vary` source, makes small changes to it (swapping `+` for `-`, changing `*` to `+`, replacing return values), and reruns the tests for each change. If the tests still pass after a change, that mutant "survived," which means the tests have a gap.

The output shows a score: the percentage of mutants your tests caught. A function like `square` that returns `x * x` might have a surviving mutant where `*` becomes `+`, but only if the test does not check values where `x * x` differs from `x + x` (which is every value except 0 and 2).

You can ask the engine to explain a specific survivor:

```text
vary@vary:/workspace$ vary mutate /opt/vary/examples/math_utils.vary --why
```

This prints what changed, where, and what test you could write to catch it.

## Write your own program

Create a file right in the container:

```text
vary@vary:/workspace$ cat > /tmp/greet.vary << 'EOF'
def greet(name: Str) -> Str {
    return f"Hello, {name}!"
}

print(greet("Docker"))
print(greet("Vary"))
EOF
```

Run it:

```text
vary@vary:/workspace$ vary run /tmp/greet.vary
Hello, Docker!
Hello, Vary!
```

Types are checked at compile time. Try changing the function to return an `Int` instead of `Str` and the compiler will catch it before anything runs.

## Try the REPL

For quick experiments, Vary has an interactive mode:

```text
vary@vary:/workspace$ vary repl
>>> 2 ** 10
1024
>>> let name = "Vary"
>>> f"Hello, {name}!"
Hello, Vary!
>>> exit()
```

## Clean up

Type `exit` to leave the container. Because you used `--rm`, Docker deletes the container and everything in it. Your host machine is untouched.

```text
vary@vary:/workspace$ exit
```

## What to do next

If you liked what you saw and want to install Vary on your machine, see the platform guides for [Linux](/docs/install-linux/), [macOS](/docs/install-macos/), or [Windows](/docs/install-windows/). For a persistent Docker setup with volume mounts instead of a throwaway container, see the [Docker guide](/docs/install-docker/).

Once installed, the [getting started](/docs/getting-started/) page walks through the language in more depth. The [test DSL](/docs/test-dsl/) page covers writing tests, and the [mutation testing](/docs/mutation/testing/) page explains the mutation engine in full.
