npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@dashkite/katana

v0.4.18

Published

Stack-based composition combinators in JavaScript

Readme

Katana

Hippocratic License HL3-CORE

Combinators for stack-based composition in JavaScript.

import { pipe } from "@dashkite/joy/function"
import { push, pop, poke } from "@dashkite/katana"

pipe [
  push -> 1
  push -> 2
  poke ( x, y ) -> x + y
  pop ( z ) -> assert.equal 3, z
  ( stack ) -> assert.equal stack.length, 0
]

Table Of Contents

Installation

Install Katana as @dashkite/katana using your favorite package manager. To use Katana in a browser, import it directly or use your favorite bundler.

Motivation

Functional programming is often promoted as a way to build more reliable software. In particular, function composition promises to make it possible to combine modular and well-tested functions to build more complex systems without sacrificing reliability or ease of maintenance.

In practice, one of the challenges is that composition can be difficult. Function signatures don’t always line up neatly, forcing awkward compromises that may be difficult to understand or reason about. In contrast, imperative code with intermediate state, though perhaps less elegant, may ultimately be more expressive.

Stack-based composition is an attempt to address this challenge by making it easier to compose binary (or non-unary) functions in a point-free style. For example, consider a binary function, encrypt, taking a key and message (the plaintext). We might write this imperatively like this:

encryptWithKeyName = ( message, name ) ->
  key = await getKey name
  encrypt message, key

If we wanted to use composition here, we’d need a way to compose encrypt with getKey. However, because encrypt is a binary function, it’s unclear how we might do this.

Stack-based composition solves this problem by composing over a stack, so that both the key and message are available (the top of the stack is the rightmost value, at the end of the array):

encryptWithKeyName = pipe [
  pair                             # [ message, key-name ]
  poke getKey                      # [ message, key ]
  swap                             # [ key, message ]
  poke encrypt                     # [ encrypted-message ]
  first                            # => encrypted-message
]

For such a simple function, we might well prefer to use the imperative style. However, for more complex cases, being able to rely strictly on composition makes it easier realize the advantages of functional programming.

API

The core stack combinators—push, pop, peek, and poke—apply a function, using the arity of the function to determine how many elements from the stack to pass into it, and, in the case of pop and poke, to remove from the stack. Applying a unary function will result in passing the top of the stack into the function. Applying a binary function will result in passing the first two elements from the stack into the function, and so on. Other stack operators, such as drop and copy, simply operate on the stack directly.

You can compose function with any composition function from your favorite functional programming library. Stack combinators that take asynchronous functions will return a promise, so your composition function should handle promises if you’re using asynchronous functions.

Reference

push

push f, stack → stack

Call f with k items from the stack, where k is the arity of f. Push the return value onto the stack.

pop

pop f, stack → stack

Call f with k items from the stack, where k is the arity of f. Pops the items from the stack.

poke, replace

poke f, stack → stack

Call f with k items from the stack, where k is the arity of f. Pops the items from the stack. Push the return value onto the stack. Alias: replace.

peek

peek f, stack → stack

Call f with k items from the stack, where k is the arity of f. Leaves the stack unchanged.

drop, discard

drop stack → stack

Pops the stack. Equivalent to pop ( x ) ->

up

up stack → stack

Rotates the items on the stack, pushing items up, while the top of the stack goes to the bottom.

do pipe [
  -> [ 1..5 ]      # [ 1, 2, 3, 4, 5 ]
  up               # [ 5, 1, 2, 3, 4 ]
]

down

down stack → stack

Rotates the items on the stack, pushing items down, while the bottom of the stack goes to the top.

do pipe [
  -> [ 1..5 ]     # [ 1, 2, 3, 4, 5 ]
  down            # [ 2, 3, 4, 5, 1 ]
]

swap

swap stack → stack

Swaps the first two items on the stack, so the first becomes the second and vice-versa.

copy, duplicate

copy stack → stack

Copies the top item on the stack.

flatten

flatten stack → stack

If the first item of the stack is an iterable, push each item it produces onto the stack. Items are added in the reverse order from when they’re produced, so that the first item produced will be at the top of the stack.

If it’s not an iterable, do nothing.

do pipe [
  -> []           # []
  push [ 1..5 ]   # [[ 1..5 ]]
  flatten         # [ 5, 4, 3, 2, 1 ]
]

[!Note]

The reason we need this operator—rather than just relying on array functions—is because there’s no simple way to do this otherwise. For example, using Array::flat would just add back the array.

stack

stack stack → stack

Place the stack on the stack as an array, removing all the other items.