Skip to content

Functional metaprogramming in Zig. Use higher order functions that evaluate at comptime for succinct code.

Notifications You must be signed in to change notification settings

goodvibs/zig-hof

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

zig-hof

A Zig library providing higher-order functions for working with comptime-known arrays. These functions leverage Zig's powerful comptime capabilities to provide zero-cost abstractions for functional programming patterns.

Features

  • map - Transform each element of an array
  • filter - Filter elements based on a predicate
  • foldLeft - Left-to-right fold/reduce operation
  • foldRight - Right-to-left fold/reduce operation

All functions work with comptime-known arrays and have zero runtime overhead when arrays are comptime.

Installation

Add this to your build.zig.zon:

.dependencies = .{
    .hof = .{
        .path = "path/to/zig-hof",
    },
}

Then in your build.zig:

const hof = b.dependency("hof", .{}).module("hof");
exe.addModule("hof", hof);

Usage

Import the module:

const hof = @import("hof");

API Reference

map

Maps each element of a comptime-known array to a new value using a function. Returns an array of the same length.

Signature:

pub fn map(comptime arr: anytype, comptime f: anytype) [Len(arr)]MapOut(arr, f)

Example:

const arr = [4]i32{ 1, 2, 3, 4 };

const Double = struct {
    fn double(x: i32) i32 {
        return x * 2;
    }
};

const result = hof.map(arr, Double.double);
// result: [4]i32{ 2, 4, 6, 8 }

Type conversion example:

const arr = [3]i32{ 1, 2, 3 };

const ToFloat = struct {
    fn toFloat(x: i32) f64 {
        return @as(f64, @floatFromInt(x));
    }
};

const result = hof.map(arr, ToFloat.toFloat);
// result: [3]f64{ 1.0, 2.0, 3.0 }

filter

Filters elements of a comptime-known array based on a predicate. Returns an array containing only the matching elements (size determined at comptime).

Signature:

pub fn filter(comptime arr: anytype, comptime pred: anytype) [countIf(arr, pred)]Elem(arr)

Example:

const arr = [5]i32{ 1, 2, 3, 4, 5 };

const IsEven = struct {
    fn isEven(x: i32) bool {
        return @rem(x, 2) == 0;
    }
};

const result = hof.filter(arr, IsEven.isEven);
// result: [2]i32{ 2, 4 }

foldLeft

Folds an array from left to right using a binary function and an initial value. Also known as reduce or foldl in other languages.

Signature:

pub fn foldLeft(comptime arr: anytype, initial: anytype, comptime folder: anytype) @TypeOf(initial)

Example:

const arr = [4]i32{ 1, 2, 3, 4 };

const Add = struct {
    fn add(acc: i32, x: i32) i32 {
        return acc + x;
    }
};

const sum = hof.foldLeft(arr, 0, Add.add);
// sum: 10 (0 + 1 + 2 + 3 + 4)

Product example:

const arr = [4]i32{ 2, 3, 4, 5 };

const Multiply = struct {
    fn multiply(acc: i32, x: i32) i32 {
        return acc * x;
    }
};

const product = hof.foldLeft(arr, 1, Multiply.multiply);
// product: 120 (1 * 2 * 3 * 4 * 5)

foldRight

Folds an array from right to left using a binary function and an initial value. Also known as foldr in other languages.

Signature:

pub fn foldRight(comptime arr: anytype, initial: anytype, comptime folder: anytype) @TypeOf(initial)

Note: The function signature is fn(x: T, acc: Acc) Acc (element first, then accumulator), which is the opposite of foldLeft.

Example:

const arr = [4]i32{ 1, 2, 3, 4 };

const Add = struct {
    fn add(x: i32, acc: i32) i32 {
        return x + acc;
    }
};

const sum = hof.foldRight(arr, 0, Add.add);
// sum: 10 (same result as foldLeft for addition)

Associativity difference: For non-associative operations, foldLeft and foldRight produce different results:

const arr = [3]i32{ 10, 3, 2 };

const SubtractLeft = struct {
    fn subtract(acc: i32, x: i32) i32 {
        return acc - x;
    }
};

const SubtractRight = struct {
    fn subtract(x: i32, acc: i32) i32 {
        return x - acc;
    }
};

const left = hof.foldLeft(arr, 0, SubtractLeft.subtract);
// left: -15 (0 - 10 - 3 - 2)

const right = hof.foldRight(arr, 0, SubtractRight.subtract);
// right: 9 (10 - (3 - (2 - 0)))

Composition

These functions compose nicely together:

const arr = [5]i32{ 1, 2, 3, 4, 5 };

const Double = struct {
    fn double(x: i32) i32 { return x * 2; }
};

const IsEven = struct {
    fn isEven(x: i32) bool { return @rem(x, 2) == 0; }
};

const Add = struct {
    fn add(acc: i32, x: i32) i32 { return acc + x; }
};

// Double all elements, filter evens, then sum
const doubled = hof.map(arr, Double.double);
const evens = hof.filter(doubled, IsEven.isEven);
const sum = hof.foldLeft(evens, 0, Add.add);
// sum: 30 (2 + 4 + 6 + 8 + 10)

Requirements

  • Zig 0.15.2 or later

About

Functional metaprogramming in Zig. Use higher order functions that evaluate at comptime for succinct code.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages