transducer

Clojure-inspired transducers for composable, reusable transformations

Files

FileDescription
.editorconfigEditor formatting configuration
.gitignoreGit ignore rules for build artifacts and dependencies
.tool-versionsasdf tool versions (Zig, Kit)
LICENSEMIT license file
README.mdThis file
docs/.keepPlaceholder for generated documentation
examples/basic.kitBasic transducer usage and convenience operations
examples/data-pipeline.kitData pipeline example with filtering, mapping, batching, and aggregation
examples/word-count.kitWord count and vocabulary analysis example
kit.tomlPackage manifest with metadata, tasks, and dependencies
src/transducer.kitMain Kit.Transducer module
tests/transducer.test.kitSelf-contained transducer tests

Dependencies

No Kit package dependencies.

The package uses only the Kit standard library. Internally, distinct-into uses Persistent.PSet to track seen values.

Installation

kit add gitlab.com/kit-lang/packages/kit-transducer.git

Usage

import Kit.Transducer as T

double = fn(x) => x * 2
even? = fn(x) => Int.even? x
small? = fn(x) => x < 20

main = fn =>
  # Compose transformations in left-to-right processing order
  xf = T.compose [
    T.map double,
    T.filter even?,
    T.filter small?
  ]

  result = T.into xf [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  println "Transformed: ${result}"

  # Convenience helper for compose + into
  piped = T.pipe [
    T.filter even?,
    T.map (fn(x) => x * x)
  ] [1, 2, 3, 4, 5, 6]
  println "Squared evens: ${piped}"

  # Aggregation helpers
  total = T.sum (T.filter even?) [1, 2, 3, 4, 5, 6]
  println "Sum of evens: ${Int.to-string total}"

main

API Overview

Core Transducers

FunctionDescription
mapApplies a function to each element
filterKeeps elements where a predicate returns true
removeDrops elements where a predicate returns true
catFlattens one level of nested lists
mapcatMaps each input to a list, then flattens
keepKeeps Some values returned by a function and drops None

Composition and Application

FunctionDescription
composeCombines transducers into one transformation
transduceApplies a transducer with a reducer, initial value, and input list
intoApplies a transducer and collects the results into a list
pipeConvenience helper for compose plus into

Collection Helpers

FunctionDescription
take-into, take-xfTake the first n values, optionally after a transducer
drop-into, drop-xfDrop the first n values, optionally after a transducer
take-while-into, take-while-xfTake while a predicate remains true
drop-while-into, drop-while-xfDrop while a predicate remains true
dedupe-into, dedupe-xfRemove consecutive duplicate values
distinct-into, distinct-xfKeep only first occurrences
interpose-into, interpose-xfInsert a separator between values
partition, partition-xfSplit a list into fixed-size chunks
partition-by, partition-by-xfGroup consecutive values by key
map-indexed, map-indexed-intoTransform values with their index

Aggregation Helpers

FunctionDescription
countCount values remaining after a transducer
sumSum numeric values remaining after a transducer
any?Check whether any values remain
none?Check whether no values remain
firstReturn the first remaining value as Some value or None
lastReturn the last remaining value as Some value or None

Design Notes

A transducer transforms one reducer into another reducer:

Transducer = (Reducer b a) -> (Reducer b c)
Reducer b a = (b, a) -> b

This lets transformation logic stay independent from collection logic. The same composed transformation can be used for collecting into a list, counting, summing, finding the first value, or other reductions.

Transducers are composed with left-to-right processing order:

xf = T.compose [T.map double, T.filter even?]

The input is mapped first and filtered second.

When passing a Kit built-in as a first-class function in examples or applications, prefer a small local wrapper:

even? = fn(x) => Int.even? x
length = fn(s) => String.length s

This keeps interpreter and compiled output behavior aligned for higher-order usage.

Development

Running Examples

Run examples with the interpreter:

kit run examples/basic.kit
kit run examples/data-pipeline.kit
kit run examples/word-count.kit

Compile an example to a native binary:

kit build examples/basic.kit && ./basic

Compare interpreter and compiled output for all examples:

kit parity --no-spinner --failures-only

Running Tests

Run the test suite:

kit test

Run the test suite with coverage:

kit test --coverage

Running kit dev

Run the standard development workflow (format, check, test):

kit dev

This will:

  1. Format and check source files in src/
  2. Run tests in tests/ with coverage

Generating Documentation

Generate API documentation from doc comments:

kit doc

Note: Kit sources with doc comments (##) will generate HTML documents in docs/*.html.

Cleaning Build Artifacts

Remove generated files, caches, parity results, and build artifacts:

kit task clean

Note: Defined in kit.toml.

Local Installation

To install this package locally for development:

kit install

This installs the package to ~/.kit/packages/@kit/transducer/, making it available for import as Kit.Transducer in other projects.

License

This package is released under the MIT License - see LICENSE for details.

Exported Functions & Types

map

(a -> b) -> Transducer a b

filter

(a -> Bool) -> Transducer a a

remove

(a -> Bool) -> Transducer a a

cat

Transducer (List a) a

mapcat

(a -> List b) -> Transducer a b

keep

(a -> Option b) -> Transducer a b

compose

List Transducer -> Transducer a b

transduce

Transducer a b -> (c -> b -> c) -> c -> List a -> c

into

Transducer a b -> List a -> List b

take-into

Int -> List a -> List a

take-xf

Int -> Transducer a b -> List a -> List b

drop-into

Int -> List a -> List a

drop-xf

Int -> Transducer a b -> List a -> List b

take-while-into

(a -> Bool) -> List a -> List a

take-while-xf

(b -> Bool) -> Transducer a b -> List a -> List b

drop-while-into

(a -> Bool) -> List a -> List a

drop-while-xf

(b -> Bool) -> Transducer a b -> List a -> List b

dedupe-into

List a -> List a

dedupe-xf

Transducer a b -> List a -> List b

distinct-into

List a -> List a

distinct-xf

Transducer a b -> List a -> List b

interpose-into

a -> List a -> List a

interpose-xf

b -> Transducer a b -> List a -> List b

map-indexed

(Int -> a -> b) -> Transducer a b

map-indexed-into

(Int -> a -> b) -> List a -> List b

count

Transducer a b -> List a -> Int

sum

Transducer a Int -> List a -> Int

any?

Transducer a b -> List a -> Bool

none?

Transducer a b -> List a -> Bool

first

Transducer a b -> List a -> Option b

last

Transducer a b -> List a -> Option b

partition

Int -> List a -> List (List a)

partition-xf

Int -> Transducer a b -> List a -> List (List b)

partition-by

(a -> b) -> List a -> List (List a)

partition-by-xf

(b -> c) -> Transducer a b -> List a -> List (List b)

pipe

List Transducer -> List a -> List b