aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNic Gaffney <gaffney_nic@protonmail.com>2025-10-21 00:09:07 -0500
committerNic Gaffney <gaffney_nic@protonmail.com>2025-10-21 00:09:07 -0500
commitc034b4a1291a803028eef8b0950a7090ea54b047 (patch)
tree987040745c887299e69f2c3014c44ea829d89bb3
parentb4588b2064c3afde9497084caef6e83246b32501 (diff)
downloadfuncz-c034b4a1291a803028eef8b0950a7090ea54b047.tar.gz
Currying, Combinators, and organization
-rw-r--r--src/combinators.zig50
-rw-r--r--src/compose.zig28
-rw-r--r--src/curry.zig68
-rw-r--r--src/lib.zig107
-rw-r--r--src/main.zig26
-rw-r--r--src/map.zig62
-rw-r--r--src/newLib.zig163
-rw-r--r--src/oldLib.zig227
-rw-r--r--src/util.zig16
9 files changed, 635 insertions, 112 deletions
diff --git a/src/combinators.zig b/src/combinators.zig
new file mode 100644
index 0000000..d78e40c
--- /dev/null
+++ b/src/combinators.zig
@@ -0,0 +1,50 @@
+const std = @import("std");
+const typeVerify = @import("util.zig").typeVerify;
+
+pub fn I(x: anytype) @TypeOf(x) {
+ return x;
+}
+
+pub fn K(x: anytype) fn(anytype) @TypeOf(x) {
+ return struct {
+ fn Kinner(y: anytype) @TypeOf(x) {
+ _=y;
+ return x;
+ }
+ }.Kinner;
+}
+
+fn SHelper(comptime x: anytype) type {
+ return struct {
+ pub fn call(comptime y: anytype) type {
+ return struct {
+ pub fn call(z: anytype) @TypeOf(x(z)(y(z))) {
+ return x(z)(y(z));
+ }
+ };
+ }
+ };
+}
+
+pub fn S(comptime x: anytype) fn(anytype) fn(anytype) @TypeOf(blk: {
+ const dummy = struct {
+ fn d(a: anytype) @TypeOf(a) { return a; }
+ }.d;
+ break :blk x(dummy)(dummy);
+}) {
+ const Helper = SHelper(x);
+ return struct {
+ fn inner1(y: anytype) fn(anytype) @TypeOf(blk: {
+ const dummy = struct {
+ fn d(a: anytype) @TypeOf(a) { return a; }
+ }.d;
+ break :blk x(dummy)(y(dummy));
+ }) {
+ return struct {
+ fn inner2(z: anytype) @TypeOf(x(z)(y(z))) {
+ return Helper.call(y).call(z);
+ }
+ }.inner2;
+ }
+ }.inner1;
+}
diff --git a/src/compose.zig b/src/compose.zig
new file mode 100644
index 0000000..61ebb20
--- /dev/null
+++ b/src/compose.zig
@@ -0,0 +1,28 @@
+const std = @import("std");
+const typeVerify = @import("util.zig").typeVerify;
+
+/// ```zig
+/// (fn (fn (b) c, fn (a) b) fn (a) c)
+/// ```
+/// Function composition
+/// Type signature: (a -> b) -> (b -> c) -> (a -> c)
+/// `outerFunc` and `innerFunc` are functions of types `b -> c` and `a -> b` respectively
+/// Haskell equivalent: `outerFunc . innerFunc`
+pub fn compose(
+ comptime outerFunc: anytype,
+ comptime innerFunc: anytype
+) blk:{
+ _=typeVerify(@TypeOf(outerFunc), .{ .@"fn" });
+ _=typeVerify(@TypeOf(innerFunc), .{ .@"fn" });
+ const out = @typeInfo(@TypeOf(outerFunc)).@"fn".return_type.?;
+ const in = @typeInfo(@TypeOf(innerFunc)).@"fn".params[0].type.?;
+ break :blk fn(in) out;
+} {
+ const out = @typeInfo(@TypeOf(outerFunc)).@"fn".return_type.?;
+ const in = @typeInfo(@TypeOf(innerFunc)).@"fn".params[0].type.?;
+ return struct {
+ fn func(input: in) out {
+ return outerFunc(innerFunc(input));
+ }
+ }.func;
+}
diff --git a/src/curry.zig b/src/curry.zig
new file mode 100644
index 0000000..d196155
--- /dev/null
+++ b/src/curry.zig
@@ -0,0 +1,68 @@
+const std = @import("std");
+
+/// ```zig
+/// (fn (a, b, ..., n) r) fn (a) fn (b) ... fn (n) r
+/// ```
+/// Function currying
+/// Type signature: (a -> b -> ... -> n -> r) -> a -> b -> ... -> n -> r
+/// Transforms a function taking multiple arguments into a sequence of functions each taking a single argument
+/// `func` is a function of type `(a, b, ..., n) -> r`
+/// Haskell equivalent: automatic currying (all functions are curried by default)
+pub fn curry(func: anytype) curryTypeGetter(@TypeOf(func), @TypeOf(func), .{}) {
+ return curryHelper(func, .{});
+}
+
+
+fn curriedTypeVerify(comptime T: type, comptime expected: std.builtin.Type) std.builtin.Type {
+ const info = @typeInfo(T);
+ if (@as(std.meta.Tag(std.builtin.Type), info) != @as(std.meta.Tag(std.builtin.Type), expected)) {
+ @compileError("Type mismatch");
+ }
+ return info;
+}
+
+fn curryTypeGetter(comptime func: type, comptime newfunc: type, comptime args: anytype) type {
+ const typeInfo = curriedTypeVerify(func, .{ .@"fn" = undefined }).@"fn";
+ const newTypeInfo = curriedTypeVerify(newfunc, .{ .@"fn" = undefined }).@"fn";
+
+ if (typeInfo.params.len == args.len + 1) {
+ return fn(typeInfo.params[args.len].type.?) typeInfo.return_type.?;
+ }
+
+ const nextParamType = typeInfo.params[args.len].type.?;
+ var buf: [64]type = undefined;
+ for (args, 0..) |a, i| {
+ buf[i] = if (@TypeOf(a) != type) @TypeOf(a) else a;
+ }
+ buf[args.len] = nextParamType;
+
+ return fn(nextParamType) curryTypeGetter(func, @Type(.{
+ .@"fn" = .{
+ .calling_convention = newTypeInfo.calling_convention,
+ .is_generic = newTypeInfo.is_generic,
+ .params = newTypeInfo.params[1..],
+ .is_var_args = newTypeInfo.is_var_args,
+ .return_type = newTypeInfo.return_type,
+ }
+ }), buf[0..args.len+1].*);
+}
+
+fn curryHelper(comptime func: anytype, args: anytype) curryTypeGetter(@TypeOf(func), @TypeOf(func), args) {
+ const typeInfo = curriedTypeVerify(@TypeOf(func), .{ .@"fn" = undefined }).@"fn";
+ const argInfo = @typeInfo(@TypeOf(args)).@"struct";
+ _=argInfo;
+
+ const nextParamType = typeInfo.params[args.len].type.?;
+
+ const Closure = struct {
+ fn funcCurry(arg: nextParamType) @typeInfo(curryTypeGetter(@TypeOf(func), @TypeOf(func), args)).@"fn".return_type.? {
+ if (args.len + 1 == typeInfo.params.len) {
+ return @call(.auto, func, args ++ .{arg});
+ }
+ const newArgs = args ++ .{arg};
+ return curryHelper(func, newArgs);
+ }
+ };
+
+ return Closure.funcCurry;
+}
diff --git a/src/lib.zig b/src/lib.zig
index 0a50a80..721b2a0 100644
--- a/src/lib.zig
+++ b/src/lib.zig
@@ -1,102 +1,5 @@
-const std = @import("std");
-const Type = std.builtin.Type;
-
-fn typeVerify(T: type, expected: anytype) Type {
- const expectedType = @TypeOf(expected);
- const expectedTypeInfo = @typeInfo(expectedType);
- if (expectedTypeInfo != .@"struct")
- @compileError("Expected struct or tuple, found " ++ @typeName(expectedType));
- const realTypeInfo = @typeInfo(T);
- for (expected) |e| {
- if(realTypeInfo == e) return realTypeInfo;
- }
- for (expected) |e|
- @compileError("Expected one of " ++ @tagName(e) ++ ", found " ++ @typeName(T));
- return realTypeInfo;
-}
-
-/// ```zig
-/// (fn (fn (b) c, fn (a) b) fn (a) c)
-/// ```
-/// Function composition
-/// Type signature: (a -> b) -> (b -> c) -> (a -> c)
-/// `outerFunc` and `innerFunc` are functions of types `b -> c` and `a -> b` respectively
-/// Haskell equivalent: `outerFunc . innerFunc`
-pub fn compose(
- comptime outerFunc: anytype,
- comptime innerFunc: anytype
-) blk:{
- _=typeVerify(@TypeOf(outerFunc), .{ .@"fn" });
- _=typeVerify(@TypeOf(innerFunc), .{ .@"fn" });
- const out = @typeInfo(@TypeOf(outerFunc)).@"fn".return_type.?;
- const in = @typeInfo(@TypeOf(innerFunc)).@"fn".params[0].type.?;
- break :blk fn(in) out;
-} {
- const out = @typeInfo(@TypeOf(outerFunc)).@"fn".return_type.?;
- const in = @typeInfo(@TypeOf(innerFunc)).@"fn".params[0].type.?;
- return struct {
- fn func(input: in) out {
- return outerFunc(innerFunc(input));
- }
- }.func;
-}
-
-/// ```zig
-/// (fn (Allocator, fn (fn (a) b, []a) error{OutOfMemory}![]b)
-/// ```
-/// Map a function onto a list of values, allocating space for the new slice
-/// Type signature: `(a -> b) -> [a] -> [b]`
-/// `func` is of type `a -> b`, where `items` is of type `[a]`.
-/// `map` will return a slice of type `[b]`
-/// Haskell equivalent: `map func items`
-pub fn mapAlloc(
- allocator: std.mem.Allocator,
- func: anytype,
- items: anytype,
-) error{OutOfMemory}!blk:{
- const funcInfo = typeVerify(@TypeOf(func), .{ .@"fn" });
- const itemsInfo = typeVerify(@TypeOf(items), .{ .array, .pointer });
- switch (itemsInfo) {
- .pointer => |p| if(p.size != .many and p.size != .slice)
- @compileError("Expected pointer of size 'many' or 'slice', found " ++ @tagName(p)),
- else =>{},
- }
-
- break :blk []funcInfo.@"fn".return_type.?;
-} {
- const funcInfo = typeVerify(@TypeOf(func), .{ .@"fn" });
- var result = try allocator.alloc(funcInfo.@"fn".return_type.?, items.len);
- for(items, 0..) |item, i|
- result[i] = func(item);
- return result;
-}
-
-/// ```zig
-/// (fn (Allocator, fn (fn (a) b, []a, *[]b) void)
-/// ```
-/// Map a function onto a list of values, using a buffer
-/// Type signature: `(a -> b) -> [a] -> [b]`
-/// `func` is of type `a -> b`, where `items` is of type `[a]` and `buffer` is a pointer to a value of type `[b]`.
-/// Haskell equivalent: `map func items`
-pub fn map(
- func: anytype,
- items: anytype,
- buffer: anytype,
-) void {
- _=typeVerify(@TypeOf(func), .{ .@"fn" });
- const itemsInfo = typeVerify(@TypeOf(items), .{ .pointer, .array });
- const bufferInfo = typeVerify(@TypeOf(buffer), .{ .pointer });
- const bufferChildInfo = typeVerify(bufferInfo.pointer.child, .{ .pointer, .array });
- switch (itemsInfo) {
- .pointer => |p| if(p.size != .many and p.size != .slice)
- @compileError("Expected pointer of size 'many' or 'slice', found '" ++ @tagName(p.size) ++ "'"),
- else =>{},
- }
- switch (bufferChildInfo) {
- .pointer => |p| if(p.size != .many and p.size != .slice)
- @compileError("Expected pointer of size 'many' or 'slice', found '" ++ @tagName(p.size) ++ "'"),
- else =>{},
- }
- for (items, 0..) |item, i|
- buffer.*[i] = func(item);
-}
+pub const map = @import("map.zig").map;
+pub const mapAlloc = @import("map.zig").mapAlloc;
+pub const curry = @import("curry.zig").curry;
+pub const compose = @import("compose.zig").compose;
+pub const combinators = @import("combinators.zig");
diff --git a/src/main.zig b/src/main.zig
index 949c368..bffa1ae 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -1,5 +1,6 @@
const std = @import("std");
const func = @import("funcz");
+const c = func.combinators;
fn iter(n: i32) i32 {
return n + 1;
@@ -9,25 +10,30 @@ fn mul2(n: i32) i32 {
return n * 2;
}
+fn add(n: i32, m: i32) i32 {
+ return n + m;
+}
+
+fn addThenMultiply(n: i32, m: i32, q: i32) i32 {
+ return (n + m) * q;
+}
+
pub fn main() !void {
var gpa = std.heap.DebugAllocator(.{}){};
const allocator = gpa.allocator();
const iterThenMul2 = func.compose(mul2, iter);
var items = [_]i32{ 0, 1, 2, 3, 4 };
const itemsSlice: []i32 = items[0..items.len];
- const newItems = try func.mapAlloc(allocator, iterThenMul2, itemsSlice);
- defer allocator.free(newItems);
- var buffer: [128]i32 = undefined;
- func.map(iterThenMul2, itemsSlice, &buffer);
+ std.debug.print("curry(add)(4)(5) = {any}\n", .{ func.curry(add)(4)(5) });
std.debug.print("compose(mul2, iter)(5) = {d}\n", .{ iterThenMul2(5) });
std.debug.print("mapAlloc(allocator, compose(mul2, iter), []i32{{ 0, 1, 2, 3, 4 }}) = {{ ", .{});
- for(newItems) |item| {
- std.debug.print("{d}, ", .{item});
- }
- std.debug.print("}}\n", .{});
- std.debug.print("map(compose(mul2, iter), []i32{{ 0, 1, 2, 3, 4 }}, &buffer) = {{ ", .{});
- for(buffer[0..items.len]) |item| {
+ // func.map(func: anytype, items: anytype, buffer: anytype)
+ const buffer = try func.mapAlloc(allocator, func.curry(addThenMultiply)(1)(2), itemsSlice);
+ for(buffer) |item| {
std.debug.print("{d}, ", .{item});
}
std.debug.print("}}\n", .{});
+ std.debug.print("I(5) = {any}\n", .{c.I(5)});
+ std.debug.print("K(5)(7) = {any}\n", .{c.K(5)(7)});
+ std.debug.print("(S K S K)(5)(7) = {any}\n", .{((c.S(c.K)(c.S)(c.K))(mul2)(func.curry(add)(3)))(7)});
}
diff --git a/src/map.zig b/src/map.zig
new file mode 100644
index 0000000..c532f9e
--- /dev/null
+++ b/src/map.zig
@@ -0,0 +1,62 @@
+const std = @import("std");
+const typeVerify = @import("util.zig").typeVerify;
+
+/// ```zig
+/// (fn (Allocator, fn (fn (a) b, []a, *[]b) void)
+/// ```
+/// Map a function onto a list of values, using a buffer
+/// Type signature: `(a -> b) -> [a] -> [b]`
+/// `func` is of type `a -> b`, where `items` is of type `[a]` and `buffer` is a pointer to a value of type `[b]`.
+/// Haskell equivalent: `map func items`
+pub fn map(
+ comptime func: anytype,
+ items: []typeVerify(@TypeOf(func), .{ .@"fn" }).@"fn".params[0].type.?,
+ buffer: *[]typeVerify(@TypeOf(func), .{ .@"fn" }).@"fn".return_type.?,
+) void {
+ _=typeVerify(@TypeOf(func), .{ .@"fn" });
+ // const itemsInfo = typeVerify(@TypeOf(items), .{ .pointer, .array });
+ // const bufferInfo = typeVerify(@TypeOf(buffer), .{ .pointer });
+ // const bufferChildInfo = typeVerify(bufferInfo.pointer.child, .{ .pointer, .array });
+ // switch (itemsInfo) {
+ // .pointer => |p| if(p.size != .many and p.size != .slice)
+ // @compileError("Expected pointer of size 'many' or 'slice', found '" ++ @tagName(p.size) ++ "'"),
+ // else =>{},
+ // }
+ // switch (bufferChildInfo) {
+ // .pointer => |p| if(p.size != .many and p.size != .slice)
+ // @compileError("Expected pointer of size 'many' or 'slice', found '" ++ @tagName(p.size) ++ "'"),
+ // else =>{},
+ // }
+ for (items, 0..) |item, i|
+ buffer.*[i] = func(item);
+}
+
+/// ```zig
+/// (fn (Allocator, fn (fn (a) b, []a) error{OutOfMemory}![]b)
+/// ```
+/// Map a function onto a list of values, allocating space for the new slice
+/// Type signature: `(a -> b) -> [a] -> [b]`
+/// `func` is of type `a -> b`, where `items` is of type `[a]`.
+/// `map` will return a slice of type `[b]`
+/// Haskell equivalent: `map func items`
+pub fn mapAlloc(
+ allocator: std.mem.Allocator,
+ func: anytype,
+ items: anytype,
+) error{OutOfMemory}!blk:{
+ const funcInfo = typeVerify(@TypeOf(func), .{ .@"fn" });
+ const itemsInfo = typeVerify(@TypeOf(items), .{ .array, .pointer });
+ switch (itemsInfo) {
+ .pointer => |p| if(p.size != .many and p.size != .slice)
+ @compileError("Expected pointer of size 'many' or 'slice', found " ++ @tagName(p)),
+ else =>{},
+ }
+
+ break :blk []funcInfo.@"fn".return_type.?;
+} {
+ const funcInfo = typeVerify(@TypeOf(func), .{ .@"fn" });
+ var result = try allocator.alloc(funcInfo.@"fn".return_type.?, items.len);
+ for(items, 0..) |item, i|
+ result[i] = func(item);
+ return result;
+}
diff --git a/src/newLib.zig b/src/newLib.zig
new file mode 100644
index 0000000..fff975a
--- /dev/null
+++ b/src/newLib.zig
@@ -0,0 +1,163 @@
+const std = @import("std");
+const Type = std.builtin.Type;
+
+fn typeVerify(T: type, expected: anytype) Type {
+ const expectedType = @TypeOf(expected);
+ const expectedTypeInfo = @typeInfo(expectedType);
+ if (expectedTypeInfo != .@"struct")
+ @compileError("Expected struct or tuple, found " ++ @typeName(expectedType));
+ const realTypeInfo = @typeInfo(T);
+ for (expected) |e| {
+ if(realTypeInfo == e) return realTypeInfo;
+ }
+ for (expected) |e|
+ @compileError("Expected one of " ++ @tagName(e) ++ ", found " ++ @typeName(T));
+ return realTypeInfo;
+}
+
+/// ```zig
+/// (fn (fn (b) c, fn (a) b) fn (a) c)
+/// ```
+/// Function composition
+/// Type signature: (a -> b) -> (b -> c) -> (a -> c)
+/// `outerFunc` and `innerFunc` are functions of types `b -> c` and `a -> b` respectively
+/// Haskell equivalent: `outerFunc . innerFunc`
+pub fn compose(
+ comptime outerFunc: anytype,
+ comptime innerFunc: anytype
+) blk:{
+ _=typeVerify(@TypeOf(outerFunc), .{ .@"fn" });
+ _=typeVerify(@TypeOf(innerFunc), .{ .@"fn" });
+ const out = @typeInfo(@TypeOf(outerFunc)).@"fn".return_type.?;
+ const in = @typeInfo(@TypeOf(innerFunc)).@"fn".params[0].type.?;
+ break :blk fn(in) out;
+} {
+ const out = @typeInfo(@TypeOf(outerFunc)).@"fn".return_type.?;
+ const in = @typeInfo(@TypeOf(innerFunc)).@"fn".params[0].type.?;
+ return struct {
+ fn func(input: in) out {
+ return outerFunc(innerFunc(input));
+ }
+ }.func;
+}
+
+/// ```zig
+/// (fn (Allocator, fn (fn (a) b, []a) error{OutOfMemory}![]b)
+/// ```
+/// Map a function onto a list of values, allocating space for the new slice
+/// Type signature: `(a -> b) -> [a] -> [b]`
+/// `func` is of type `a -> b`, where `items` is of type `[a]`.
+/// `map` will return a slice of type `[b]`
+/// Haskell equivalent: `map func items`
+pub fn mapAlloc(
+ allocator: std.mem.Allocator,
+ func: anytype,
+ items: anytype,
+) error{OutOfMemory}!blk:{
+ const funcInfo = typeVerify(@TypeOf(func), .{ .@"fn" });
+ const itemsInfo = typeVerify(@TypeOf(items), .{ .array, .pointer });
+ switch (itemsInfo) {
+ .pointer => |p| if(p.size != .many and p.size != .slice)
+ @compileError("Expected pointer of size 'many' or 'slice', found " ++ @tagName(p)),
+ else =>{},
+ }
+
+ break :blk []funcInfo.@"fn".return_type.?;
+} {
+ const funcInfo = typeVerify(@TypeOf(func), .{ .@"fn" });
+ var result = try allocator.alloc(funcInfo.@"fn".return_type.?, items.len);
+ for(items, 0..) |item, i|
+ result[i] = func(item);
+ return result;
+}
+
+/// ```zig
+/// (fn (Allocator, fn (fn (a) b, []a, *[]b) void)
+/// ```
+/// Map a function onto a list of values, using a buffer
+/// Type signature: `(a -> b) -> [a] -> [b]`
+/// `func` is of type `a -> b`, where `items` is of type `[a]` and `buffer` is a pointer to a value of type `[b]`.
+/// Haskell equivalent: `map func items`
+pub fn map(
+ func: anytype,
+ items: anytype,
+ buffer: anytype,
+) void {
+ _=typeVerify(@TypeOf(func), .{ .@"fn" });
+ const itemsInfo = typeVerify(@TypeOf(items), .{ .pointer, .array });
+ const bufferInfo = typeVerify(@TypeOf(buffer), .{ .pointer });
+ const bufferChildInfo = typeVerify(bufferInfo.pointer.child, .{ .pointer, .array });
+ switch (itemsInfo) {
+ .pointer => |p| if(p.size != .many and p.size != .slice)
+ @compileError("Expected pointer of size 'many' or 'slice', found '" ++ @tagName(p.size) ++ "'"),
+ else =>{},
+ }
+ switch (bufferChildInfo) {
+ .pointer => |p| if(p.size != .many and p.size != .slice)
+ @compileError("Expected pointer of size 'many' or 'slice', found '" ++ @tagName(p.size) ++ "'"),
+ else =>{},
+ }
+ for (items, 0..) |item, i|
+ buffer.*[i] = func(item);
+}
+
+pub fn curry(func: anytype) curryTypeGetter(@TypeOf(func), @TypeOf(func), .{}) {
+ return curryHelper(func, .{});
+}
+
+fn curryTypeGetter(comptime func: type, comptime newfunc: type, comptime args: anytype) type {
+ const typeInfo = typeVerify(func, .{ .@"fn" = undefined }).@"fn";
+ const newTypeInfo = typeVerify(newfunc, .{ .@"fn" = undefined }).@"fn";
+
+ // Base case: if we've curried all but one parameter, return final function type
+ if (typeInfo.params.len == args.len + 1) {
+ return fn(typeInfo.params[args.len].type.?) typeInfo.return_type.?;
+ }
+
+ // Recursive case: return function that takes next param and returns curried function
+ const nextParamType = typeInfo.params[args.len].type.?;
+ var buf: [64]type = undefined;
+ for (args, 0..) |a, i| {
+ buf[i] = if (@TypeOf(a) != type) @TypeOf(a) else a;
+ }
+ buf[args.len] = nextParamType;
+
+ return fn(nextParamType) curryTypeGetter(func, @Type(.{
+ .@"fn" = .{
+ .calling_convention = newTypeInfo.calling_convention,
+ .is_generic = newTypeInfo.is_generic,
+ .params = newTypeInfo.params[1..],
+ .is_var_args = newTypeInfo.is_var_args,
+ .return_type = newTypeInfo.return_type,
+ }
+ }), buf[0..args.len+1].*);
+}
+
+fn curryHelper(comptime func: anytype, args: anytype) curryTypeGetter(@TypeOf(func), @TypeOf(func), args) {
+ const typeInfo = typeVerify(@TypeOf(func), .{ .@"fn" = undefined }).@"fn";
+ const argInfo = @typeInfo(@TypeOf(args)).@"struct";
+ _=argInfo;
+
+ const nextParamType = typeInfo.params[args.len].type.?;
+
+ const Closure = struct {
+ fn funcCurry(arg: nextParamType) curryTypeGetter(@TypeOf(func), @TypeOf(func), args).ReturnType {
+ // Base case: if we have all arguments, call the function
+ if (args.len + 1 == typeInfo.params.len) {
+ return @call(.auto, func, args ++ .{arg});
+ }
+
+ // Recursive case: create new tuple with additional argument
+ const newArgs = args ++ .{arg};
+ return curryHelper(func, newArgs);
+ }
+ };
+
+ return Closure.funcCurry;
+}
+
+fn intToStringZ(int: u32, buf: []u8) ![:0]u8 {
+ return try std.fmt.bufPrintZ(buf, "{}", .{int});
+}
+
+// TODO: Add tests
diff --git a/src/oldLib.zig b/src/oldLib.zig
new file mode 100644
index 0000000..013daa7
--- /dev/null
+++ b/src/oldLib.zig
@@ -0,0 +1,227 @@
+const std = @import("std");
+const Type = std.builtin.Type;
+
+fn typeVerify(T: type, expected: anytype) Type {
+ const expectedType = @TypeOf(expected);
+ const expectedTypeInfo = @typeInfo(expectedType);
+ if (expectedTypeInfo != .@"struct")
+ @compileError("Expected struct or tuple, found " ++ @typeName(expectedType));
+ const realTypeInfo = @typeInfo(T);
+ for (expected) |e| {
+ if(realTypeInfo == e) return realTypeInfo;
+ }
+ for (expected) |e|
+ @compileError("Expected one of " ++ @tagName(e) ++ ", found " ++ @typeName(T));
+ return realTypeInfo;
+}
+
+/// ```zig
+/// (fn (fn (b) c, fn (a) b) fn (a) c)
+/// ```
+/// Function composition
+/// Type signature: (a -> b) -> (b -> c) -> (a -> c)
+/// `outerFunc` and `innerFunc` are functions of types `b -> c` and `a -> b` respectively
+/// Haskell equivalent: `outerFunc . innerFunc`
+pub fn compose(
+ comptime outerFunc: anytype,
+ comptime innerFunc: anytype
+) blk:{
+ _=typeVerify(@TypeOf(outerFunc), .{ .@"fn" });
+ _=typeVerify(@TypeOf(innerFunc), .{ .@"fn" });
+ const out = @typeInfo(@TypeOf(outerFunc)).@"fn".return_type.?;
+ const in = @typeInfo(@TypeOf(innerFunc)).@"fn".params[0].type.?;
+ break :blk fn(in) out;
+} {
+ const out = @typeInfo(@TypeOf(outerFunc)).@"fn".return_type.?;
+ const in = @typeInfo(@TypeOf(innerFunc)).@"fn".params[0].type.?;
+ return struct {
+ fn func(input: in) out {
+ return outerFunc(innerFunc(input));
+ }
+ }.func;
+}
+
+/// ```zig
+/// (fn (Allocator, fn (fn (a) b, []a) error{OutOfMemory}![]b)
+/// ```
+/// Map a function onto a list of values, allocating space for the new slice
+/// Type signature: `(a -> b) -> [a] -> [b]`
+/// `func` is of type `a -> b`, where `items` is of type `[a]`.
+/// `map` will return a slice of type `[b]`
+/// Haskell equivalent: `map func items`
+pub fn mapAlloc(
+ allocator: std.mem.Allocator,
+ func: anytype,
+ items: anytype,
+) error{OutOfMemory}!blk:{
+ const funcInfo = typeVerify(@TypeOf(func), .{ .@"fn" });
+ const itemsInfo = typeVerify(@TypeOf(items), .{ .array, .pointer });
+ switch (itemsInfo) {
+ .pointer => |p| if(p.size != .many and p.size != .slice)
+ @compileError("Expected pointer of size 'many' or 'slice', found " ++ @tagName(p)),
+ else =>{},
+ }
+
+ break :blk []funcInfo.@"fn".return_type.?;
+} {
+ const funcInfo = typeVerify(@TypeOf(func), .{ .@"fn" });
+ var result = try allocator.alloc(funcInfo.@"fn".return_type.?, items.len);
+ for(items, 0..) |item, i|
+ result[i] = func(item);
+ return result;
+}
+
+/// ```zig
+/// (fn (Allocator, fn (fn (a) b, []a, *[]b) void)
+/// ```
+/// Map a function onto a list of values, using a buffer
+/// Type signature: `(a -> b) -> [a] -> [b]`
+/// `func` is of type `a -> b`, where `items` is of type `[a]` and `buffer` is a pointer to a value of type `[b]`.
+/// Haskell equivalent: `map func items`
+pub fn map(
+ func: anytype,
+ items: anytype,
+ buffer: anytype,
+) void {
+ _=typeVerify(@TypeOf(func), .{ .@"fn" });
+ const itemsInfo = typeVerify(@TypeOf(items), .{ .pointer, .array });
+ const bufferInfo = typeVerify(@TypeOf(buffer), .{ .pointer });
+ const bufferChildInfo = typeVerify(bufferInfo.pointer.child, .{ .pointer, .array });
+ switch (itemsInfo) {
+ .pointer => |p| if(p.size != .many and p.size != .slice)
+ @compileError("Expected pointer of size 'many' or 'slice', found '" ++ @tagName(p.size) ++ "'"),
+ else =>{},
+ }
+ switch (bufferChildInfo) {
+ .pointer => |p| if(p.size != .many and p.size != .slice)
+ @compileError("Expected pointer of size 'many' or 'slice', found '" ++ @tagName(p.size) ++ "'"),
+ else =>{},
+ }
+ for (items, 0..) |item, i|
+ buffer.*[i] = func(item);
+}
+
+pub fn curry(func: anytype) blk: {
+ const typeInfo = typeVerify(@TypeOf(func), .{ .@"fn" }).@"fn";
+ if (typeInfo.params.len == 1)
+ break :blk @TypeOf(func);
+ if (typeInfo.params.len == 2)
+ break :blk fn(typeInfo.params[0].type.?)
+ fn(typeInfo.params[1].type.?) typeInfo.return_type.?;
+ if (typeInfo.params.len == 3)
+ break :blk fn(typeInfo.params[0].type.?)
+ fn(typeInfo.params[1].type.?)
+ fn(typeInfo.params[2].type.?) typeInfo.return_type.?;
+
+} {
+ const typeInfo = typeVerify(@TypeOf(func), .{ .@"fn" }).@"fn";
+ if (typeInfo.params.len == 1)
+ return func;
+ if (typeInfo.params.len == 2)
+ return struct {
+ fn funct(arg1: typeInfo.params[0].type.?) fn(typeInfo.params[1].type.?) typeInfo.return_type.? {
+ return struct {
+ fn func2(arg2: typeInfo.params[1].type.?) typeInfo.return_type.? {
+ return func(arg1, arg2);
+ }
+ }.func2;
+ }
+ }.funct;
+ if (typeInfo.params.len == 3)
+ return struct {
+ fn func1(arg1: typeInfo.params[0].type.?) fn(typeInfo.params[1].type.?) fn(typeInfo.params[2].type.?)
+ typeInfo.return_type.? {
+ return struct {
+ fn func2(arg2: typeInfo.params[1].type.?) fn(typeInfo.params[2].type.?) typeInfo.return_type.? {
+ return struct {
+ fn func3(arg3: typeInfo.params[2].type.?) typeInfo.return_type.? {
+ return func(arg1, arg2, arg3);
+ }
+ }.func3;
+ }
+ }.func2;
+ }
+ }.func1;
+}
+
+pub fn curryHelper(func: anytype, args: anytype) blk: {
+ const typeInfo = typeVerify(@TypeOf(func), .{ .@"fn" }).@"fn";
+ _=typeVerify(@TypeOf(args), .{ .@"struct" });
+ if (typeInfo.params.len == 1)
+ break :blk @TypeOf(func);
+ const newInfo = std.builtin.Type{
+ .@"fn" = .{
+ .calling_convention = typeInfo.calling_convention,
+ .is_generic = typeInfo.is_generic,
+ .params = typeInfo.params[1..],
+ .is_var_args = typeInfo.is_var_args,
+ .return_type = typeInfo.return_type,
+ }
+ };
+ _=newInfo;
+ // break :blk fn(typeInfo.params[args.len].type.?) @Type(newInfo);
+ break :blk type;
+} {
+ const typeInfo = typeVerify(@TypeOf(func), .{ .@"fn" }).@"fn";
+ const argInfo = typeVerify(@TypeOf(args), .{ .@"struct" }).@"struct";
+ if (args.len == typeInfo.params.len) return struct {
+ pub fn funcCurry() typeInfo.return_type.? {
+ return @call(.auto, func, args);
+ }
+ };
+ const newInfo = std.builtin.Type{
+ .@"fn" = .{
+ .calling_convention = typeInfo.calling_convention,
+ .is_generic = typeInfo.is_generic,
+ .params = typeInfo.params[1..],
+ .is_var_args = typeInfo.is_var_args,
+ .return_type = typeInfo.return_type,
+ }
+ };
+ _=newInfo;
+ // const newType = @Type(newInfo);
+ return struct {
+ pub fn funcCurry(arg: typeInfo.params[0].type.?) type {
+ var fields: [64]std.builtin.Type.StructField = .{std.builtin.Type.StructField{.name="10",.type=type,.is_comptime=false,.alignment=8,.default_value_ptr=null}} ** 64;
+ for (argInfo.fields, 0..) |f, i| {
+ fields[i] = f;
+ }
+ var buf2: [3:0]u8 = undefined;
+ fields[args.len] = .{
+ .name = blk: {
+ break :blk try intToStringZ(args.len, &buf2);
+ },
+ .type = typeInfo.params[argInfo.fields.len].type.?,
+ .is_comptime = false,
+ .alignment = @alignOf(typeInfo.params[0].type.?),
+ .default_value_ptr = null,
+ };
+ const newStruct = std.builtin.Type{
+ .@"struct" = .{
+ .backing_integer = argInfo.backing_integer,
+ .decls = argInfo.decls,
+ .fields = fields[0..args.len+1],
+ .is_tuple = argInfo.is_tuple,
+ .layout = argInfo.layout,
+ }
+ };
+
+ // std.debug.print("{any}", .{fields[0..3]});
+ const t = @Type(newStruct);
+ var newArgs: t = undefined;
+ for (@typeInfo(t).@"struct".fields, 0..) |f, i| {
+ if (i == args.len) {
+ @field(newArgs, f.name) = arg;
+ } else @field(newArgs, f.name) = args[i];
+
+ }
+ return curryHelper(func, newArgs);
+ }
+ };
+}
+
+fn intToStringZ(int: u32, buf: []u8) ![:0]u8 {
+ return try std.fmt.bufPrintZ(buf, "{}", .{int});
+}
+
+// TODO: Add
diff --git a/src/util.zig b/src/util.zig
new file mode 100644
index 0000000..8de989e
--- /dev/null
+++ b/src/util.zig
@@ -0,0 +1,16 @@
+const std = @import("std");
+const Type = std.builtin.Type;
+
+pub fn typeVerify(T: type, expected: anytype) Type {
+ const expectedType = @TypeOf(expected);
+ const expectedTypeInfo = @typeInfo(expectedType);
+ if (expectedTypeInfo != .@"struct")
+ @compileError("Expected struct or tuple, found " ++ @typeName(expectedType));
+ const realTypeInfo = @typeInfo(T);
+ for (expected) |e| {
+ if(realTypeInfo == e) return realTypeInfo;
+ }
+ for (expected) |e|
+ @compileError("Expected one of " ++ @tagName(e) ++ ", found " ++ @typeName(T));
+ return realTypeInfo;
+}