Ai Language Idioms

Language Idioms for AI Agents

❄️ Nix

  • Infinite Recursion: Never use config.foo to define the enable condition for config.foo.
  • Strings: Prefer lib.concatStringsSep over repetitive string interpolation for lists.
  • Let Bindings: Keep let ... in blocks as close to their usage scope as possible.

🥟 Janet

  • Immutability: Janet has both mutable (array, table, buffer) and immutable (tuple, struct, string) data structures. Be explicit about which you are using.
  • Macros: Avoid macros unless absolutely necessary. Use higher-order functions (map, filter, reduce) first.
  • Error Handling: Use (try ... ([err] ...)) instead of letting the script crash, especially in hey CLI tasks.
  • (dyn :syspath) Trap: Janet sets the default (dyn :syspath) to the last entry of JANET_PATH, not to the directory where a module was actually found. Never use (dyn :syspath) to construct filesystem paths that must exist (e.g., (os/realpath (path/join (dyn :syspath) "../bin"))). Instead, iterate JANET_PATH entries with some and check (path/directory? ...) before using the result. See lib/hey/lib.janet exec-path definition and docs/toolchain.md §6 for the full rationale.
  • os/realpath Crashes on Missing Paths: Unlike os/stat or path/directory? which return nil on missing files, os/realpath throws an error if the target doesn’t exist. Prefer existence checks (path/directory?, path/file?) over os/realpath unless symlink resolution is required AND you know the path exists.
  • Janet Image Creation Trap (def at build time): When jpm install builds a compiled executable, Janet evaluates all module-level def, var, and delay expressions at image creation time (the build sandbox), capturing build-time environment values into the binary. (os/getenv "USER") returns nix-build, (os/getenv "XDG_DATA_HOME") returns /build/.local/share, etc. These stale values are baked into /run/current-system/sw/bin/hey permanently.
    • Never use (def x (os/getenv ...)) or (var x (os/getenv ...)) for values that must reflect the runtime host.
    • Never rely on (delay ...) — Janet fibers may not survive make-image/load-image cycles intact, or may be forced at build time with stale env.
    • Always use (defn name [] ...) — pure functions called at runtime, evaluating os/getenv fresh each invocation.
    • Testing: The dev build (./scripts/build_hey.zsh) runs from source with correct env vars and will not reproduce the bug. Always test the nix-built binary (nix build .#hey && result/bin/hey info) to confirm the system-level fix.
    • See docs/toolchain.md §XDG Path Compile-Time Caching Bug and §6 Exec-Path Resolution.

🎨 Matugen Templates (matugen 4.0.0)

  • Never use | set_alpha: X | format: "rgba" on colors.*.default: This panics in matugen 4.0.0 with “Cant convert map to FilterReturnType”. The color object structure is incompatible with the filter chain. Use .hex_alpha | set_alpha: N instead.
  • .hex for opaque, .hex_alpha for transparency:
    # Safe — opaque hex
    {{ colors.primary.default.hex }}
    
    # Safe — hex with alpha (8-digit #RRGGBBAA)
    {{ colors.background.default.hex_alpha | set_alpha: 0.85 }}
    
    # PANICS in 4.0.0
    {{ colors.background.default | set_alpha: 0.85 | format: "rgba" }}
  • | format: "rgba" only with | to_color first: The format filter works when preceded by to_color on a string literal, e.g. {{ "red" | to_color | set_alpha: 0.5 | format: "rgba" }}. It panics when applied to a colors.* object directly.
  • Always test all 11 templates after changes: See docs/toolchain.md §7 for the full verification protocol. Run grep -r "set_alpha\|format:" config/matugen/templates/ before committing any matugen changes.

🐚 Zsh

  • Scoping: Always use local for variables inside functions.
  • Arrays: Use Zsh array syntax my_array=(a b c) and iterate with for item in "${my_array[@]}"; do.
  • Conditionals: Prefer [[ ]] over [ ] for safe string evaluation.