Shik — I finally feel joy writing scripts — DeepSeek Blog | Neura Market
    Neura MarketNeura Market/DeepSeek
    ChatGPTChatGPTClaudeClaudeGeminiGeminiCursorCursorGrokGrokPerplexityPerplexityDeepSeekDeepSeek
    CoPilotCoPilotStable DiffusionStable DiffusionMidjourneyMidjourney
    View All Directories
    OverviewRulesPromptsMCPsAgentsBlogVideosGuidesCoursesCommunityTrendingGenerate
    DeepSeekBlogShik — I finally feel joy writing scripts
    Back to Blog
    Shik — I finally feel joy writing scripts
    programming

    Shik — I finally feel joy writing scripts

    Maksim Iakovlev April 15, 2026
    0 views

    You know that feeling when the thought in your head is simple and clear — "go through files, find the...

    You know that feeling when the thought in your head is simple and clear — "go through files, find the right ones, move them" — but between the thought and the result there's a wall of googling flags, quoting rules, and incompatible utilities? I've been into language design for a while — what interests me most is syntax, how form shapes thinking. When I decided to build a complete language, I picked the area where I was most frustrated and where nothing fit my taste — terminal scripting. ## Where the joy disappeared Here's a task: find files containing the string "- links", move them to a topics/ folder. In your head, that's one sentence. In the terminal, it turns into this: ```bash for file in *; do if [ -f "$file" ] && grep -q -- "- links" "$file"; then mv "$file" topics/ fi done ``` Every line is a small minefield. Quotes around `"$file"` — forget them, blow up on a space in the name. `grep -q` — or was it `-l`? Or `-s`? `--` before the string — why? Oh, because of the dash. Five lines, four traps. An experienced will say: `grep -l "- links" * | xargs mv -t topics/` — one line! Sure. But which grep flag prints filenames — `-l` or `-L`? `xargs mv` — what order do the arguments go in? `-t` — is that target? What about spaces in names? Every single time — the task takes a second in your head, five minutes on Google or AI. Every time — not your solution, you just copied someone's code and hope it works. Or count lines across all `.rs` files. In your head: "find → read → count → sum." In the terminal: `find` + `xargs` + `wc` + `awk` — four utilities, each with its own universe of flags. Here's the thing. It's not because bash is bad. Bash is a good **shell**. Fish and friends (Nushell, Elvish) are reimagined **shells**. Python/Node.js/Ruby are powerful languages built for backend work. **None of them were designed to make chaining actions in a terminal feel good to type.** Each has its own focus, and scripting everyday chores is a side effect. I didn't want another shell or another general-purpose language. I needed thoughts to flow from my head into the terminal without friction. ## Where it came back I built **Shik**. Here's that same task: ```shik file.glob :./* $> list.filter file.is-file $> list.filter (fn [f] file.read f $> string.has "- links") $> list.iterate (file.move :topics) ``` Four lines. Each one is a single step. Data flows top to bottom, left to right. Pure functions only, functions return primitive data (list/string/number/bool), functions compose and curry. **Reads like the thought.** Count lines: ```shik file.glob :./src/**/*.rs $> list.map (file.read #> string.lines #> list.len) $> list.sum $> print ``` This is where I felt it: **this is it**. I think "find files → read → split into lines → count → sum → print" — and I type exactly that. One to one. Thought = code. ## One rule for the whole language The design grew out of Lisp and Haskell, adapted for the terminal. During development I deliberately kept using the most primitive REPL — arrow keys didn't even work. If the language is comfortable to use even under those conditions, the syntax works. **Everything is function application.** `+ 1 2` isn't an operator — it's calling the function `+` with arguments `1` and `2`. `list.map`, `file.glob`, `string.upper` — also functions. `if`, `let`, `while` — also functions. One rule, and you know the whole language. `list.map` is not a function `map` from module `list` — it's the full name of the function, the dot is part of the name! **Space is application:** ```shik file.glob :./src/**/*.rs ``` Literally: "apply `:./src/**/*.rs` to the function `file.glob`." Like `f(x)`, but without parentheses. **Currying gives you free combinators.** Pass fewer arguments than a function expects — get a new function back: ```shik let lst [1 2 3 4] lst $> list.map (+ 1) ; [2 3 4 5] lst $> list.map (- 1) ; [0 1 2 3] lst $> list.map (* 2) ; [2 4 6 8] lst $> list.map (^ 2) ; [1 4 9 16] ``` `(+ 1)` is a function meaning "add one." `(* 2)` is "multiply by two." Uniform, no lambdas needed. But why does `(- 1)` mean "subtract one" and not "subtract from one"? This is a deliberate, heretical decision! In Shik, argument order is a design choice — everything is built around currying. For arithmetic: **the first argument is the modifier, the second is the base**. `- 1 5` = `4`, because `1` is "how much to subtract" and `5` is "from what." If `-` worked as "first minus second," `(- 1)` would mean "one minus something," and you'd have to write `fn [x] - x 1` or introduce `flip`. You can read more about the argument ordering philosophy in the [docs](https://blog.pungy.me/articles/shik-book/functions#argument-order-philosophy). **Four operators — that's it.** All about application, composition, and most importantly precedence — from tightest binding to loosest: | Operator | What it does | Example | |----------|-------------|---------| | ` ` (space) | function application | `f x` | | `#>` | composition | `file.read #> string.lines` | | `$` | low-precedence application | `print $ + 1 2` | | `$>` | left-to-right pipe | `x $> f` | This is the **entire** flow control syntax. Nothing else. Everything else is combinations of these four things. These operators fix the main problem with using Lisp in a terminal. Instead of: ```shik print (list.sum (list.map (fn [path] list.len (string.lines (file.read path))) (file.glob :./src/**/*.rs))) ``` You write: ```shik file.glob :./src/**/*.rs $> list.map (file.read #> string.lines #> list.len) $> list.sum $> print ``` These two are fully equivalent and both valid Shik code! **Ready out of the box:** `file.`, `string.`, `list.`, `object.`, `shell.` — available without imports. Open the REPL and start working. `help list.` shows all list functions. ## Syntax in five minutes Everything you need to know: ```shik ; Literals 42 ; number "hello world" ; string :hello ; also a string — for words without spaces (faster to type!) [1 2 3] ; list {:name :Alice :age 30} ; object fn [arg] body ; function ; Variables let name :Alice let greet fn [name] "Hello, {name}!" ; {expression} — interpolation print $ greet name ; Hello, Alice! ; Composition — gluing functions together let read-lines (file.read #> string.lines) read-lines :.gitignore ; ["target" "docs" "releases"] ; Multi-line functions let reverse fn [str] '( let reversed "" str $> string.iterate-backward (string.push reversed) reversed ; last expression is the result ) ; Destructuring let head fn [[x _]] x head [1 2 3] ; 1 ; Pattern matching match [1 2 3 4] { [] :empty [x y #rest] "first: {x}, rest: {rest}" } ; "first: 1, rest: [3 4]" ; External commands shell "git log --oneline -5" $> print shell.lines "git branch" $> list.filter (string.has :feature) $> list.iterate print ``` That's it. Seriously. If you've read this block, you know Shik. If you interesting in details, you can read the [book](https://blog.pungy.me/articles/shik-book)! Now — the real tasks this was all built for. ## In practice **Find all TODOs in a project and print a report, sorted by count descending:** ```shik let has-todo (file.read #> string.has :TODO) let count-todos (file.read #> string.lines #> list.filter (string.has :TODO) #> list.len) file.glob :./src/**/*.rs $> list.filter has-todo $> ;; turn list of paths into [path todo_count] list.map (fn [f] [f (count-todos f)]) $> list.sort (fn [[_ a] [_ b]] - a b) $> list.iterate (fn [[file n]] print "{file}: {n} TODOs") ``` Seven lines. `has-todo` and `count-todos` are assembled by gluing existing functions with `#>`. No classes, objects, modules, or imports. Just: "read → check" and "read → split → filter → count." **Config backup across machines** — the script I used to debug the entire language: ```shik let HOME shell.home let make-path fn [dir] "{HOME}/.config/{dir}" let$ [KITTY-PATH FISH-PATH] [(make-path :kitty) (make-path :fish)] let FISH-FILES [:fish_plugins :functions/start.fish :functions/gr.fish] let KITTY-FILES $ file.list KITTY-PATH let HOME-FILES [:.ghci :.gitconfig] let make-copier fn [files from dest] fn [] '( files $> list.iterate fn [file] '( print "Copy: {from}/{file} -> {dest}/{file}" file.copy "{dest}/{file}" "{from}/{file}" ) ) let sync-fish $ make-copier FISH-FILES FISH-PATH :fish let sync-home $ make-copier HOME-FILES HOME :home let install-fish $ make-copier FISH-FILES :fish FISH-PATH let install-home $ make-copier HOME-FILES :home HOME ; Run: shik backup.shk sync fish home let options $ list.drop 2 shell.args let mode $ list.head options let targets (if (list.empty? $ list.tail options) [:fish :home :kitty] (list.tail options)) targets $> list.map (+ "{mode}-" #> var.get) $> list.iterate fn.invoke ``` The last three lines are my favorite part. `(+ "{mode}-")` prepends a prefix, `var.get` turns the string `"sync-fish"` into the variable `sync-fish`, `fn.invoke` calls it. Strings → names → functions → result. Shik deliberately trades strictness for flexibility — and that's exactly the tradeoff that makes scripting fun. <img src="https://raw.githubusercontent.com/pungy/shik/main/shik-demo.gif"> ## Performance Shik is written in Rust. I didn't make performance the primary focus, but I optimized where it didn't add complexity. It has its own parser and tree-walk interpreter, no tracing GC — memory management via Rc/RefCell, all built-in functions are written in Rust. IO-bound operations run fast. Line counting across the [Shik project itself](https://github.com/pungy/shik) (~9,800 lines, 37 files): **Shik**: ```shik file.glob :./src/**/*.rs $> list.map (file.read-lines #> list.len) $> list.sum $> print ``` **Bash**: ```bash find ./src -name '*.rs' -exec cat {} + | wc -l ``` **Python**: ```python from pathlib import Path print(sum(len(f.read_text().splitlines()) for f in Path('./src').rglob('*.rs'))) ``` Benchmarks via `hyperfine --warmup 3 -N`, macOS, Apple Silicon: - **Shik**: - **Time**: 4.4 ms - **Memory**: 2.6 MB - **Bash**: - **Time**: 9.1 ms - **Memory**: 2.1 MB - **Python**: - **Time**: 30.3 ms - **Memory**: 12 MB However, on CPU-bound algorithmic work with heavy application and branching — e.g. a [dice game win calculator](https://github.com/PunGy/shik/blob/main/demo/dice-game.shk) — Shik loses to Python by roughly **10x**. Optimization is planned, but the focus will always be on syntax ergonomics and writing comfort. ## Try it ```bash # Via cargo cargo install shik # macOS / Linux curl --proto '=https' --tlsv1.2 -LsSf https://github.com/pungy/shik/releases/latest/download/shik-installer.sh | sh # Windows (PowerShell) powershell -ExecutionPolicy ByPass -c "irm https://github.com/pungy/shik/releases/latest/download/shik-installer.ps1 | iex" ``` ```bash shik # REPL — try typing help inside shik script.shk # run a file ``` The project is in active development (v0.7.1). This is not a production-ready tool — it's a thing I use every day and genuinely enjoy using. **Planned:** shebang support, regex, JSON parsing, networking, try/catch, multithreading. --- Shik won't replace bash — it's not a shell. It won't replace Python for bots. But if every couple of days you need to move, filter, rename, or generate a report — and every time you spend more time fighting the tool than solving the problem — give it a shot. You might find some joy in it too. - **[GitHub](https://github.com/pungy/shik)** - **[Shik Book](https://blog.pungy.me/articles/shik-book)**

    Tags

    programmingshowdevtoolingopensource

    Comments

    More Blog

    View all
    How I'm using ASTs and Gemini to solve the "Codebase Onboarding" problem 🧠ai

    How I'm using ASTs and Gemini to solve the "Codebase Onboarding" problem 🧠

    Hi everyone! 👋 I’m Tara, a Senior Software Engineer and Consultant. Over the years, I've jumped...

    T
    tworrell
    Local AI Will Save Us All (The Math Says So, Trust Me)ai

    Local AI Will Save Us All (The Math Says So, Trust Me)

    Every few weeks a take goes viral in tech circles making the case for ditching cloud AI and running...

    S
    Sebastian Schürmann
    Lost in the AI Hype, I Started Smallai

    Lost in the AI Hype, I Started Small

    And it helped me get back into tech without drowning TL;DR at the end Coming back to...

    R
    Rohini Gaonkar
    Building a Replay-Tested Interactive Brokers Client in Gogo

    Building a Replay-Tested Interactive Brokers Client in Go

    I wanted an IBKR library that felt like Go and had testing I could trust. So I wrote one.

    T
    Thomas Marcelis
    Playwright in Pictures: Fully Parallel Modeplaywright

    Playwright in Pictures: Fully Parallel Mode

    Playwright’s fullyParallel mode is often treated as a simple performance switch. In practice, it...

    V
    Vitaliy Potapov
    Designing a CLI for Both Humans and Agentscli

    Designing a CLI for Both Humans and Agents

    Learn how Alpic designed its CLI for both human developers and AI agents — covering tradeoffs like polling, context windows, interactivity, and statelessness.

    J
    Julien Vallini

    Stay up to date

    Get the latest DeepSeek prompts, rules, and resources delivered to your inbox weekly.

    Neura Market LogoNeura Market

    Discover the best AI prompts, plugins, and resources for DeepSeek and more.

    Content Types

    • Rules
    • Prompts
    • MCPs
    • Agents
    • Guides

    Platforms

    • ChatGPT Directory
    • Claude Directory
    • Gemini Directory
    • Cursor Directory
    • Grok Directory
    • Perplexity Directory
    • DeepSeek Directory
    • CoPilot Directory
    • Stable Diffusion Directory
    • Midjourney Directory
    • All Directories

    Resources

    • Blog
    • Documentation
    • Help Center
    • Marketplace

    Legal

    • Privacy Policy
    • Terms of Service

    © 2026 Neura Market. All rights reserved.

    |

    Not affiliated with any AI platform vendors.