Modules

Modules allow you to organize Kit code into reusable units. Kit's module system supports importing from the standard library, external packages, and local files.

Overview

Kit's module system is designed to be simple and intuitive. Each .kit file can be imported as a module, and packages can contain multiple modules organized in a directory structure.

  • Standard Library - Built-in modules like List, String, Map
  • External Packages - Third-party packages installed via kit add
  • Local Modules - Your own .kit files

Importing Modules

Standard Library Imports

Import built-in modules using their capitalized names:

# Standard library modules are always available
import List
import String
import Map
import Set
import File
import JSON

# Use module functions with dot notation
items = [1, 2, 3]
doubled = List.map (fn(x) => x * 2) items

text = "hello world"
words = String.split " " text

Package Imports

Import external packages after adding them with kit add:

# Import installed packages
import HTTP
import Postgres
import JSON
import Crypto

# Use package functions
response = HTTP.get "https://api.example.com/data"
hash = Crypto.sha256 "hello"

Local File Imports

Import local .kit files using relative paths:

# Import from the same directory
import "./utils.kit"
import "./models.kit"

# Import from subdirectory
import "./lib/helpers.kit"

# Import from parent directory
import "../shared/common.kit"

# Use imported functions
result = utils.format-data data

Aliased Imports

Give modules shorter or more descriptive names:

# Alias a module
import Postgres as Pg
import "./database/connection.kit" as DB

# Use the alias
conn = Pg.connect config
result = DB.query "SELECT * FROM users"

Selective Imports

Import specific items from a module using the .{...} syntax:

# Import specific functions from a local module
import "./types.kit".{User, create-user, validate}

# Import with aliases for individual items
import "./logger.kit".{debug as log-debug, info as log-info}

# Multi-line selective imports
import "./types.kit".{
  Debug,
  Info,
  Warn,
  Error,
  should-log?
}

# Use imported items directly (no module prefix)
user = create-user 1 "Alice"
log-debug "User created"

Dotted Module Paths

Import modules from nested package structures:

# Import a submodule from a package
import Embeddings.Postgres
import Embeddings.SQLite as Store

# Use the imported module
store = Store.create config

Import Syntax Summary

Syntax Description Usage
import Module Import standard library or package module Module.function
import "path.kit" Import local file path.function
import Module as M Import with alias M.function
import "path.kit" as M Import local file with alias M.function
import "path.kit".{a, b} Selective import a, b (direct)
import "path.kit".{a as x} Selective import with alias x (direct)
import Parent.Child Import submodule Child.function

Creating Modules

Basic Module

Any .kit file can be imported as a module. All top-level bindings are exported:

# math.kit

# Public functions (exported)
pi = 3.14159265359

square = fn(x) => x * x

cube = fn(x) => x * x * x

circle-area = fn(radius) =>
  pi * square radius

sphere-volume = fn(radius) =>
  (4 / 3) * pi * cube radius
# main.kit
import "./math.kit"

area = math.circle-area 5
println "Circle area: ${area}"

Module with Types

Modules can export types along with functions:

# user.kit

# Type definition
type User = {
  id: Int,
  name: String,
  email: String,
  active?: Bool
}

type UserError =
  | NotFound String
  | InvalidEmail String
  | Unauthorized

# Constructor functions
create = fn(id, name, email) =>
  {id: id, name: name, email: email, active?: true}

# Validation
validate = fn(user) =>
  if not (String.contains? "@" user.email) then
    Err (InvalidEmail user.email)
  else
    Ok user

# Queries
is-active? = fn(user) => user.active?

display-name = fn(user) =>
  "${user.name} <${user.email}>"

Packages

A package is a collection of modules with a kit.toml manifest file. Packages can be published and shared with others.

Creating a Package

Initialize a new package with kit init:

# Create a new package
kit init my-package
cd my-package

# Package structure created:
my-package/
  kit.toml
  src/
    main.kit

Package Commands

# Initialize a new package
kit init [name]

# Install dependencies from kit.toml
kit install

