SKILL.md

  1---
  2name: scripting-with-roc
  3description: 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.
  4license: AGPL-3.0-or-later
  5metadata:
  6  author: Amolith <amolith@secluded.site>
  7---
  8
  9Create 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.
 10
 11**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.
 12
 13## Reference documentation
 14
 15- **Tutorial**: See [llms.txt](references/llms.txt) for comprehensive language guide
 16- **Standard library**: See [builtins-llms.txt](references/builtins-llms.txt) for all builtin modules
 17
 18## Basic structure
 19
 20Every Roc CLI script needs:
 21
 221. Shebang line (use `/usr/bin/env roc` or full path to roc)
 232. App header declaring the platform
 243. Imports from platform
 254. Main function with effectful signature
 26
 27```roc
 28#!/usr/bin/env roc
 29app [main!] { cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.20.0/X73hGh05nNTkDHU06FHC0YfFaQB1pimX7gncRcao5mU.tar.br" }
 30
 31import cli.Stdout
 32
 33main! = |_args|
 34    Stdout.line!("Hello, World!")
 35```
 36
 37Make executable: `chmod +x script.roc`, then run: `./script.roc`
 38
 39If `roc` is not in PATH, use full path in shebang: `#!/home/user/.local/share/roc/current/roc`
 40
 41## Command-line arguments
 42
 43```roc
 44#!/usr/bin/env roc
 45app [main!] {
 46    cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.20.0/X73hGh05nNTkDHU06FHC0YfFaQB1pimX7gncRcao5mU.tar.br",
 47}
 48
 49import cli.Stdout
 50import cli.Arg exposing [Arg]
 51
 52main! : List Arg => Result {} _
 53main! = |raw_args|
 54    args = List.map(raw_args, Arg.display)
 55
 56    # Index 0 is executable path, 1 is first real argument
 57    when List.get(args, 1) is
 58        Err(_) ->
 59            Err(Exit(1, "Error: Expected one argument\n\tUsage: ./script.roc <input>"))
 60
 61        Ok(first_arg) ->
 62            Stdout.line!("Received: ${first_arg}")
 63```
 64
 65## Code quality tools
 66
 67Roc provides helpful tooling for maintaining code quality. Use these regularly:
 68
 69### Format code
 70
 71```bash
 72roc format script.roc
 73```
 74
 75Automatically fixes spacing, indentation, and formatting. Run before committing code.
 76
 77### Type check
 78
 79```bash
 80roc check script.roc
 81```
 82
 83Checks types and reports errors/warnings without executing. Catches:
 84- Type mismatches
 85- Unused definitions
 86- Unused arguments (suggests prefixing with `_`)
 87- Missing pattern matches
 88
 89Example warnings:
 90```
 91── UNUSED DEFINITION in script.roc ──────────────────────────────────────
 92unused_var is not used anywhere in your code.
 9312│      unused_var = 42
 94         ^^^^^^^^^^
 95```
 96
 97### Run tests
 98
 99```bash
100roc test script.roc
101```
102
103Runs all `expect` expressions. Example:
104
105```roc
106add = |a, b| a + b
107
108expect add(2, 3) == 5
109expect add(0, 0) == 0
110```
111
112Warnings appear during script execution but don't prevent running. Fix them for clean code.
113
114## Common patterns
115
116### Reading stdin
117
118```roc
119app [main!] { cli: platform "..." }
120
121import cli.Stdin
122import cli.Stdout
123
124main! = |_args|
125    input = Stdin.line!
126    Stdout.line!("You entered: ${input}")
127```
128
129### File operations
130
131```roc
132app [main!] { cli: platform "..." }
133
134import cli.File
135import cli.Path
136import cli.Stdout
137
138main! = |_args|
139    path = Path.from_str("input.txt")
140    content = File.read_utf8!(path)?
141
142    Stdout.line!("File content: ${content}")
143```
144
145### Error handling
146
147Roc uses `Result` types, not exceptions. Use `?` to short-circuit on errors:
148
149```roc
150main! = |_args|
151    path = Path.from_str("config.json")
152    content = File.read_utf8!(path)?  # Returns early if error
153
154    # Process content...
155    Stdout.line!("Success!")
156```
157
158Or explicit pattern matching:
159
160```roc
161main! = |_args|
162    result = File.read_utf8!(Path.from_str("file.txt"))
163
164    when result is
165        Ok(content) -> Stdout.line!("Read: ${content}")
166        Err(err) -> Err(Exit(1, "Failed: ${Inspect.to_str(err)}"))
167```
168
169### Working with lists
170
171```roc
172# Filter, map, fold
173numbers = [1, 2, 3, 4, 5]
174evens = List.keep_if(numbers, |n| n % 2 == 0)
175doubled = List.map(evens, |n| n * 2)
176sum = List.walk(doubled, 0, Num.add)
177```
178
179### String manipulation
180
181```roc
182# Concatenation, splitting, trimming
183text = Str.concat("Hello", " World")
184words = Str.split(text, " ")
185trimmed = Str.trim("  spaces  ")
186replaced = Str.replace_each(text, "World", "Roc")
187```
188
189## Type signatures
190
191Add type signatures for clarity (optional but helpful):
192
193```roc
194process_file : Str => Result Str [FileNotFound, InvalidUtf8]
195process_file = |filename|
196    path = Path.from_str(filename)
197    content = File.read_utf8!(path)?
198    Ok(Str.to_upper(content))
199
200main! : List Arg => Result {} _
201main! = |args|
202    when List.get(args, 1) is
203        Ok(filename) ->
204            result = process_file!(filename)
205            when result is
206                Ok(content) -> Stdout.line!(content)
207                Err(_) -> Err(Exit(1, "Failed to process file"))
208
209        Err(_) ->
210            Err(Exit(1, "Usage: roc script.roc <filename>"))
211```
212
213## Important notes
214
215- **Platform URL**: Use current basic-cli release from https://github.com/roc-lang/basic-cli/releases
216- **No runtime**: Compiled binaries are standalone
217- **Immutable**: All values immutable; operations return new values
218- **Type inference**: Types inferred but signatures help clarity
219- **Helpful errors**: Compiler provides detailed, friendly error messages
220- **No exceptions**: Use `Result` for errors; no try/catch
221- **Functional**: Functions are first-class; use `|param| expression` syntax
222
223## Platform imports
224
225Common modules from `cli` platform:
226
227- `cli.Stdout` - Write to stdout
228- `cli.Stdin` - Read from stdin
229- `cli.File` - File operations
230- `cli.Path` - Path handling
231- `cli.Arg` - Command-line arguments
232- `cli.Env` - Environment variables
233- `cli.Dir` - Directory operations
234
235## When to use Roc
236
237**Good for:**
238- Type-safe scripts that catch errors at compile time
239- Functional programming enthusiasts
240- Long-lived scripts benefiting from stability
241- Learning functional programming
242- No dependencies needed (strong stdlib)
243
244**Avoid for:**
245- Production-critical code (Roc pre-1.0, breaking changes possible)
246- When you need mature ecosystem/libraries
247- Quick throwaway scripts (compilation overhead)
248- When team doesn't know functional programming
249
250## Maturity caveat
251
252Roc lacks numbered releases (still alpha/pre-1.0). Core features work well, but expect:
253- Breaking changes between versions
254- Some missing features
255- Smaller community/fewer examples
256- Excellent for learning and experimentation, use production caution