Effective Meow

Idiomatic patterns and conventions for writing clean, consistent Meow code.

Placeholder Names

Every language has its go-to stand-in names — the world at large reaches for foo, bar, and baz. In Meow, we have our own cast of characters:

NameRole
NyantyuThe first cat on the scene — your default placeholder
TyakoThe second — shows up when one name isn’t enough
TyomusukeThe third — for when the gang’s all here

Use them in examples, tests, and documentation whenever you need throwaway names. Keeping these consistent makes .nyan code feel at home everywhere in the project.

kitty Cat {
  name: string
  age: int
}

nyan nyantyu = Cat("Nyantyu", 3)
nyan tyako = Cat("Tyako", 5)
nyan tyomusuke = Cat("Tyomusuke", 2)

nya(nyantyu)
nya(tyako)
nya(tyomusuke)

Naming Conventions

All user-facing identifiers in Meow use snake_case. This is enforced by meow lint with the snake-case rule.

# Good
nyan max_value int = 100
meow find_max(nums litter) int { ... }
meow test_basic_pipe() { ... }

# Bad — will trigger lint warning
nyan maxValue int = 100
meow findMax(nums litter) int { ... }
  • Variables: snake_casemy_cat, total_count, is_valid
  • Functions: snake_casefind_max, check_age, parse_input
  • Test functions: test_ prefix — test_addition, test_error_handling
  • Catwalk functions: catwalk_ prefix — catwalk_hello, catwalk_output
  • Kitty types: PascalCaseCat, Point, HttpResponse (they are type names, like Go structs)
  • Keywords: single lowercase words — nyan, meow, sniff, purr

Type Annotations

Add type annotations to function parameters and return values. This enables compile-time checking and generates faster native Go code instead of boxed meow.Value operations.

# Good — fully typed, generates native Go arithmetic
meow add(a int, b int) int {
  bring a + b
}

# Works but slower — generates boxed meow.Value operations
meow add(a, b) {
  bring a + b
}

For variables, type annotations are recommended when the type isn’t obvious from the initial value:

# Type is clear from the literal — annotation optional
nyan name = "Nyantyu"
nyan count = 0

# Type clarifies intent
nyan max_retries int = 3
nyan threshold float = 0.95

Use Go-style grouped types to reduce repetition:

# Instead of this
meow distance(x1 int, y1 int, x2 int, y2 int) float { ... }

# Write this
meow distance(x1, y1, x2, y2 int) float { ... }

Error Handling Patterns

Meow provides three error-handling mechanisms. Choose the right one for each situation:

~> — Use for inline recovery with a default value

Best when you have a sensible fallback:

nyan config = file.snoop("config.txt") ~> "default config"
nyan port = to_int(port_str) ~> 8080

~> with handler — Use when you need to inspect the error

nyan result = http.pounce(url) ~> paw(err) {
  nya("Request failed:", err)
  "{}"
}

gag / is_furball — Use for complex error logic

Best when you need to branch on success/failure:

nyan result = gag(paw() { parse_data(input) })
sniff (is_furball(result)) {
  nya("Parse error:", result)
  bring catnap
}
# continue with result

hiss — Use to signal unrecoverable errors

meow require_positive(n int) int {
  sniff (n <= 0) {
    hiss("expected positive number, got:", to_string(n))
  }
  bring n
}

Pipe Operator Idioms

The pipe |=| makes data transformation chains readable. Use it to express left-to-right data flow:

List processing chains

nyan result = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  |=| picky(paw(x) { x % 2 == 0 })
  |=| lick(paw(x) { x * x })
  |=| curl(0, paw(acc, x) { acc + x })

nya(result)   # => 220 (sum of squares of even numbers)

File processing with pipe

nab "file"

file.stalk("input.txt")
  |=| picky(paw(line) { len(line) > 0 })
  |=| lick(paw(line) { "=> " + line })
  |=| nya

nya works naturally at the end of a pipe:

[1, 2, 3] |=| lick(paw(x) { x * 2 }) |=| nya
# => [2, 4, 6]

Pattern Matching Best Practices

Always include a wildcard

Every peek expression should include a _ wildcard arm as the last case. This ensures all values are handled:

# Good
nyan label = peek(score) {
  0 => "zero",
  1..50 => "low",
  51..100 => "high",
  _ => "unknown"
}

# Risky — no wildcard means unmatched values return catnap
nyan label = peek(score) {
  0 => "zero",
  1..50 => "low"
}

Use ranges for numeric classification

meow grade(score int) string {
  bring peek(score) {
    0..59 => "F",
    60..69 => "D",
    70..79 => "C",
    80..89 => "B",
    90..100 => "A",
    _ => "invalid"
  }
}

Kitty Design Patterns

Use typed fields

Always annotate kitty fields with types:

kitty Cat {
  name: string
  age: int
}

Constructor pattern

Kitty types get an automatic constructor. Pass field values in order:

nyan nyantyu = Cat("Nyantyu", 3)

Using kitty with functions

kitty Rectangle {
  width: float
  height: float
}

meow area(rect) {
  bring to_float(rect.width) * to_float(rect.height)
}

meow describe(rect) {
  nya(to_string(rect.width) + "x" + to_string(rect.height)
    + " = " + to_string(area(rect)))
}

nyan r = Rectangle(4.0, 5.0)
describe(r)   # => 4x5 = 20

Testing Patterns

test_ vs catwalk_

Use test_ for logic assertions, catwalk_ for output verification:

# test_ — checks return values and conditions
meow test_addition() {
  expect(1 + 1, 2)
  judge(10 > 5)
}

# catwalk_ — checks printed output
meow catwalk_greeting() {
  nya("Hello, World!")
}
# Output:
# Hello, World!

Test organization

Group related assertions in a single test function:

meow test_string_operations() {
  expect(len("hello"), 5, "length")
  expect("a" + "b", "ab", "concatenation")
  judge("hello" == "hello", "equality")
  refuse("hello" == "world", "inequality")
}

Testing error conditions

meow test_division_by_zero() {
  nyan result = gag(paw() { divide(10, 0) })
  judge(is_furball(result), "should produce furball")
}

Fuzz testing

Functions with fuzz_ prefix receive random inputs via seed():

meow fuzz_addition(a int, b int) {
  nyan result = a + b
  judge(result - a == b, "addition identity")
}

Run with: meow test -fuzz fuzz_file.nyan

Code Organization

One concept per file

Keep .nyan files focused on a single module or concept:

flowchart LR
    subgraph project["project/"]
        main["main.nyan — Entry point"]
        math["math.nyan — Math utilities"]
        math_test["math_test.nyan — Tests for math"]
        http_client["http_client.nyan — HTTP operations"]
    end

Use nab for standard library

Declare imports at the top of the file:

nab "file"
nab "http"

# ... rest of code

Keep functions small

Prefer many small, focused functions over large monolithic ones. Use descriptive names:

# Good
meow is_even(n int) bool { bring n % 2 == 0 }
meow square(n int) int { bring n * n }

nyan result = [1, 2, 3, 4, 5]
  |=| picky(is_even)
  |=| lick(square)

# Less readable
nyan result = picky([1, 2, 3, 4, 5], paw(x) { x % 2 == 0 })
result = lick(result, paw(x) { x * x })

Formatting

Use meow fmt to auto-format your code:

meow fmt my_file.nyan
meow fmt .        # format all .nyan files in current directory

Linting

Use meow lint to check for style issues:

meow lint my_file.nyan
meow lint .       # lint all .nyan files

Current lint rules:

  • snake-case — identifiers must be snake_case