Examples & Performance

Maksim I

Examples

Runnable scripts are in the demo/ directory.

Sort by size

List .docx files sorted by size with human-readable byte counts. Shows list destructuring in list.sort comparators and string.bytes.

Pattern matching

Some examples of using pattern matching.

XO kata

Codewars kata: check whether a string has equal numbers of x and o. Shows string.iterate with mutable closure state and if as a multi-branch expression.

Dice game

Codewars kata: score a five-dice throw by the Greed rules. Shows list.fold building a frequency object, match dispatching on dice face, and curried scoring helpers.

Scoping

Counter factory: counter n returns a bump function that closes over its own x. Three independent counters, outer x untouched. Shows lexical closures and mutable captured state.

Chain list

Recursion+pattern matching. Format a list as [a-b-c-d] via recursion and match on length. Includes a one-liner equivalent using string.join to show the contrast.


Performance

Shik is written in Rust with a tree-walk interpreter, Rc/RefCell memory management (no tracing GC), and all built-in functions implemented in native Rust. IO-bound workloads are fast. CPU-bound algorithmic work (heavy branching, deep recursion) is slower.

Benchmark: count lines across ~9,800 lines of Rust source (37 files). Via hyperfine --warmup 3 -N, macOS, Apple Silicon.

Shik:

file.glob :./src/**/*.rs $>
  list.map (file.read-lines #> list.len) $>
  list.sum $> print

Bash:

find ./src -name '*.rs' -exec cat {} + | wc -l

Python:

from pathlib import Path
print(sum(len(f.read_text().splitlines()) for f in Path('./src').rglob('*.rs')))
ToolTimeMemory
Shik4.4 ms2.6 MB
Bash9.1 ms2.1 MB
Python30.3 ms12 MB

For CPU-bound algorithmic work (e.g. a dice game win probability calculator), Shik is roughly 10× slower than Python. Optimization is planned, but ergonomics come first.

Shik: Dice game

But replaced in the end with

list.range 1000 $> list.iterate fn [_] '(
  dice-game [5 1 3 4 1]
  dice-game [1 1 1 3 1]
  dice-game [2 4 4 5 4]
)

Python:

from collections import Counter

def dice_game(throw):
    bucket = Counter(throw)

    def count_accumulative(triple_mod, single_mod, count):
        if count == 3:
            return triple_mod
        elif count > 3:
            return triple_mod + (count % 3) * single_mod
        else:
            return (count % 3) * single_mod

    score = 0
    for dice, count in bucket.items():
        if dice == 1:
            score += count_accumulative(1000, 100, count)
        elif dice == 5:
            score += count_accumulative(500, 50, count)
        elif count >= 3:
            score += dice * 100

    return score

for _ in range(1000):
    dice_game([5, 1, 3, 4, 1])
    dice_game([1, 1, 1, 3, 1])
    dice_game([2, 4, 4, 5, 4])

Result:

Benchmark 1: shik dice-game.shk
  Time (mean ± σ):     335.2 ms ±   5.8 ms    [User: 312.1 ms, System: 17.5 ms]
  Range (min … max):   326.2 ms … 341.6 ms    10 runs
 
Benchmark 2: python3 dice-game.py
  Time (mean ± σ):      30.9 ms ±   3.1 ms    [User: 23.3 ms, System: 5.2 ms]
  Range (min … max):    28.5 ms …  45.1 ms    89 runs

Summary
  python3 dice-game.py ran
   10.84 ± 1.10 times faster than shik dice-game.shk

Shik is positioned for IO-bound shell automation, not complex lifetime applications or servers.


Roadmap

Current version: v0.7.1

Planned, roughly in priority order:


Contributing

Shik is in active development. The codebase is a Rust project; see CLAUDE.md (named after one nice fella, he is a great contributor) for architecture notes and conventions for adding native functions.

Issues and PRs welcome at github.com/pungy/shik.

License: MIT