# Add a dependency
kit add kit-json
kit add kit-postgres --version 1.2.0

# Remove a dependency
kit remove kit-json

# Clean installed modules
kit clean

# Manage the package cache
kit cache           # Show cache stats
kit cache list      # List cached packages
kit cache clear     # Clear the cache

The kit.toml File

The kit.toml file describes your package and its dependencies:

# Package metadata
[package]
name = "my-app"
version = "2026.1.13"
authors = ["Your Name"]
description = "My awesome Kit application"

# Dependencies
[dependencies]
kit-json = "1.0.0"
kit-csv = "2.1.0"
kit-postgres = "1.5.0"

# Git dependencies
my-lib = { git = "https://github.com/user/my-lib", tag = "v1.0" }

# Local dependencies
shared-utils = { path = "../shared-utils" }

# Files to exclude when published
exclude = ["examples/", "tests/", "*.test.kit", "README.md"]

Dependency Sources

Source Syntax Description
Registry "1.0.0" Package from the Kit registry
Git { git = "url", tag = "v1.0" } Package from a Git repository
Local { path = "../lib" } Local package by path

Exclude Patterns

Use exclude to specify files that shouldn't be included when the package is installed as a dependency:

exclude = [
  "examples/",      # Exclude directory and contents
  "tests/",         # Exclude test directory
  "*.test.kit",     # Exclude test files
  "**/*.md",        # Exclude all markdown files
  "docs/"           # Exclude documentation
]

Managing Dependencies

Installing Dependencies

# Add a package
kit add kit-json

# Add with specific version
kit add kit-postgres --version 1.5.0

# Install all dependencies from kit.toml
kit install

Using Installed Packages

# After running kit add kit-json
import Json

data = Json.parse "{\"name\": \"Kit\"}"

match Json.get "name" data
| Some name -> println name
| None -> println "Not found"

Dependency Locations

Dependencies are installed to kit_modules/ in your project directory. The global cache is located at ~/.kit/packages/.

Project Structure

Simple Application

my-app/
  kit.toml
  src/
    main.kit
    utils.kit
    models.kit

Library Package

my-library/
  kit.toml
  src/
    lib.kit           # Main entry point
    types.kit
    helpers.kit
  tests/
    lib.test.kit
  examples/
    basic.kit
    advanced.kit
  README.md

Large Application

my-app/
  kit.toml
  src/
    main.kit
    config.kit
    api/
      routes.kit
      handlers.kit
      middleware.kit
    db/
      connection.kit
      queries.kit
      models.kit
    services/
      auth.kit
      users.kit
      orders.kit
  tests/
    api.test.kit
    db.test.kit
  scripts/
    migrate.kit
    seed.kit

Best Practices

Module Organization

  • One concept per module - Each module should have a clear, focused purpose
  • Group related functionality - Keep related types and functions together
  • Use descriptive names - Module names should clearly indicate their contents

Imports

# Good: Import what you need
import List
import Map
import "./user.kit"

# Good: Use aliases for long names or conflicts
import Postgres as Pg
import "./database/user-queries.kit" as UserDb

# Good: Use selective imports for commonly used items
import "./types.kit".{User, create-user, validate}

Package Dependencies

  • Pin versions - Specify exact versions for reproducible builds
  • Minimize dependencies - Only add packages you actually need
  • Keep dependencies updated - Regularly check for security updates

Exporting

# user.kit - Clean public API

# Types (exported)
type User = {id: Int, name: String, email: String}

# Public functions (exported)
create = fn(id, name, email) =>
  {id: id, name: name, email: email}

validate = fn(user) =>
  validate-email user.email
    |> Result.and-then (fn(_) => validate-name user.name)
    |> Result.map (fn(_) => user)

# Helper functions (also exported, but considered internal)
validate-email = fn(email) =>
  if String.contains? "@" email then
    Ok email
  else
    Err "Invalid email"

validate-name = fn(name) =>
  if String.length name > 0 then
    Ok name
  else
    Err "Name required"

Next Steps

Now that you understand modules, explore: