---
name: scripting-with-roc
description: Creates Roc scripts for CLI automation with strong static types, helpful compiler errors, and functional programming. Use when the user wants Roc scripts, mentions Roc, functional scripting, type-safe scripts, strongly-typed scripts, or needs executable .roc files.
license: AGPL-3.0-or-later
metadata:
  author: Amolith <amolith@secluded.site>
---

Create scripts using Roc, a functional language with strong types, helpful compiler errors, and no runtime. Scripts can run with `roc` or compile to standalone binaries.

**Platform version note**: Examples use basic-cli 0.20.0. Check https://github.com/roc-lang/basic-cli/releases for the latest version and update URLs accordingly.

## Reference documentation

- **Tutorial**: See [llms.txt](references/llms.txt) for comprehensive language guide
- **Standard library**: See [builtins-llms.txt](references/builtins-llms.txt) for all builtin modules

## Basic structure

Every Roc CLI script needs:

1. Shebang line (use `/usr/bin/env roc` or full path to roc)
2. App header declaring the platform
3. Imports from platform
4. Main function with effectful signature

```roc
#!/usr/bin/env roc
app [main!] { cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.20.0/X73hGh05nNTkDHU06FHC0YfFaQB1pimX7gncRcao5mU.tar.br" }

import cli.Stdout

main! = |_args|
    Stdout.line!("Hello, World!")
```

Make executable: `chmod +x script.roc`, then run: `./script.roc`

If `roc` is not in PATH, use full path in shebang: `#!/home/user/.local/share/roc/current/roc`

## Command-line arguments

```roc
#!/usr/bin/env roc
app [main!] {
    cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.20.0/X73hGh05nNTkDHU06FHC0YfFaQB1pimX7gncRcao5mU.tar.br",
}

import cli.Stdout
import cli.Arg exposing [Arg]

main! : List Arg => Result {} _
main! = |raw_args|
    args = List.map(raw_args, Arg.display)

    # Index 0 is executable path, 1 is first real argument
    when List.get(args, 1) is
        Err(_) ->
            Err(Exit(1, "Error: Expected one argument\n\tUsage: ./script.roc <input>"))

        Ok(first_arg) ->
            Stdout.line!("Received: ${first_arg}")
```

## Code quality tools

Roc provides helpful tooling for maintaining code quality. Use these regularly:

### Format code

```bash
roc format script.roc
```

Automatically fixes spacing, indentation, and formatting. Run before committing code.

### Type check

```bash
roc check script.roc
```

Checks types and reports errors/warnings without executing. Catches:
- Type mismatches
- Unused definitions
- Unused arguments (suggests prefixing with `_`)
- Missing pattern matches

Example warnings:
```
── UNUSED DEFINITION in script.roc ──────────────────────────────────────
unused_var is not used anywhere in your code.
12│      unused_var = 42
         ^^^^^^^^^^
```

### Run tests

```bash
roc test script.roc
```

Runs all `expect` expressions. Example:

```roc
add = |a, b| a + b

expect add(2, 3) == 5
expect add(0, 0) == 0
```

Warnings appear during script execution but don't prevent running. Fix them for clean code.

## Common patterns

### Reading stdin

```roc
app [main!] { cli: platform "..." }

import cli.Stdin
import cli.Stdout

main! = |_args|
    input = Stdin.line!
    Stdout.line!("You entered: ${input}")
```

### File operations

```roc
app [main!] { cli: platform "..." }

import cli.File
import cli.Path
import cli.Stdout

main! = |_args|
    path = Path.from_str("input.txt")
    content = File.read_utf8!(path)?

    Stdout.line!("File content: ${content}")
```

### Error handling

Roc uses `Result` types, not exceptions. Use `?` to short-circuit on errors:

```roc
main! = |_args|
    path = Path.from_str("config.json")
    content = File.read_utf8!(path)?  # Returns early if error

    # Process content...
    Stdout.line!("Success!")
```

Or explicit pattern matching:

```roc
main! = |_args|
    result = File.read_utf8!(Path.from_str("file.txt"))

    when result is
        Ok(content) -> Stdout.line!("Read: ${content}")
        Err(err) -> Err(Exit(1, "Failed: ${Inspect.to_str(err)}"))
```

### Working with lists

```roc
# Filter, map, fold
numbers = [1, 2, 3, 4, 5]
evens = List.keep_if(numbers, |n| n % 2 == 0)
doubled = List.map(evens, |n| n * 2)
sum = List.walk(doubled, 0, Num.add)
```

### String manipulation

```roc
# Concatenation, splitting, trimming
text = Str.concat("Hello", " World")
words = Str.split(text, " ")
trimmed = Str.trim("  spaces  ")
replaced = Str.replace_each(text, "World", "Roc")
```

## Type signatures

Add type signatures for clarity (optional but helpful):

```roc
process_file : Str => Result Str [FileNotFound, InvalidUtf8]
process_file = |filename|
    path = Path.from_str(filename)
    content = File.read_utf8!(path)?
    Ok(Str.to_upper(content))

main! : List Arg => Result {} _
main! = |args|
    when List.get(args, 1) is
        Ok(filename) ->
            result = process_file!(filename)
            when result is
                Ok(content) -> Stdout.line!(content)
                Err(_) -> Err(Exit(1, "Failed to process file"))

        Err(_) ->
            Err(Exit(1, "Usage: roc script.roc <filename>"))
```

## Important notes

- **Platform URL**: Use current basic-cli release from https://github.com/roc-lang/basic-cli/releases
- **No runtime**: Compiled binaries are standalone
- **Immutable**: All values immutable; operations return new values
- **Type inference**: Types inferred but signatures help clarity
- **Helpful errors**: Compiler provides detailed, friendly error messages
- **No exceptions**: Use `Result` for errors; no try/catch
- **Functional**: Functions are first-class; use `|param| expression` syntax

## Platform imports

Common modules from `cli` platform:

- `cli.Stdout` - Write to stdout
- `cli.Stdin` - Read from stdin
- `cli.File` - File operations
- `cli.Path` - Path handling
- `cli.Arg` - Command-line arguments
- `cli.Env` - Environment variables
- `cli.Dir` - Directory operations

## When to use Roc

**Good for:**
- Type-safe scripts that catch errors at compile time
- Functional programming enthusiasts
- Long-lived scripts benefiting from stability
- Learning functional programming
- No dependencies needed (strong stdlib)

**Avoid for:**
- Production-critical code (Roc pre-1.0, breaking changes possible)
- When you need mature ecosystem/libraries
- Quick throwaway scripts (compilation overhead)
- When team doesn't know functional programming

## Maturity caveat

Roc lacks numbered releases (still alpha/pre-1.0). Core features work well, but expect:
- Breaking changes between versions
- Some missing features
- Smaller community/fewer examples
- Excellent for learning and experimentation, use production caution